numcodecs_wasm_host_reproducible/transform/
instcnt.rs

1use std::sync::OnceLock;
2
3use anyhow::{Error, anyhow};
4use semver::Version;
5use wasm_component_layer::{InterfaceIdentifier, PackageIdentifier, PackageName};
6use wasm_encoder::reencode::{self, Reencode};
7
8pub enum InstructionCounterInjecter {}
9
10impl InstructionCounterInjecter {
11    pub fn apply_to_module(
12        wasm: &[u8],
13        features: wasmparser::WasmFeatures,
14    ) -> Result<Vec<u8>, anyhow::Error> {
15        let mut parser = wasmparser::Parser::new(0);
16        parser.set_features(features);
17
18        let mut module = wasm_encoder::Module::new();
19
20        let mut reencoder = InstructionCounterInjecterReencoder {
21            instruction_counter_global: None,
22            num_imported_funcs: 0,
23            instruction_counter_func_index: None,
24            func_index: 0,
25        };
26        reencoder
27            .parse_core_module(&mut module, parser, wasm)
28            .map_err(|err| anyhow::format_err!("{}", err))?;
29
30        if let Some(instruction_counter_func_index) = reencoder.instruction_counter_func_index {
31            anyhow::ensure!(
32                reencoder.func_index > instruction_counter_func_index,
33                "missing WASM instruction counter reader function body"
34            );
35        }
36
37        let wasm = module.finish();
38        wasmparser::Validator::new_with_features(features).validate_all(&wasm)?;
39        Ok(wasm)
40    }
41}
42
43struct InstructionCounterInjecterReencoder {
44    instruction_counter_global: Option<u32>,
45    num_imported_funcs: u32,
46    instruction_counter_func_index: Option<u32>,
47    func_index: u32,
48}
49
50impl wasm_encoder::reencode::Reencode for InstructionCounterInjecterReencoder {
51    type Error = Error;
52
53    fn global_index(&mut self, global: u32) -> Result<u32, reencode::Error<Self::Error>> {
54        match self.instruction_counter_global {
55            Some(instruction_counter_global) if global >= instruction_counter_global => {
56                Ok(global + 1)
57            }
58            _ => Ok(global),
59        }
60    }
61
62    fn parse_import_section(
63        &mut self,
64        imports: &mut wasm_encoder::ImportSection,
65        section: wasmparser::ImportSectionReader<'_>,
66    ) -> Result<(), reencode::Error<Self::Error>> {
67        self.instruction_counter_global.get_or_insert(0);
68        for import in section {
69            let import = import?;
70            match import.ty {
71                wasmparser::TypeRef::Func(_) => self.num_imported_funcs += 1,
72                wasmparser::TypeRef::Global(_) => {
73                    *self.instruction_counter_global.get_or_insert(0) += 1;
74                }
75                wasmparser::TypeRef::Table(_)
76                | wasmparser::TypeRef::Memory(_)
77                | wasmparser::TypeRef::Tag(_) => (),
78            }
79            self.parse_import(imports, import)?;
80        }
81        Self::inject_instruction_counter_import(imports);
82        Ok(())
83    }
84
85    fn intersperse_section_hook(
86        &mut self,
87        module: &mut wasm_encoder::Module,
88        _after: Option<wasm_encoder::SectionId>,
89        before: Option<wasm_encoder::SectionId>,
90    ) -> Result<(), reencode::Error<Self::Error>> {
91        // the function section directly follows the import section
92        // if the function section is missing, we also don't need to do any
93        //  instruction counting
94        let Some(wasm_encoder::SectionId::Function) = before else {
95            return Ok(());
96        };
97
98        if self.instruction_counter_global.is_none() {
99            self.instruction_counter_global = Some(0);
100
101            let mut imports = wasm_encoder::ImportSection::new();
102            Self::inject_instruction_counter_import(&mut imports);
103            module.section(&imports);
104        }
105
106        Ok(())
107    }
108
109    fn parse_export_section(
110        &mut self,
111        exports: &mut wasm_encoder::ExportSection,
112        section: wasmparser::ExportSectionReader<'_>,
113    ) -> Result<(), reencode::Error<Self::Error>> {
114        let instruction_counter_export_name = {
115            let PerfWitInterfaces {
116                perf: perf_interface,
117                instruction_counter,
118                ..
119            } = PerfWitInterfaces::get();
120
121            format!("{perf_interface}#{instruction_counter}")
122        };
123
124        for export in section {
125            let export = export?;
126            if export.name == instruction_counter_export_name {
127                if !matches!(export.kind, wasmparser::ExternalKind::Func) {
128                    return Err(reencode::Error::UserError(anyhow!(
129                        "instruction counter reader export must be a function",
130                    )));
131                }
132                if self.instruction_counter_func_index.is_some() {
133                    return Err(reencode::Error::UserError(anyhow!(
134                        "duplicate instruction counter reader export",
135                    )));
136                }
137                self.instruction_counter_func_index = Some(export.index - self.num_imported_funcs);
138            }
139
140            self.parse_export(exports, export)?;
141        }
142        Ok(())
143    }
144
145    fn parse_function_body(
146        &mut self,
147        code: &mut wasm_encoder::CodeSection,
148        func: wasmparser::FunctionBody<'_>,
149    ) -> Result<(), reencode::Error<Self::Error>> {
150        let Some(instruction_counter_global) = self.instruction_counter_global else {
151            return Err(reencode::Error::UserError(anyhow!(
152                "missing instruction counter import",
153            )));
154        };
155
156        let mut function = self.new_function_with_parsed_locals(&func)?;
157        let instructions = func.get_operators_reader()?;
158
159        if Some(self.func_index) == self.instruction_counter_func_index {
160            let locals = func.get_locals_reader()?;
161
162            if locals.get_count() > 0 {
163                return Err(reencode::Error::UserError(anyhow!(
164                    "instruction counter function has no locals",
165                )));
166            }
167            let instructions = instructions.into_iter().collect::<Result<Vec<_>, _>>()?;
168            if !matches!(
169                instructions.as_slice(),
170                [wasmparser::Operator::Unreachable, wasmparser::Operator::End]
171            ) {
172                return Err(reencode::Error::UserError(anyhow!(
173                    "instruction counter function has a single instruction and is unreachable",
174                )));
175            }
176
177            function.instruction(&wasm_encoder::Instruction::GlobalGet(
178                instruction_counter_global,
179            ));
180            function.instruction(&wasm_encoder::Instruction::Return);
181            function.instruction(&wasm_encoder::Instruction::End);
182        } else {
183            let mut counter: i64 = 0;
184
185            for instruction in instructions {
186                let instruction = instruction?;
187
188                if let Some(update) = Self::instruction_needs_counter_update(&instruction) {
189                    counter += 1;
190
191                    if update {
192                        for count_instruction in Self::generate_instruction_counter_update(
193                            instruction_counter_global,
194                            counter,
195                        ) {
196                            function.instruction(&count_instruction);
197                        }
198                    }
199
200                    counter = 0;
201                }
202
203                function.instruction(&self.instruction(instruction)?);
204            }
205        }
206        code.function(&function);
207        self.func_index += 1;
208        Ok(())
209    }
210}
211
212impl InstructionCounterInjecterReencoder {
213    fn inject_instruction_counter_import(imports: &mut wasm_encoder::ImportSection) {
214        let PerfWitInterfaces {
215            perf: perf_interface,
216            instruction_counter,
217        } = PerfWitInterfaces::get();
218
219        imports.import(
220            &format!("{perf_interface}"),
221            instruction_counter,
222            wasm_encoder::EntityType::Global(wasm_encoder::GlobalType {
223                val_type: wasm_encoder::ValType::I64,
224                mutable: true,
225                shared: false,
226            }),
227        );
228    }
229
230    const fn generate_instruction_counter_update(
231        instruction_counter_global: u32,
232        delta: i64,
233    ) -> impl IntoIterator<Item = wasm_encoder::Instruction<'static>> {
234        [
235            wasm_encoder::Instruction::GlobalGet(instruction_counter_global),
236            wasm_encoder::Instruction::I64Const(delta),
237            wasm_encoder::Instruction::I64Add,
238            wasm_encoder::Instruction::GlobalSet(instruction_counter_global),
239        ]
240    }
241
242    #[expect(clippy::too_many_lines)]
243    fn instruction_needs_counter_update(instr: &wasmparser::Operator) -> Option<bool> {
244        match instr {
245            // === MVP ===
246            // we cannot recover from an unreachable instruction, so instruction
247            //  counting won't matter at that point anyways
248            wasmparser::Operator::Unreachable => Some(false),
249            // no-op is not an instruction
250            wasmparser::Operator::Nop => None,
251            // we need to save before diverging control flow, and these
252            //  instructions are jump targets
253            wasmparser::Operator::Block { .. }
254            | wasmparser::Operator::Loop { .. }
255            | wasmparser::Operator::If { .. }
256            | wasmparser::Operator::Else => Some(true),
257            // === Exception handling ===
258            // we need to save before diverging control flow, and this
259            //  instruction is a jump target
260            wasmparser::Operator::TryTable { .. } => Some(true),
261            // we need to save before diverging control flow
262            wasmparser::Operator::Throw { .. } | wasmparser::Operator::ThrowRef => Some(true),
263            // === Legacy exception handling (deprecated) ===
264            // we need to save before diverging control flow, and this
265            //  instruction is a jump target
266            wasmparser::Operator::Try { .. } => Some(true),
267            // we need to save before diverging control flow
268            wasmparser::Operator::Catch { .. }
269            | wasmparser::Operator::Rethrow { .. }
270            | wasmparser::Operator::Delegate { .. }
271            | wasmparser::Operator::CatchAll => Some(true),
272            // === MVP ===
273            // we conservatively save at the end of a scope
274            wasmparser::Operator::End => Some(true),
275            // we need to save before diverging control flow
276            wasmparser::Operator::Br { .. }
277            | wasmparser::Operator::BrIf { .. }
278            | wasmparser::Operator::BrTable { .. } => Some(true),
279            // we need to save before returning from a function
280            wasmparser::Operator::Return => Some(true),
281            // calling into a function will return control flow right back to
282            //  here, so saving is not necessary
283            wasmparser::Operator::Call { .. } | wasmparser::Operator::CallIndirect { .. } => {
284                Some(false)
285            }
286            // === Tail calls ===
287            // tail calls need to save since they return from this function
288            wasmparser::Operator::ReturnCall { .. }
289            | wasmparser::Operator::ReturnCallIndirect { .. } => Some(true),
290            // === MVP ===
291            // no control flow
292            wasmparser::Operator::Drop | wasmparser::Operator::Select => Some(false),
293            // === Reference types ===
294            // no control flow
295            wasmparser::Operator::TypedSelect { .. } => Some(false),
296            // === MVP ===
297            // no control flow
298            wasmparser::Operator::LocalGet { .. }
299            | wasmparser::Operator::LocalSet { .. }
300            | wasmparser::Operator::LocalTee { .. }
301            | wasmparser::Operator::GlobalGet { .. }
302            | wasmparser::Operator::GlobalSet { .. }
303            | wasmparser::Operator::I32Load { .. }
304            | wasmparser::Operator::I64Load { .. }
305            | wasmparser::Operator::F32Load { .. }
306            | wasmparser::Operator::F64Load { .. }
307            | wasmparser::Operator::I32Load8S { .. }
308            | wasmparser::Operator::I32Load8U { .. }
309            | wasmparser::Operator::I32Load16S { .. }
310            | wasmparser::Operator::I32Load16U { .. }
311            | wasmparser::Operator::I64Load8S { .. }
312            | wasmparser::Operator::I64Load8U { .. }
313            | wasmparser::Operator::I64Load16S { .. }
314            | wasmparser::Operator::I64Load16U { .. }
315            | wasmparser::Operator::I64Load32S { .. }
316            | wasmparser::Operator::I64Load32U { .. }
317            | wasmparser::Operator::I32Store { .. }
318            | wasmparser::Operator::I64Store { .. }
319            | wasmparser::Operator::F32Store { .. }
320            | wasmparser::Operator::F64Store { .. }
321            | wasmparser::Operator::I32Store8 { .. }
322            | wasmparser::Operator::I32Store16 { .. }
323            | wasmparser::Operator::I64Store8 { .. }
324            | wasmparser::Operator::I64Store16 { .. }
325            | wasmparser::Operator::I64Store32 { .. }
326            | wasmparser::Operator::MemorySize { .. }
327            | wasmparser::Operator::MemoryGrow { .. }
328            | wasmparser::Operator::I32Const { .. }
329            | wasmparser::Operator::I64Const { .. }
330            | wasmparser::Operator::F32Const { .. }
331            | wasmparser::Operator::F64Const { .. } => Some(false),
332            // === Reference types ===
333            // no control flow
334            wasmparser::Operator::RefNull { .. }
335            | wasmparser::Operator::RefIsNull
336            | wasmparser::Operator::RefFunc { .. } => Some(false),
337            // === Garbage collection ===
338            // no control flow
339            wasmparser::Operator::RefEq => Some(false),
340            // === MVP ===
341            // no control flow
342            wasmparser::Operator::I32Eqz
343            | wasmparser::Operator::I32Eq
344            | wasmparser::Operator::I32Ne
345            | wasmparser::Operator::I32LtS
346            | wasmparser::Operator::I32LtU
347            | wasmparser::Operator::I32GtS
348            | wasmparser::Operator::I32GtU
349            | wasmparser::Operator::I32LeS
350            | wasmparser::Operator::I32LeU
351            | wasmparser::Operator::I32GeS
352            | wasmparser::Operator::I32GeU
353            | wasmparser::Operator::I64Eqz
354            | wasmparser::Operator::I64Eq
355            | wasmparser::Operator::I64Ne
356            | wasmparser::Operator::I64LtS
357            | wasmparser::Operator::I64LtU
358            | wasmparser::Operator::I64GtS
359            | wasmparser::Operator::I64GtU
360            | wasmparser::Operator::I64LeS
361            | wasmparser::Operator::I64LeU
362            | wasmparser::Operator::I64GeS
363            | wasmparser::Operator::I64GeU
364            | wasmparser::Operator::F32Eq
365            | wasmparser::Operator::F32Ne
366            | wasmparser::Operator::F32Lt
367            | wasmparser::Operator::F32Gt
368            | wasmparser::Operator::F32Le
369            | wasmparser::Operator::F32Ge
370            | wasmparser::Operator::F64Eq
371            | wasmparser::Operator::F64Ne
372            | wasmparser::Operator::F64Lt
373            | wasmparser::Operator::F64Gt
374            | wasmparser::Operator::F64Le
375            | wasmparser::Operator::F64Ge
376            | wasmparser::Operator::I32Clz
377            | wasmparser::Operator::I32Ctz
378            | wasmparser::Operator::I32Popcnt
379            | wasmparser::Operator::I32Add
380            | wasmparser::Operator::I32Sub
381            | wasmparser::Operator::I32Mul
382            | wasmparser::Operator::I32DivS
383            | wasmparser::Operator::I32DivU
384            | wasmparser::Operator::I32RemS
385            | wasmparser::Operator::I32RemU
386            | wasmparser::Operator::I32And
387            | wasmparser::Operator::I32Or
388            | wasmparser::Operator::I32Xor
389            | wasmparser::Operator::I32Shl
390            | wasmparser::Operator::I32ShrS
391            | wasmparser::Operator::I32ShrU
392            | wasmparser::Operator::I32Rotl
393            | wasmparser::Operator::I32Rotr
394            | wasmparser::Operator::I64Clz
395            | wasmparser::Operator::I64Ctz
396            | wasmparser::Operator::I64Popcnt
397            | wasmparser::Operator::I64Add
398            | wasmparser::Operator::I64Sub
399            | wasmparser::Operator::I64Mul
400            | wasmparser::Operator::I64DivS
401            | wasmparser::Operator::I64DivU
402            | wasmparser::Operator::I64RemS
403            | wasmparser::Operator::I64RemU
404            | wasmparser::Operator::I64And
405            | wasmparser::Operator::I64Or
406            | wasmparser::Operator::I64Xor
407            | wasmparser::Operator::I64Shl
408            | wasmparser::Operator::I64ShrS
409            | wasmparser::Operator::I64ShrU
410            | wasmparser::Operator::I64Rotl
411            | wasmparser::Operator::I64Rotr
412            | wasmparser::Operator::F32Abs
413            | wasmparser::Operator::F32Neg
414            | wasmparser::Operator::F32Ceil
415            | wasmparser::Operator::F32Floor
416            | wasmparser::Operator::F32Trunc
417            | wasmparser::Operator::F32Nearest
418            | wasmparser::Operator::F32Sqrt
419            | wasmparser::Operator::F32Add
420            | wasmparser::Operator::F32Sub
421            | wasmparser::Operator::F32Mul
422            | wasmparser::Operator::F32Div
423            | wasmparser::Operator::F32Min
424            | wasmparser::Operator::F32Max
425            | wasmparser::Operator::F32Copysign
426            | wasmparser::Operator::F64Abs
427            | wasmparser::Operator::F64Neg
428            | wasmparser::Operator::F64Ceil
429            | wasmparser::Operator::F64Floor
430            | wasmparser::Operator::F64Trunc
431            | wasmparser::Operator::F64Nearest
432            | wasmparser::Operator::F64Sqrt
433            | wasmparser::Operator::F64Add
434            | wasmparser::Operator::F64Sub
435            | wasmparser::Operator::F64Mul
436            | wasmparser::Operator::F64Div
437            | wasmparser::Operator::F64Min
438            | wasmparser::Operator::F64Max
439            | wasmparser::Operator::F64Copysign
440            | wasmparser::Operator::I32WrapI64
441            | wasmparser::Operator::I32TruncF32S
442            | wasmparser::Operator::I32TruncF32U
443            | wasmparser::Operator::I32TruncF64S
444            | wasmparser::Operator::I32TruncF64U
445            | wasmparser::Operator::I64ExtendI32S
446            | wasmparser::Operator::I64ExtendI32U
447            | wasmparser::Operator::I64TruncF32S
448            | wasmparser::Operator::I64TruncF32U
449            | wasmparser::Operator::I64TruncF64S
450            | wasmparser::Operator::I64TruncF64U
451            | wasmparser::Operator::F32ConvertI32S
452            | wasmparser::Operator::F32ConvertI32U
453            | wasmparser::Operator::F32ConvertI64S
454            | wasmparser::Operator::F32ConvertI64U
455            | wasmparser::Operator::F32DemoteF64
456            | wasmparser::Operator::F64ConvertI32S
457            | wasmparser::Operator::F64ConvertI32U
458            | wasmparser::Operator::F64ConvertI64S
459            | wasmparser::Operator::F64ConvertI64U
460            | wasmparser::Operator::F64PromoteF32
461            | wasmparser::Operator::I32ReinterpretF32
462            | wasmparser::Operator::I64ReinterpretF64
463            | wasmparser::Operator::F32ReinterpretI32
464            | wasmparser::Operator::F64ReinterpretI64 => Some(false),
465            // === Sign extension ===
466            // no control flow
467            wasmparser::Operator::I32Extend8S
468            | wasmparser::Operator::I32Extend16S
469            | wasmparser::Operator::I64Extend8S
470            | wasmparser::Operator::I64Extend16S
471            | wasmparser::Operator::I64Extend32S => Some(false),
472            // === Garbage collection ===
473            // no control flow
474            wasmparser::Operator::StructNew { .. }
475            | wasmparser::Operator::StructNewDefault { .. }
476            | wasmparser::Operator::StructGet { .. }
477            | wasmparser::Operator::StructGetS { .. }
478            | wasmparser::Operator::StructGetU { .. }
479            | wasmparser::Operator::StructSet { .. }
480            | wasmparser::Operator::ArrayNew { .. }
481            | wasmparser::Operator::ArrayNewDefault { .. }
482            | wasmparser::Operator::ArrayNewFixed { .. }
483            | wasmparser::Operator::ArrayNewData { .. }
484            | wasmparser::Operator::ArrayNewElem { .. }
485            | wasmparser::Operator::ArrayGet { .. }
486            | wasmparser::Operator::ArrayGetS { .. }
487            | wasmparser::Operator::ArrayGetU { .. }
488            | wasmparser::Operator::ArraySet { .. }
489            | wasmparser::Operator::ArrayLen
490            | wasmparser::Operator::ArrayFill { .. }
491            | wasmparser::Operator::ArrayCopy { .. }
492            | wasmparser::Operator::ArrayInitData { .. }
493            | wasmparser::Operator::ArrayInitElem { .. } => Some(false),
494            // no control flow
495            wasmparser::Operator::RefTestNonNull { .. }
496            | wasmparser::Operator::RefTestNullable { .. } => Some(false),
497            // no (observable) control flow (except for trapping)
498            wasmparser::Operator::RefCastNonNull { .. }
499            | wasmparser::Operator::RefCastNullable { .. } => Some(false),
500            // we need to save before diverging control flow
501            wasmparser::Operator::BrOnCast { .. } | wasmparser::Operator::BrOnCastFail { .. } => {
502                Some(true)
503            }
504            // no control flow
505            wasmparser::Operator::AnyConvertExtern
506            | wasmparser::Operator::ExternConvertAny
507            | wasmparser::Operator::RefI31
508            | wasmparser::Operator::I31GetS
509            | wasmparser::Operator::I31GetU => Some(false),
510            // === Non-trapping float-to-int conversions ===
511            // no control flow
512            wasmparser::Operator::I32TruncSatF32S
513            | wasmparser::Operator::I32TruncSatF32U
514            | wasmparser::Operator::I32TruncSatF64S
515            | wasmparser::Operator::I32TruncSatF64U
516            | wasmparser::Operator::I64TruncSatF32S
517            | wasmparser::Operator::I64TruncSatF32U
518            | wasmparser::Operator::I64TruncSatF64S
519            | wasmparser::Operator::I64TruncSatF64U => Some(false),
520            // === Bulk memory ===
521            // no control flow
522            wasmparser::Operator::MemoryInit { .. }
523            | wasmparser::Operator::DataDrop { .. }
524            | wasmparser::Operator::MemoryCopy { .. }
525            | wasmparser::Operator::MemoryFill { .. }
526            | wasmparser::Operator::TableInit { .. }
527            | wasmparser::Operator::ElemDrop { .. }
528            | wasmparser::Operator::TableCopy { .. } => Some(false),
529            // === Reference types ===
530            // no control flow
531            wasmparser::Operator::TableFill { .. }
532            | wasmparser::Operator::TableGet { .. }
533            | wasmparser::Operator::TableSet { .. }
534            | wasmparser::Operator::TableGrow { .. }
535            | wasmparser::Operator::TableSize { .. } => Some(false),
536            // === Memory control ===
537            // no control flow
538            wasmparser::Operator::MemoryDiscard { .. } => Some(false),
539            // === Threads ===
540            // no control flow
541            wasmparser::Operator::MemoryAtomicNotify { .. }
542            | wasmparser::Operator::MemoryAtomicWait32 { .. }
543            | wasmparser::Operator::MemoryAtomicWait64 { .. }
544            | wasmparser::Operator::AtomicFence
545            | wasmparser::Operator::I32AtomicLoad { .. }
546            | wasmparser::Operator::I64AtomicLoad { .. }
547            | wasmparser::Operator::I32AtomicLoad8U { .. }
548            | wasmparser::Operator::I32AtomicLoad16U { .. }
549            | wasmparser::Operator::I64AtomicLoad8U { .. }
550            | wasmparser::Operator::I64AtomicLoad16U { .. }
551            | wasmparser::Operator::I64AtomicLoad32U { .. }
552            | wasmparser::Operator::I32AtomicStore { .. }
553            | wasmparser::Operator::I64AtomicStore { .. }
554            | wasmparser::Operator::I32AtomicStore8 { .. }
555            | wasmparser::Operator::I32AtomicStore16 { .. }
556            | wasmparser::Operator::I64AtomicStore8 { .. }
557            | wasmparser::Operator::I64AtomicStore16 { .. }
558            | wasmparser::Operator::I64AtomicStore32 { .. }
559            | wasmparser::Operator::I32AtomicRmwAdd { .. }
560            | wasmparser::Operator::I64AtomicRmwAdd { .. }
561            | wasmparser::Operator::I32AtomicRmw8AddU { .. }
562            | wasmparser::Operator::I32AtomicRmw16AddU { .. }
563            | wasmparser::Operator::I64AtomicRmw8AddU { .. }
564            | wasmparser::Operator::I64AtomicRmw16AddU { .. }
565            | wasmparser::Operator::I64AtomicRmw32AddU { .. }
566            | wasmparser::Operator::I32AtomicRmwSub { .. }
567            | wasmparser::Operator::I64AtomicRmwSub { .. }
568            | wasmparser::Operator::I32AtomicRmw8SubU { .. }
569            | wasmparser::Operator::I32AtomicRmw16SubU { .. }
570            | wasmparser::Operator::I64AtomicRmw8SubU { .. }
571            | wasmparser::Operator::I64AtomicRmw16SubU { .. }
572            | wasmparser::Operator::I64AtomicRmw32SubU { .. }
573            | wasmparser::Operator::I32AtomicRmwAnd { .. }
574            | wasmparser::Operator::I64AtomicRmwAnd { .. }
575            | wasmparser::Operator::I32AtomicRmw8AndU { .. }
576            | wasmparser::Operator::I32AtomicRmw16AndU { .. }
577            | wasmparser::Operator::I64AtomicRmw8AndU { .. }
578            | wasmparser::Operator::I64AtomicRmw16AndU { .. }
579            | wasmparser::Operator::I64AtomicRmw32AndU { .. }
580            | wasmparser::Operator::I32AtomicRmwOr { .. }
581            | wasmparser::Operator::I64AtomicRmwOr { .. }
582            | wasmparser::Operator::I32AtomicRmw8OrU { .. }
583            | wasmparser::Operator::I32AtomicRmw16OrU { .. }
584            | wasmparser::Operator::I64AtomicRmw8OrU { .. }
585            | wasmparser::Operator::I64AtomicRmw16OrU { .. }
586            | wasmparser::Operator::I64AtomicRmw32OrU { .. }
587            | wasmparser::Operator::I32AtomicRmwXor { .. }
588            | wasmparser::Operator::I64AtomicRmwXor { .. }
589            | wasmparser::Operator::I32AtomicRmw8XorU { .. }
590            | wasmparser::Operator::I32AtomicRmw16XorU { .. }
591            | wasmparser::Operator::I64AtomicRmw8XorU { .. }
592            | wasmparser::Operator::I64AtomicRmw16XorU { .. }
593            | wasmparser::Operator::I64AtomicRmw32XorU { .. }
594            | wasmparser::Operator::I32AtomicRmwXchg { .. }
595            | wasmparser::Operator::I64AtomicRmwXchg { .. }
596            | wasmparser::Operator::I32AtomicRmw8XchgU { .. }
597            | wasmparser::Operator::I32AtomicRmw16XchgU { .. }
598            | wasmparser::Operator::I64AtomicRmw8XchgU { .. }
599            | wasmparser::Operator::I64AtomicRmw16XchgU { .. }
600            | wasmparser::Operator::I64AtomicRmw32XchgU { .. }
601            | wasmparser::Operator::I32AtomicRmwCmpxchg { .. }
602            | wasmparser::Operator::I64AtomicRmwCmpxchg { .. }
603            | wasmparser::Operator::I32AtomicRmw8CmpxchgU { .. }
604            | wasmparser::Operator::I32AtomicRmw16CmpxchgU { .. }
605            | wasmparser::Operator::I64AtomicRmw8CmpxchgU { .. }
606            | wasmparser::Operator::I64AtomicRmw16CmpxchgU { .. }
607            | wasmparser::Operator::I64AtomicRmw32CmpxchgU { .. } => Some(false),
608            // === Shared-everything threads ===
609            // no control flow
610            wasmparser::Operator::GlobalAtomicGet { .. }
611            | wasmparser::Operator::GlobalAtomicSet { .. }
612            | wasmparser::Operator::GlobalAtomicRmwAdd { .. }
613            | wasmparser::Operator::GlobalAtomicRmwSub { .. }
614            | wasmparser::Operator::GlobalAtomicRmwAnd { .. }
615            | wasmparser::Operator::GlobalAtomicRmwOr { .. }
616            | wasmparser::Operator::GlobalAtomicRmwXor { .. }
617            | wasmparser::Operator::GlobalAtomicRmwXchg { .. }
618            | wasmparser::Operator::GlobalAtomicRmwCmpxchg { .. }
619            | wasmparser::Operator::TableAtomicGet { .. }
620            | wasmparser::Operator::TableAtomicSet { .. }
621            | wasmparser::Operator::TableAtomicRmwXchg { .. }
622            | wasmparser::Operator::TableAtomicRmwCmpxchg { .. }
623            | wasmparser::Operator::StructAtomicGet { .. }
624            | wasmparser::Operator::StructAtomicGetS { .. }
625            | wasmparser::Operator::StructAtomicGetU { .. }
626            | wasmparser::Operator::StructAtomicSet { .. }
627            | wasmparser::Operator::StructAtomicRmwAdd { .. }
628            | wasmparser::Operator::StructAtomicRmwSub { .. }
629            | wasmparser::Operator::StructAtomicRmwAnd { .. }
630            | wasmparser::Operator::StructAtomicRmwOr { .. }
631            | wasmparser::Operator::StructAtomicRmwXor { .. }
632            | wasmparser::Operator::StructAtomicRmwXchg { .. }
633            | wasmparser::Operator::StructAtomicRmwCmpxchg { .. }
634            | wasmparser::Operator::ArrayAtomicGet { .. }
635            | wasmparser::Operator::ArrayAtomicGetS { .. }
636            | wasmparser::Operator::ArrayAtomicGetU { .. }
637            | wasmparser::Operator::ArrayAtomicSet { .. }
638            | wasmparser::Operator::ArrayAtomicRmwAdd { .. }
639            | wasmparser::Operator::ArrayAtomicRmwSub { .. }
640            | wasmparser::Operator::ArrayAtomicRmwAnd { .. }
641            | wasmparser::Operator::ArrayAtomicRmwOr { .. }
642            | wasmparser::Operator::ArrayAtomicRmwXor { .. }
643            | wasmparser::Operator::ArrayAtomicRmwXchg { .. }
644            | wasmparser::Operator::ArrayAtomicRmwCmpxchg { .. }
645            | wasmparser::Operator::RefI31Shared => Some(false),
646            // === SIMD ===
647            // no control flow
648            wasmparser::Operator::V128Load { .. }
649            | wasmparser::Operator::V128Load8x8S { .. }
650            | wasmparser::Operator::V128Load8x8U { .. }
651            | wasmparser::Operator::V128Load16x4S { .. }
652            | wasmparser::Operator::V128Load16x4U { .. }
653            | wasmparser::Operator::V128Load32x2S { .. }
654            | wasmparser::Operator::V128Load32x2U { .. }
655            | wasmparser::Operator::V128Load8Splat { .. }
656            | wasmparser::Operator::V128Load16Splat { .. }
657            | wasmparser::Operator::V128Load32Splat { .. }
658            | wasmparser::Operator::V128Load64Splat { .. }
659            | wasmparser::Operator::V128Load32Zero { .. }
660            | wasmparser::Operator::V128Load64Zero { .. }
661            | wasmparser::Operator::V128Store { .. }
662            | wasmparser::Operator::V128Load8Lane { .. }
663            | wasmparser::Operator::V128Load16Lane { .. }
664            | wasmparser::Operator::V128Load32Lane { .. }
665            | wasmparser::Operator::V128Load64Lane { .. }
666            | wasmparser::Operator::V128Store8Lane { .. }
667            | wasmparser::Operator::V128Store16Lane { .. }
668            | wasmparser::Operator::V128Store32Lane { .. }
669            | wasmparser::Operator::V128Store64Lane { .. }
670            | wasmparser::Operator::V128Const { .. }
671            | wasmparser::Operator::I8x16Shuffle { .. }
672            | wasmparser::Operator::I8x16ExtractLaneS { .. }
673            | wasmparser::Operator::I8x16ExtractLaneU { .. }
674            | wasmparser::Operator::I8x16ReplaceLane { .. }
675            | wasmparser::Operator::I16x8ExtractLaneS { .. }
676            | wasmparser::Operator::I16x8ExtractLaneU { .. }
677            | wasmparser::Operator::I16x8ReplaceLane { .. }
678            | wasmparser::Operator::I32x4ExtractLane { .. }
679            | wasmparser::Operator::I32x4ReplaceLane { .. }
680            | wasmparser::Operator::I64x2ExtractLane { .. }
681            | wasmparser::Operator::I64x2ReplaceLane { .. }
682            | wasmparser::Operator::F32x4ExtractLane { .. }
683            | wasmparser::Operator::F32x4ReplaceLane { .. }
684            | wasmparser::Operator::F64x2ExtractLane { .. }
685            | wasmparser::Operator::F64x2ReplaceLane { .. }
686            | wasmparser::Operator::I8x16Swizzle
687            | wasmparser::Operator::I8x16Splat
688            | wasmparser::Operator::I16x8Splat
689            | wasmparser::Operator::I32x4Splat
690            | wasmparser::Operator::I64x2Splat
691            | wasmparser::Operator::F32x4Splat
692            | wasmparser::Operator::F64x2Splat
693            | wasmparser::Operator::I8x16Eq
694            | wasmparser::Operator::I8x16Ne
695            | wasmparser::Operator::I8x16LtS
696            | wasmparser::Operator::I8x16LtU
697            | wasmparser::Operator::I8x16GtS
698            | wasmparser::Operator::I8x16GtU
699            | wasmparser::Operator::I8x16LeS
700            | wasmparser::Operator::I8x16LeU
701            | wasmparser::Operator::I8x16GeS
702            | wasmparser::Operator::I8x16GeU
703            | wasmparser::Operator::I16x8Eq
704            | wasmparser::Operator::I16x8Ne
705            | wasmparser::Operator::I16x8LtS
706            | wasmparser::Operator::I16x8LtU
707            | wasmparser::Operator::I16x8GtS
708            | wasmparser::Operator::I16x8GtU
709            | wasmparser::Operator::I16x8LeS
710            | wasmparser::Operator::I16x8LeU
711            | wasmparser::Operator::I16x8GeS
712            | wasmparser::Operator::I16x8GeU
713            | wasmparser::Operator::I32x4Eq
714            | wasmparser::Operator::I32x4Ne
715            | wasmparser::Operator::I32x4LtS
716            | wasmparser::Operator::I32x4LtU
717            | wasmparser::Operator::I32x4GtS
718            | wasmparser::Operator::I32x4GtU
719            | wasmparser::Operator::I32x4LeS
720            | wasmparser::Operator::I32x4LeU
721            | wasmparser::Operator::I32x4GeS
722            | wasmparser::Operator::I32x4GeU
723            | wasmparser::Operator::I64x2Eq
724            | wasmparser::Operator::I64x2Ne
725            | wasmparser::Operator::I64x2LtS
726            | wasmparser::Operator::I64x2GtS
727            | wasmparser::Operator::I64x2LeS
728            | wasmparser::Operator::I64x2GeS
729            | wasmparser::Operator::F32x4Eq
730            | wasmparser::Operator::F32x4Ne
731            | wasmparser::Operator::F32x4Lt
732            | wasmparser::Operator::F32x4Gt
733            | wasmparser::Operator::F32x4Le
734            | wasmparser::Operator::F32x4Ge
735            | wasmparser::Operator::F64x2Eq
736            | wasmparser::Operator::F64x2Ne
737            | wasmparser::Operator::F64x2Lt
738            | wasmparser::Operator::F64x2Gt
739            | wasmparser::Operator::F64x2Le
740            | wasmparser::Operator::F64x2Ge
741            | wasmparser::Operator::V128Not
742            | wasmparser::Operator::V128And
743            | wasmparser::Operator::V128AndNot
744            | wasmparser::Operator::V128Or
745            | wasmparser::Operator::V128Xor
746            | wasmparser::Operator::V128Bitselect
747            | wasmparser::Operator::V128AnyTrue
748            | wasmparser::Operator::I8x16Abs
749            | wasmparser::Operator::I8x16Neg
750            | wasmparser::Operator::I8x16Popcnt
751            | wasmparser::Operator::I8x16AllTrue
752            | wasmparser::Operator::I8x16Bitmask
753            | wasmparser::Operator::I8x16NarrowI16x8S
754            | wasmparser::Operator::I8x16NarrowI16x8U
755            | wasmparser::Operator::I8x16Shl
756            | wasmparser::Operator::I8x16ShrS
757            | wasmparser::Operator::I8x16ShrU
758            | wasmparser::Operator::I8x16Add
759            | wasmparser::Operator::I8x16AddSatS
760            | wasmparser::Operator::I8x16AddSatU
761            | wasmparser::Operator::I8x16Sub
762            | wasmparser::Operator::I8x16SubSatS
763            | wasmparser::Operator::I8x16SubSatU
764            | wasmparser::Operator::I8x16MinS
765            | wasmparser::Operator::I8x16MinU
766            | wasmparser::Operator::I8x16MaxS
767            | wasmparser::Operator::I8x16MaxU
768            | wasmparser::Operator::I8x16AvgrU
769            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16S
770            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16U
771            | wasmparser::Operator::I16x8Abs
772            | wasmparser::Operator::I16x8Neg
773            | wasmparser::Operator::I16x8Q15MulrSatS
774            | wasmparser::Operator::I16x8AllTrue
775            | wasmparser::Operator::I16x8Bitmask
776            | wasmparser::Operator::I16x8NarrowI32x4S
777            | wasmparser::Operator::I16x8NarrowI32x4U
778            | wasmparser::Operator::I16x8ExtendLowI8x16S
779            | wasmparser::Operator::I16x8ExtendHighI8x16S
780            | wasmparser::Operator::I16x8ExtendLowI8x16U
781            | wasmparser::Operator::I16x8ExtendHighI8x16U
782            | wasmparser::Operator::I16x8Shl
783            | wasmparser::Operator::I16x8ShrS
784            | wasmparser::Operator::I16x8ShrU
785            | wasmparser::Operator::I16x8Add
786            | wasmparser::Operator::I16x8AddSatS
787            | wasmparser::Operator::I16x8AddSatU
788            | wasmparser::Operator::I16x8Sub
789            | wasmparser::Operator::I16x8SubSatS
790            | wasmparser::Operator::I16x8SubSatU
791            | wasmparser::Operator::I16x8Mul
792            | wasmparser::Operator::I16x8MinS
793            | wasmparser::Operator::I16x8MinU
794            | wasmparser::Operator::I16x8MaxS
795            | wasmparser::Operator::I16x8MaxU
796            | wasmparser::Operator::I16x8AvgrU
797            | wasmparser::Operator::I16x8ExtMulLowI8x16S
798            | wasmparser::Operator::I16x8ExtMulHighI8x16S
799            | wasmparser::Operator::I16x8ExtMulLowI8x16U
800            | wasmparser::Operator::I16x8ExtMulHighI8x16U
801            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8S
802            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8U
803            | wasmparser::Operator::I32x4Abs
804            | wasmparser::Operator::I32x4Neg
805            | wasmparser::Operator::I32x4AllTrue
806            | wasmparser::Operator::I32x4Bitmask
807            | wasmparser::Operator::I32x4ExtendLowI16x8S
808            | wasmparser::Operator::I32x4ExtendHighI16x8S
809            | wasmparser::Operator::I32x4ExtendLowI16x8U
810            | wasmparser::Operator::I32x4ExtendHighI16x8U
811            | wasmparser::Operator::I32x4Shl
812            | wasmparser::Operator::I32x4ShrS
813            | wasmparser::Operator::I32x4ShrU
814            | wasmparser::Operator::I32x4Add
815            | wasmparser::Operator::I32x4Sub
816            | wasmparser::Operator::I32x4Mul
817            | wasmparser::Operator::I32x4MinS
818            | wasmparser::Operator::I32x4MinU
819            | wasmparser::Operator::I32x4MaxS
820            | wasmparser::Operator::I32x4MaxU
821            | wasmparser::Operator::I32x4DotI16x8S
822            | wasmparser::Operator::I32x4ExtMulLowI16x8S
823            | wasmparser::Operator::I32x4ExtMulHighI16x8S
824            | wasmparser::Operator::I32x4ExtMulLowI16x8U
825            | wasmparser::Operator::I32x4ExtMulHighI16x8U
826            | wasmparser::Operator::I64x2Abs
827            | wasmparser::Operator::I64x2Neg
828            | wasmparser::Operator::I64x2AllTrue
829            | wasmparser::Operator::I64x2Bitmask
830            | wasmparser::Operator::I64x2ExtendLowI32x4S
831            | wasmparser::Operator::I64x2ExtendHighI32x4S
832            | wasmparser::Operator::I64x2ExtendLowI32x4U
833            | wasmparser::Operator::I64x2ExtendHighI32x4U
834            | wasmparser::Operator::I64x2Shl
835            | wasmparser::Operator::I64x2ShrS
836            | wasmparser::Operator::I64x2ShrU
837            | wasmparser::Operator::I64x2Add
838            | wasmparser::Operator::I64x2Sub
839            | wasmparser::Operator::I64x2Mul
840            | wasmparser::Operator::I64x2ExtMulLowI32x4S
841            | wasmparser::Operator::I64x2ExtMulHighI32x4S
842            | wasmparser::Operator::I64x2ExtMulLowI32x4U
843            | wasmparser::Operator::I64x2ExtMulHighI32x4U
844            | wasmparser::Operator::F32x4Ceil
845            | wasmparser::Operator::F32x4Floor
846            | wasmparser::Operator::F32x4Trunc
847            | wasmparser::Operator::F32x4Nearest
848            | wasmparser::Operator::F32x4Abs
849            | wasmparser::Operator::F32x4Neg
850            | wasmparser::Operator::F32x4Sqrt
851            | wasmparser::Operator::F32x4Add
852            | wasmparser::Operator::F32x4Sub
853            | wasmparser::Operator::F32x4Mul
854            | wasmparser::Operator::F32x4Div
855            | wasmparser::Operator::F32x4Min
856            | wasmparser::Operator::F32x4Max
857            | wasmparser::Operator::F32x4PMin
858            | wasmparser::Operator::F32x4PMax
859            | wasmparser::Operator::F64x2Ceil
860            | wasmparser::Operator::F64x2Floor
861            | wasmparser::Operator::F64x2Trunc
862            | wasmparser::Operator::F64x2Nearest
863            | wasmparser::Operator::F64x2Abs
864            | wasmparser::Operator::F64x2Neg
865            | wasmparser::Operator::F64x2Sqrt
866            | wasmparser::Operator::F64x2Add
867            | wasmparser::Operator::F64x2Sub
868            | wasmparser::Operator::F64x2Mul
869            | wasmparser::Operator::F64x2Div
870            | wasmparser::Operator::F64x2Min
871            | wasmparser::Operator::F64x2Max
872            | wasmparser::Operator::F64x2PMin
873            | wasmparser::Operator::F64x2PMax
874            | wasmparser::Operator::I32x4TruncSatF32x4S
875            | wasmparser::Operator::I32x4TruncSatF32x4U
876            | wasmparser::Operator::F32x4ConvertI32x4S
877            | wasmparser::Operator::F32x4ConvertI32x4U
878            | wasmparser::Operator::I32x4TruncSatF64x2SZero
879            | wasmparser::Operator::I32x4TruncSatF64x2UZero
880            | wasmparser::Operator::F64x2ConvertLowI32x4S
881            | wasmparser::Operator::F64x2ConvertLowI32x4U
882            | wasmparser::Operator::F32x4DemoteF64x2Zero
883            | wasmparser::Operator::F64x2PromoteLowF32x4 => Some(false),
884            // === Relaxed SIMD ===
885            // no control flow
886            wasmparser::Operator::I8x16RelaxedSwizzle
887            | wasmparser::Operator::I32x4RelaxedTruncF32x4S
888            | wasmparser::Operator::I32x4RelaxedTruncF32x4U
889            | wasmparser::Operator::I32x4RelaxedTruncF64x2SZero
890            | wasmparser::Operator::I32x4RelaxedTruncF64x2UZero
891            | wasmparser::Operator::F32x4RelaxedMadd
892            | wasmparser::Operator::F32x4RelaxedNmadd
893            | wasmparser::Operator::F64x2RelaxedMadd
894            | wasmparser::Operator::F64x2RelaxedNmadd
895            | wasmparser::Operator::I8x16RelaxedLaneselect
896            | wasmparser::Operator::I16x8RelaxedLaneselect
897            | wasmparser::Operator::I32x4RelaxedLaneselect
898            | wasmparser::Operator::I64x2RelaxedLaneselect
899            | wasmparser::Operator::F32x4RelaxedMin
900            | wasmparser::Operator::F32x4RelaxedMax
901            | wasmparser::Operator::F64x2RelaxedMin
902            | wasmparser::Operator::F64x2RelaxedMax
903            | wasmparser::Operator::I16x8RelaxedQ15mulrS
904            | wasmparser::Operator::I16x8RelaxedDotI8x16I7x16S
905            | wasmparser::Operator::I32x4RelaxedDotI8x16I7x16AddS => Some(false),
906            // === Typed function references ===
907            // calling into a typed function will return control flow right back
908            //  back to here, so saving is not necessary
909            wasmparser::Operator::CallRef { .. } => Some(false),
910            // typed tail calls need to save since they return from this
911            //  function
912            wasmparser::Operator::ReturnCallRef { .. } => Some(true),
913            // no (observable) control flow (except for trapping)
914            wasmparser::Operator::RefAsNonNull => Some(false),
915            // we need to save before diverging control flow
916            wasmparser::Operator::BrOnNull { .. } | wasmparser::Operator::BrOnNonNull { .. } => {
917                Some(true)
918            }
919            // === Stack switching ===
920            // creating a continuation does not change the control flow (yet)
921            wasmparser::Operator::ContNew { .. } | wasmparser::Operator::ContBind { .. } => {
922                Some(false)
923            }
924            // we need to save before diverging control flow by suspending or
925            //  resuming a continuation diverg
926            wasmparser::Operator::Suspend { .. }
927            | wasmparser::Operator::Resume { .. }
928            | wasmparser::Operator::ResumeThrow { .. } => Some(true),
929            // we need to save before diverging control flow by switching to a
930            //  different continuation
931            wasmparser::Operator::Switch { .. } => Some(true),
932            // === Wide Arithmetic ===
933            // no control flow
934            wasmparser::Operator::I64Add128
935            | wasmparser::Operator::I64Sub128
936            | wasmparser::Operator::I64MulWideS
937            | wasmparser::Operator::I64MulWideU => Some(false),
938            // === FIXME ===
939            #[cfg(not(test))]
940            #[expect(clippy::panic)]
941            _ => panic!("unsupported instruction"),
942            #[cfg(test)]
943            #[expect(unsafe_code)]
944            _ => {
945                unsafe extern "C" {
946                    fn instruction_counter_unhandled_operator() -> !;
947                }
948                unsafe { instruction_counter_unhandled_operator() }
949            }
950        }
951    }
952}
953
954pub struct PerfWitInterfaces {
955    pub perf: InterfaceIdentifier,
956    pub instruction_counter: String,
957}
958
959impl PerfWitInterfaces {
960    #[must_use]
961    pub fn get() -> &'static Self {
962        static PERF_WIT_INTERFACES: OnceLock<PerfWitInterfaces> = OnceLock::new();
963
964        PERF_WIT_INTERFACES.get_or_init(|| Self {
965            perf: InterfaceIdentifier::new(
966                PackageIdentifier::new(
967                    PackageName::new("numcodecs", "wasm"),
968                    Some(Version::new(0, 1, 0)),
969                ),
970                "perf",
971            ),
972            instruction_counter: String::from("instruction-counter"),
973        })
974    }
975}