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 { .. }
296            | wasmparser::Operator::TypedSelectMulti { .. } => Some(false),
297            // === MVP ===
298            // no control flow
299            wasmparser::Operator::LocalGet { .. }
300            | wasmparser::Operator::LocalSet { .. }
301            | wasmparser::Operator::LocalTee { .. }
302            | wasmparser::Operator::GlobalGet { .. }
303            | wasmparser::Operator::GlobalSet { .. }
304            | wasmparser::Operator::I32Load { .. }
305            | wasmparser::Operator::I64Load { .. }
306            | wasmparser::Operator::F32Load { .. }
307            | wasmparser::Operator::F64Load { .. }
308            | wasmparser::Operator::I32Load8S { .. }
309            | wasmparser::Operator::I32Load8U { .. }
310            | wasmparser::Operator::I32Load16S { .. }
311            | wasmparser::Operator::I32Load16U { .. }
312            | wasmparser::Operator::I64Load8S { .. }
313            | wasmparser::Operator::I64Load8U { .. }
314            | wasmparser::Operator::I64Load16S { .. }
315            | wasmparser::Operator::I64Load16U { .. }
316            | wasmparser::Operator::I64Load32S { .. }
317            | wasmparser::Operator::I64Load32U { .. }
318            | wasmparser::Operator::I32Store { .. }
319            | wasmparser::Operator::I64Store { .. }
320            | wasmparser::Operator::F32Store { .. }
321            | wasmparser::Operator::F64Store { .. }
322            | wasmparser::Operator::I32Store8 { .. }
323            | wasmparser::Operator::I32Store16 { .. }
324            | wasmparser::Operator::I64Store8 { .. }
325            | wasmparser::Operator::I64Store16 { .. }
326            | wasmparser::Operator::I64Store32 { .. }
327            | wasmparser::Operator::MemorySize { .. }
328            | wasmparser::Operator::MemoryGrow { .. }
329            | wasmparser::Operator::I32Const { .. }
330            | wasmparser::Operator::I64Const { .. }
331            | wasmparser::Operator::F32Const { .. }
332            | wasmparser::Operator::F64Const { .. } => Some(false),
333            // === Reference types ===
334            // no control flow
335            wasmparser::Operator::RefNull { .. }
336            | wasmparser::Operator::RefIsNull
337            | wasmparser::Operator::RefFunc { .. } => Some(false),
338            // === Garbage collection ===
339            // no control flow
340            wasmparser::Operator::RefEq => Some(false),
341            // === MVP ===
342            // no control flow
343            wasmparser::Operator::I32Eqz
344            | wasmparser::Operator::I32Eq
345            | wasmparser::Operator::I32Ne
346            | wasmparser::Operator::I32LtS
347            | wasmparser::Operator::I32LtU
348            | wasmparser::Operator::I32GtS
349            | wasmparser::Operator::I32GtU
350            | wasmparser::Operator::I32LeS
351            | wasmparser::Operator::I32LeU
352            | wasmparser::Operator::I32GeS
353            | wasmparser::Operator::I32GeU
354            | wasmparser::Operator::I64Eqz
355            | wasmparser::Operator::I64Eq
356            | wasmparser::Operator::I64Ne
357            | wasmparser::Operator::I64LtS
358            | wasmparser::Operator::I64LtU
359            | wasmparser::Operator::I64GtS
360            | wasmparser::Operator::I64GtU
361            | wasmparser::Operator::I64LeS
362            | wasmparser::Operator::I64LeU
363            | wasmparser::Operator::I64GeS
364            | wasmparser::Operator::I64GeU
365            | wasmparser::Operator::F32Eq
366            | wasmparser::Operator::F32Ne
367            | wasmparser::Operator::F32Lt
368            | wasmparser::Operator::F32Gt
369            | wasmparser::Operator::F32Le
370            | wasmparser::Operator::F32Ge
371            | wasmparser::Operator::F64Eq
372            | wasmparser::Operator::F64Ne
373            | wasmparser::Operator::F64Lt
374            | wasmparser::Operator::F64Gt
375            | wasmparser::Operator::F64Le
376            | wasmparser::Operator::F64Ge
377            | wasmparser::Operator::I32Clz
378            | wasmparser::Operator::I32Ctz
379            | wasmparser::Operator::I32Popcnt
380            | wasmparser::Operator::I32Add
381            | wasmparser::Operator::I32Sub
382            | wasmparser::Operator::I32Mul
383            | wasmparser::Operator::I32DivS
384            | wasmparser::Operator::I32DivU
385            | wasmparser::Operator::I32RemS
386            | wasmparser::Operator::I32RemU
387            | wasmparser::Operator::I32And
388            | wasmparser::Operator::I32Or
389            | wasmparser::Operator::I32Xor
390            | wasmparser::Operator::I32Shl
391            | wasmparser::Operator::I32ShrS
392            | wasmparser::Operator::I32ShrU
393            | wasmparser::Operator::I32Rotl
394            | wasmparser::Operator::I32Rotr
395            | wasmparser::Operator::I64Clz
396            | wasmparser::Operator::I64Ctz
397            | wasmparser::Operator::I64Popcnt
398            | wasmparser::Operator::I64Add
399            | wasmparser::Operator::I64Sub
400            | wasmparser::Operator::I64Mul
401            | wasmparser::Operator::I64DivS
402            | wasmparser::Operator::I64DivU
403            | wasmparser::Operator::I64RemS
404            | wasmparser::Operator::I64RemU
405            | wasmparser::Operator::I64And
406            | wasmparser::Operator::I64Or
407            | wasmparser::Operator::I64Xor
408            | wasmparser::Operator::I64Shl
409            | wasmparser::Operator::I64ShrS
410            | wasmparser::Operator::I64ShrU
411            | wasmparser::Operator::I64Rotl
412            | wasmparser::Operator::I64Rotr
413            | wasmparser::Operator::F32Abs
414            | wasmparser::Operator::F32Neg
415            | wasmparser::Operator::F32Ceil
416            | wasmparser::Operator::F32Floor
417            | wasmparser::Operator::F32Trunc
418            | wasmparser::Operator::F32Nearest
419            | wasmparser::Operator::F32Sqrt
420            | wasmparser::Operator::F32Add
421            | wasmparser::Operator::F32Sub
422            | wasmparser::Operator::F32Mul
423            | wasmparser::Operator::F32Div
424            | wasmparser::Operator::F32Min
425            | wasmparser::Operator::F32Max
426            | wasmparser::Operator::F32Copysign
427            | wasmparser::Operator::F64Abs
428            | wasmparser::Operator::F64Neg
429            | wasmparser::Operator::F64Ceil
430            | wasmparser::Operator::F64Floor
431            | wasmparser::Operator::F64Trunc
432            | wasmparser::Operator::F64Nearest
433            | wasmparser::Operator::F64Sqrt
434            | wasmparser::Operator::F64Add
435            | wasmparser::Operator::F64Sub
436            | wasmparser::Operator::F64Mul
437            | wasmparser::Operator::F64Div
438            | wasmparser::Operator::F64Min
439            | wasmparser::Operator::F64Max
440            | wasmparser::Operator::F64Copysign
441            | wasmparser::Operator::I32WrapI64
442            | wasmparser::Operator::I32TruncF32S
443            | wasmparser::Operator::I32TruncF32U
444            | wasmparser::Operator::I32TruncF64S
445            | wasmparser::Operator::I32TruncF64U
446            | wasmparser::Operator::I64ExtendI32S
447            | wasmparser::Operator::I64ExtendI32U
448            | wasmparser::Operator::I64TruncF32S
449            | wasmparser::Operator::I64TruncF32U
450            | wasmparser::Operator::I64TruncF64S
451            | wasmparser::Operator::I64TruncF64U
452            | wasmparser::Operator::F32ConvertI32S
453            | wasmparser::Operator::F32ConvertI32U
454            | wasmparser::Operator::F32ConvertI64S
455            | wasmparser::Operator::F32ConvertI64U
456            | wasmparser::Operator::F32DemoteF64
457            | wasmparser::Operator::F64ConvertI32S
458            | wasmparser::Operator::F64ConvertI32U
459            | wasmparser::Operator::F64ConvertI64S
460            | wasmparser::Operator::F64ConvertI64U
461            | wasmparser::Operator::F64PromoteF32
462            | wasmparser::Operator::I32ReinterpretF32
463            | wasmparser::Operator::I64ReinterpretF64
464            | wasmparser::Operator::F32ReinterpretI32
465            | wasmparser::Operator::F64ReinterpretI64 => Some(false),
466            // === Sign extension ===
467            // no control flow
468            wasmparser::Operator::I32Extend8S
469            | wasmparser::Operator::I32Extend16S
470            | wasmparser::Operator::I64Extend8S
471            | wasmparser::Operator::I64Extend16S
472            | wasmparser::Operator::I64Extend32S => Some(false),
473            // === Garbage collection ===
474            // no control flow
475            wasmparser::Operator::StructNew { .. }
476            | wasmparser::Operator::StructNewDefault { .. }
477            | wasmparser::Operator::StructGet { .. }
478            | wasmparser::Operator::StructGetS { .. }
479            | wasmparser::Operator::StructGetU { .. }
480            | wasmparser::Operator::StructSet { .. }
481            | wasmparser::Operator::ArrayNew { .. }
482            | wasmparser::Operator::ArrayNewDefault { .. }
483            | wasmparser::Operator::ArrayNewFixed { .. }
484            | wasmparser::Operator::ArrayNewData { .. }
485            | wasmparser::Operator::ArrayNewElem { .. }
486            | wasmparser::Operator::ArrayGet { .. }
487            | wasmparser::Operator::ArrayGetS { .. }
488            | wasmparser::Operator::ArrayGetU { .. }
489            | wasmparser::Operator::ArraySet { .. }
490            | wasmparser::Operator::ArrayLen
491            | wasmparser::Operator::ArrayFill { .. }
492            | wasmparser::Operator::ArrayCopy { .. }
493            | wasmparser::Operator::ArrayInitData { .. }
494            | wasmparser::Operator::ArrayInitElem { .. } => Some(false),
495            // no control flow
496            wasmparser::Operator::RefTestNonNull { .. }
497            | wasmparser::Operator::RefTestNullable { .. } => Some(false),
498            // no (observable) control flow (except for trapping)
499            wasmparser::Operator::RefCastNonNull { .. }
500            | wasmparser::Operator::RefCastNullable { .. } => Some(false),
501            // we need to save before diverging control flow
502            wasmparser::Operator::BrOnCast { .. } | wasmparser::Operator::BrOnCastFail { .. } => {
503                Some(true)
504            }
505            // no control flow
506            wasmparser::Operator::AnyConvertExtern
507            | wasmparser::Operator::ExternConvertAny
508            | wasmparser::Operator::RefI31
509            | wasmparser::Operator::I31GetS
510            | wasmparser::Operator::I31GetU => Some(false),
511            // === Non-trapping float-to-int conversions ===
512            // no control flow
513            wasmparser::Operator::I32TruncSatF32S
514            | wasmparser::Operator::I32TruncSatF32U
515            | wasmparser::Operator::I32TruncSatF64S
516            | wasmparser::Operator::I32TruncSatF64U
517            | wasmparser::Operator::I64TruncSatF32S
518            | wasmparser::Operator::I64TruncSatF32U
519            | wasmparser::Operator::I64TruncSatF64S
520            | wasmparser::Operator::I64TruncSatF64U => Some(false),
521            // === Bulk memory ===
522            // no control flow
523            wasmparser::Operator::MemoryInit { .. }
524            | wasmparser::Operator::DataDrop { .. }
525            | wasmparser::Operator::MemoryCopy { .. }
526            | wasmparser::Operator::MemoryFill { .. }
527            | wasmparser::Operator::TableInit { .. }
528            | wasmparser::Operator::ElemDrop { .. }
529            | wasmparser::Operator::TableCopy { .. } => Some(false),
530            // === Reference types ===
531            // no control flow
532            wasmparser::Operator::TableFill { .. }
533            | wasmparser::Operator::TableGet { .. }
534            | wasmparser::Operator::TableSet { .. }
535            | wasmparser::Operator::TableGrow { .. }
536            | wasmparser::Operator::TableSize { .. } => Some(false),
537            // === Memory control ===
538            // no control flow
539            wasmparser::Operator::MemoryDiscard { .. } => Some(false),
540            // === Threads ===
541            // no control flow
542            wasmparser::Operator::MemoryAtomicNotify { .. }
543            | wasmparser::Operator::MemoryAtomicWait32 { .. }
544            | wasmparser::Operator::MemoryAtomicWait64 { .. }
545            | wasmparser::Operator::AtomicFence
546            | wasmparser::Operator::I32AtomicLoad { .. }
547            | wasmparser::Operator::I64AtomicLoad { .. }
548            | wasmparser::Operator::I32AtomicLoad8U { .. }
549            | wasmparser::Operator::I32AtomicLoad16U { .. }
550            | wasmparser::Operator::I64AtomicLoad8U { .. }
551            | wasmparser::Operator::I64AtomicLoad16U { .. }
552            | wasmparser::Operator::I64AtomicLoad32U { .. }
553            | wasmparser::Operator::I32AtomicStore { .. }
554            | wasmparser::Operator::I64AtomicStore { .. }
555            | wasmparser::Operator::I32AtomicStore8 { .. }
556            | wasmparser::Operator::I32AtomicStore16 { .. }
557            | wasmparser::Operator::I64AtomicStore8 { .. }
558            | wasmparser::Operator::I64AtomicStore16 { .. }
559            | wasmparser::Operator::I64AtomicStore32 { .. }
560            | wasmparser::Operator::I32AtomicRmwAdd { .. }
561            | wasmparser::Operator::I64AtomicRmwAdd { .. }
562            | wasmparser::Operator::I32AtomicRmw8AddU { .. }
563            | wasmparser::Operator::I32AtomicRmw16AddU { .. }
564            | wasmparser::Operator::I64AtomicRmw8AddU { .. }
565            | wasmparser::Operator::I64AtomicRmw16AddU { .. }
566            | wasmparser::Operator::I64AtomicRmw32AddU { .. }
567            | wasmparser::Operator::I32AtomicRmwSub { .. }
568            | wasmparser::Operator::I64AtomicRmwSub { .. }
569            | wasmparser::Operator::I32AtomicRmw8SubU { .. }
570            | wasmparser::Operator::I32AtomicRmw16SubU { .. }
571            | wasmparser::Operator::I64AtomicRmw8SubU { .. }
572            | wasmparser::Operator::I64AtomicRmw16SubU { .. }
573            | wasmparser::Operator::I64AtomicRmw32SubU { .. }
574            | wasmparser::Operator::I32AtomicRmwAnd { .. }
575            | wasmparser::Operator::I64AtomicRmwAnd { .. }
576            | wasmparser::Operator::I32AtomicRmw8AndU { .. }
577            | wasmparser::Operator::I32AtomicRmw16AndU { .. }
578            | wasmparser::Operator::I64AtomicRmw8AndU { .. }
579            | wasmparser::Operator::I64AtomicRmw16AndU { .. }
580            | wasmparser::Operator::I64AtomicRmw32AndU { .. }
581            | wasmparser::Operator::I32AtomicRmwOr { .. }
582            | wasmparser::Operator::I64AtomicRmwOr { .. }
583            | wasmparser::Operator::I32AtomicRmw8OrU { .. }
584            | wasmparser::Operator::I32AtomicRmw16OrU { .. }
585            | wasmparser::Operator::I64AtomicRmw8OrU { .. }
586            | wasmparser::Operator::I64AtomicRmw16OrU { .. }
587            | wasmparser::Operator::I64AtomicRmw32OrU { .. }
588            | wasmparser::Operator::I32AtomicRmwXor { .. }
589            | wasmparser::Operator::I64AtomicRmwXor { .. }
590            | wasmparser::Operator::I32AtomicRmw8XorU { .. }
591            | wasmparser::Operator::I32AtomicRmw16XorU { .. }
592            | wasmparser::Operator::I64AtomicRmw8XorU { .. }
593            | wasmparser::Operator::I64AtomicRmw16XorU { .. }
594            | wasmparser::Operator::I64AtomicRmw32XorU { .. }
595            | wasmparser::Operator::I32AtomicRmwXchg { .. }
596            | wasmparser::Operator::I64AtomicRmwXchg { .. }
597            | wasmparser::Operator::I32AtomicRmw8XchgU { .. }
598            | wasmparser::Operator::I32AtomicRmw16XchgU { .. }
599            | wasmparser::Operator::I64AtomicRmw8XchgU { .. }
600            | wasmparser::Operator::I64AtomicRmw16XchgU { .. }
601            | wasmparser::Operator::I64AtomicRmw32XchgU { .. }
602            | wasmparser::Operator::I32AtomicRmwCmpxchg { .. }
603            | wasmparser::Operator::I64AtomicRmwCmpxchg { .. }
604            | wasmparser::Operator::I32AtomicRmw8CmpxchgU { .. }
605            | wasmparser::Operator::I32AtomicRmw16CmpxchgU { .. }
606            | wasmparser::Operator::I64AtomicRmw8CmpxchgU { .. }
607            | wasmparser::Operator::I64AtomicRmw16CmpxchgU { .. }
608            | wasmparser::Operator::I64AtomicRmw32CmpxchgU { .. } => Some(false),
609            // === Shared-everything threads ===
610            // no control flow
611            wasmparser::Operator::GlobalAtomicGet { .. }
612            | wasmparser::Operator::GlobalAtomicSet { .. }
613            | wasmparser::Operator::GlobalAtomicRmwAdd { .. }
614            | wasmparser::Operator::GlobalAtomicRmwSub { .. }
615            | wasmparser::Operator::GlobalAtomicRmwAnd { .. }
616            | wasmparser::Operator::GlobalAtomicRmwOr { .. }
617            | wasmparser::Operator::GlobalAtomicRmwXor { .. }
618            | wasmparser::Operator::GlobalAtomicRmwXchg { .. }
619            | wasmparser::Operator::GlobalAtomicRmwCmpxchg { .. }
620            | wasmparser::Operator::TableAtomicGet { .. }
621            | wasmparser::Operator::TableAtomicSet { .. }
622            | wasmparser::Operator::TableAtomicRmwXchg { .. }
623            | wasmparser::Operator::TableAtomicRmwCmpxchg { .. }
624            | wasmparser::Operator::StructAtomicGet { .. }
625            | wasmparser::Operator::StructAtomicGetS { .. }
626            | wasmparser::Operator::StructAtomicGetU { .. }
627            | wasmparser::Operator::StructAtomicSet { .. }
628            | wasmparser::Operator::StructAtomicRmwAdd { .. }
629            | wasmparser::Operator::StructAtomicRmwSub { .. }
630            | wasmparser::Operator::StructAtomicRmwAnd { .. }
631            | wasmparser::Operator::StructAtomicRmwOr { .. }
632            | wasmparser::Operator::StructAtomicRmwXor { .. }
633            | wasmparser::Operator::StructAtomicRmwXchg { .. }
634            | wasmparser::Operator::StructAtomicRmwCmpxchg { .. }
635            | wasmparser::Operator::ArrayAtomicGet { .. }
636            | wasmparser::Operator::ArrayAtomicGetS { .. }
637            | wasmparser::Operator::ArrayAtomicGetU { .. }
638            | wasmparser::Operator::ArrayAtomicSet { .. }
639            | wasmparser::Operator::ArrayAtomicRmwAdd { .. }
640            | wasmparser::Operator::ArrayAtomicRmwSub { .. }
641            | wasmparser::Operator::ArrayAtomicRmwAnd { .. }
642            | wasmparser::Operator::ArrayAtomicRmwOr { .. }
643            | wasmparser::Operator::ArrayAtomicRmwXor { .. }
644            | wasmparser::Operator::ArrayAtomicRmwXchg { .. }
645            | wasmparser::Operator::ArrayAtomicRmwCmpxchg { .. }
646            | wasmparser::Operator::RefI31Shared => Some(false),
647            // === SIMD ===
648            // no control flow
649            wasmparser::Operator::V128Load { .. }
650            | wasmparser::Operator::V128Load8x8S { .. }
651            | wasmparser::Operator::V128Load8x8U { .. }
652            | wasmparser::Operator::V128Load16x4S { .. }
653            | wasmparser::Operator::V128Load16x4U { .. }
654            | wasmparser::Operator::V128Load32x2S { .. }
655            | wasmparser::Operator::V128Load32x2U { .. }
656            | wasmparser::Operator::V128Load8Splat { .. }
657            | wasmparser::Operator::V128Load16Splat { .. }
658            | wasmparser::Operator::V128Load32Splat { .. }
659            | wasmparser::Operator::V128Load64Splat { .. }
660            | wasmparser::Operator::V128Load32Zero { .. }
661            | wasmparser::Operator::V128Load64Zero { .. }
662            | wasmparser::Operator::V128Store { .. }
663            | wasmparser::Operator::V128Load8Lane { .. }
664            | wasmparser::Operator::V128Load16Lane { .. }
665            | wasmparser::Operator::V128Load32Lane { .. }
666            | wasmparser::Operator::V128Load64Lane { .. }
667            | wasmparser::Operator::V128Store8Lane { .. }
668            | wasmparser::Operator::V128Store16Lane { .. }
669            | wasmparser::Operator::V128Store32Lane { .. }
670            | wasmparser::Operator::V128Store64Lane { .. }
671            | wasmparser::Operator::V128Const { .. }
672            | wasmparser::Operator::I8x16Shuffle { .. }
673            | wasmparser::Operator::I8x16ExtractLaneS { .. }
674            | wasmparser::Operator::I8x16ExtractLaneU { .. }
675            | wasmparser::Operator::I8x16ReplaceLane { .. }
676            | wasmparser::Operator::I16x8ExtractLaneS { .. }
677            | wasmparser::Operator::I16x8ExtractLaneU { .. }
678            | wasmparser::Operator::I16x8ReplaceLane { .. }
679            | wasmparser::Operator::I32x4ExtractLane { .. }
680            | wasmparser::Operator::I32x4ReplaceLane { .. }
681            | wasmparser::Operator::I64x2ExtractLane { .. }
682            | wasmparser::Operator::I64x2ReplaceLane { .. }
683            | wasmparser::Operator::F32x4ExtractLane { .. }
684            | wasmparser::Operator::F32x4ReplaceLane { .. }
685            | wasmparser::Operator::F64x2ExtractLane { .. }
686            | wasmparser::Operator::F64x2ReplaceLane { .. }
687            | wasmparser::Operator::I8x16Swizzle
688            | wasmparser::Operator::I8x16Splat
689            | wasmparser::Operator::I16x8Splat
690            | wasmparser::Operator::I32x4Splat
691            | wasmparser::Operator::I64x2Splat
692            | wasmparser::Operator::F32x4Splat
693            | wasmparser::Operator::F64x2Splat
694            | wasmparser::Operator::I8x16Eq
695            | wasmparser::Operator::I8x16Ne
696            | wasmparser::Operator::I8x16LtS
697            | wasmparser::Operator::I8x16LtU
698            | wasmparser::Operator::I8x16GtS
699            | wasmparser::Operator::I8x16GtU
700            | wasmparser::Operator::I8x16LeS
701            | wasmparser::Operator::I8x16LeU
702            | wasmparser::Operator::I8x16GeS
703            | wasmparser::Operator::I8x16GeU
704            | wasmparser::Operator::I16x8Eq
705            | wasmparser::Operator::I16x8Ne
706            | wasmparser::Operator::I16x8LtS
707            | wasmparser::Operator::I16x8LtU
708            | wasmparser::Operator::I16x8GtS
709            | wasmparser::Operator::I16x8GtU
710            | wasmparser::Operator::I16x8LeS
711            | wasmparser::Operator::I16x8LeU
712            | wasmparser::Operator::I16x8GeS
713            | wasmparser::Operator::I16x8GeU
714            | wasmparser::Operator::I32x4Eq
715            | wasmparser::Operator::I32x4Ne
716            | wasmparser::Operator::I32x4LtS
717            | wasmparser::Operator::I32x4LtU
718            | wasmparser::Operator::I32x4GtS
719            | wasmparser::Operator::I32x4GtU
720            | wasmparser::Operator::I32x4LeS
721            | wasmparser::Operator::I32x4LeU
722            | wasmparser::Operator::I32x4GeS
723            | wasmparser::Operator::I32x4GeU
724            | wasmparser::Operator::I64x2Eq
725            | wasmparser::Operator::I64x2Ne
726            | wasmparser::Operator::I64x2LtS
727            | wasmparser::Operator::I64x2GtS
728            | wasmparser::Operator::I64x2LeS
729            | wasmparser::Operator::I64x2GeS
730            | wasmparser::Operator::F32x4Eq
731            | wasmparser::Operator::F32x4Ne
732            | wasmparser::Operator::F32x4Lt
733            | wasmparser::Operator::F32x4Gt
734            | wasmparser::Operator::F32x4Le
735            | wasmparser::Operator::F32x4Ge
736            | wasmparser::Operator::F64x2Eq
737            | wasmparser::Operator::F64x2Ne
738            | wasmparser::Operator::F64x2Lt
739            | wasmparser::Operator::F64x2Gt
740            | wasmparser::Operator::F64x2Le
741            | wasmparser::Operator::F64x2Ge
742            | wasmparser::Operator::V128Not
743            | wasmparser::Operator::V128And
744            | wasmparser::Operator::V128AndNot
745            | wasmparser::Operator::V128Or
746            | wasmparser::Operator::V128Xor
747            | wasmparser::Operator::V128Bitselect
748            | wasmparser::Operator::V128AnyTrue
749            | wasmparser::Operator::I8x16Abs
750            | wasmparser::Operator::I8x16Neg
751            | wasmparser::Operator::I8x16Popcnt
752            | wasmparser::Operator::I8x16AllTrue
753            | wasmparser::Operator::I8x16Bitmask
754            | wasmparser::Operator::I8x16NarrowI16x8S
755            | wasmparser::Operator::I8x16NarrowI16x8U
756            | wasmparser::Operator::I8x16Shl
757            | wasmparser::Operator::I8x16ShrS
758            | wasmparser::Operator::I8x16ShrU
759            | wasmparser::Operator::I8x16Add
760            | wasmparser::Operator::I8x16AddSatS
761            | wasmparser::Operator::I8x16AddSatU
762            | wasmparser::Operator::I8x16Sub
763            | wasmparser::Operator::I8x16SubSatS
764            | wasmparser::Operator::I8x16SubSatU
765            | wasmparser::Operator::I8x16MinS
766            | wasmparser::Operator::I8x16MinU
767            | wasmparser::Operator::I8x16MaxS
768            | wasmparser::Operator::I8x16MaxU
769            | wasmparser::Operator::I8x16AvgrU
770            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16S
771            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16U
772            | wasmparser::Operator::I16x8Abs
773            | wasmparser::Operator::I16x8Neg
774            | wasmparser::Operator::I16x8Q15MulrSatS
775            | wasmparser::Operator::I16x8AllTrue
776            | wasmparser::Operator::I16x8Bitmask
777            | wasmparser::Operator::I16x8NarrowI32x4S
778            | wasmparser::Operator::I16x8NarrowI32x4U
779            | wasmparser::Operator::I16x8ExtendLowI8x16S
780            | wasmparser::Operator::I16x8ExtendHighI8x16S
781            | wasmparser::Operator::I16x8ExtendLowI8x16U
782            | wasmparser::Operator::I16x8ExtendHighI8x16U
783            | wasmparser::Operator::I16x8Shl
784            | wasmparser::Operator::I16x8ShrS
785            | wasmparser::Operator::I16x8ShrU
786            | wasmparser::Operator::I16x8Add
787            | wasmparser::Operator::I16x8AddSatS
788            | wasmparser::Operator::I16x8AddSatU
789            | wasmparser::Operator::I16x8Sub
790            | wasmparser::Operator::I16x8SubSatS
791            | wasmparser::Operator::I16x8SubSatU
792            | wasmparser::Operator::I16x8Mul
793            | wasmparser::Operator::I16x8MinS
794            | wasmparser::Operator::I16x8MinU
795            | wasmparser::Operator::I16x8MaxS
796            | wasmparser::Operator::I16x8MaxU
797            | wasmparser::Operator::I16x8AvgrU
798            | wasmparser::Operator::I16x8ExtMulLowI8x16S
799            | wasmparser::Operator::I16x8ExtMulHighI8x16S
800            | wasmparser::Operator::I16x8ExtMulLowI8x16U
801            | wasmparser::Operator::I16x8ExtMulHighI8x16U
802            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8S
803            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8U
804            | wasmparser::Operator::I32x4Abs
805            | wasmparser::Operator::I32x4Neg
806            | wasmparser::Operator::I32x4AllTrue
807            | wasmparser::Operator::I32x4Bitmask
808            | wasmparser::Operator::I32x4ExtendLowI16x8S
809            | wasmparser::Operator::I32x4ExtendHighI16x8S
810            | wasmparser::Operator::I32x4ExtendLowI16x8U
811            | wasmparser::Operator::I32x4ExtendHighI16x8U
812            | wasmparser::Operator::I32x4Shl
813            | wasmparser::Operator::I32x4ShrS
814            | wasmparser::Operator::I32x4ShrU
815            | wasmparser::Operator::I32x4Add
816            | wasmparser::Operator::I32x4Sub
817            | wasmparser::Operator::I32x4Mul
818            | wasmparser::Operator::I32x4MinS
819            | wasmparser::Operator::I32x4MinU
820            | wasmparser::Operator::I32x4MaxS
821            | wasmparser::Operator::I32x4MaxU
822            | wasmparser::Operator::I32x4DotI16x8S
823            | wasmparser::Operator::I32x4ExtMulLowI16x8S
824            | wasmparser::Operator::I32x4ExtMulHighI16x8S
825            | wasmparser::Operator::I32x4ExtMulLowI16x8U
826            | wasmparser::Operator::I32x4ExtMulHighI16x8U
827            | wasmparser::Operator::I64x2Abs
828            | wasmparser::Operator::I64x2Neg
829            | wasmparser::Operator::I64x2AllTrue
830            | wasmparser::Operator::I64x2Bitmask
831            | wasmparser::Operator::I64x2ExtendLowI32x4S
832            | wasmparser::Operator::I64x2ExtendHighI32x4S
833            | wasmparser::Operator::I64x2ExtendLowI32x4U
834            | wasmparser::Operator::I64x2ExtendHighI32x4U
835            | wasmparser::Operator::I64x2Shl
836            | wasmparser::Operator::I64x2ShrS
837            | wasmparser::Operator::I64x2ShrU
838            | wasmparser::Operator::I64x2Add
839            | wasmparser::Operator::I64x2Sub
840            | wasmparser::Operator::I64x2Mul
841            | wasmparser::Operator::I64x2ExtMulLowI32x4S
842            | wasmparser::Operator::I64x2ExtMulHighI32x4S
843            | wasmparser::Operator::I64x2ExtMulLowI32x4U
844            | wasmparser::Operator::I64x2ExtMulHighI32x4U
845            | wasmparser::Operator::F32x4Ceil
846            | wasmparser::Operator::F32x4Floor
847            | wasmparser::Operator::F32x4Trunc
848            | wasmparser::Operator::F32x4Nearest
849            | wasmparser::Operator::F32x4Abs
850            | wasmparser::Operator::F32x4Neg
851            | wasmparser::Operator::F32x4Sqrt
852            | wasmparser::Operator::F32x4Add
853            | wasmparser::Operator::F32x4Sub
854            | wasmparser::Operator::F32x4Mul
855            | wasmparser::Operator::F32x4Div
856            | wasmparser::Operator::F32x4Min
857            | wasmparser::Operator::F32x4Max
858            | wasmparser::Operator::F32x4PMin
859            | wasmparser::Operator::F32x4PMax
860            | wasmparser::Operator::F64x2Ceil
861            | wasmparser::Operator::F64x2Floor
862            | wasmparser::Operator::F64x2Trunc
863            | wasmparser::Operator::F64x2Nearest
864            | wasmparser::Operator::F64x2Abs
865            | wasmparser::Operator::F64x2Neg
866            | wasmparser::Operator::F64x2Sqrt
867            | wasmparser::Operator::F64x2Add
868            | wasmparser::Operator::F64x2Sub
869            | wasmparser::Operator::F64x2Mul
870            | wasmparser::Operator::F64x2Div
871            | wasmparser::Operator::F64x2Min
872            | wasmparser::Operator::F64x2Max
873            | wasmparser::Operator::F64x2PMin
874            | wasmparser::Operator::F64x2PMax
875            | wasmparser::Operator::I32x4TruncSatF32x4S
876            | wasmparser::Operator::I32x4TruncSatF32x4U
877            | wasmparser::Operator::F32x4ConvertI32x4S
878            | wasmparser::Operator::F32x4ConvertI32x4U
879            | wasmparser::Operator::I32x4TruncSatF64x2SZero
880            | wasmparser::Operator::I32x4TruncSatF64x2UZero
881            | wasmparser::Operator::F64x2ConvertLowI32x4S
882            | wasmparser::Operator::F64x2ConvertLowI32x4U
883            | wasmparser::Operator::F32x4DemoteF64x2Zero
884            | wasmparser::Operator::F64x2PromoteLowF32x4 => Some(false),
885            // === Relaxed SIMD ===
886            // no control flow
887            wasmparser::Operator::I8x16RelaxedSwizzle
888            | wasmparser::Operator::I32x4RelaxedTruncF32x4S
889            | wasmparser::Operator::I32x4RelaxedTruncF32x4U
890            | wasmparser::Operator::I32x4RelaxedTruncF64x2SZero
891            | wasmparser::Operator::I32x4RelaxedTruncF64x2UZero
892            | wasmparser::Operator::F32x4RelaxedMadd
893            | wasmparser::Operator::F32x4RelaxedNmadd
894            | wasmparser::Operator::F64x2RelaxedMadd
895            | wasmparser::Operator::F64x2RelaxedNmadd
896            | wasmparser::Operator::I8x16RelaxedLaneselect
897            | wasmparser::Operator::I16x8RelaxedLaneselect
898            | wasmparser::Operator::I32x4RelaxedLaneselect
899            | wasmparser::Operator::I64x2RelaxedLaneselect
900            | wasmparser::Operator::F32x4RelaxedMin
901            | wasmparser::Operator::F32x4RelaxedMax
902            | wasmparser::Operator::F64x2RelaxedMin
903            | wasmparser::Operator::F64x2RelaxedMax
904            | wasmparser::Operator::I16x8RelaxedQ15mulrS
905            | wasmparser::Operator::I16x8RelaxedDotI8x16I7x16S
906            | wasmparser::Operator::I32x4RelaxedDotI8x16I7x16AddS => Some(false),
907            // === Typed function references ===
908            // calling into a typed function will return control flow right back
909            //  back to here, so saving is not necessary
910            wasmparser::Operator::CallRef { .. } => Some(false),
911            // typed tail calls need to save since they return from this
912            //  function
913            wasmparser::Operator::ReturnCallRef { .. } => Some(true),
914            // no (observable) control flow (except for trapping)
915            wasmparser::Operator::RefAsNonNull => Some(false),
916            // we need to save before diverging control flow
917            wasmparser::Operator::BrOnNull { .. } | wasmparser::Operator::BrOnNonNull { .. } => {
918                Some(true)
919            }
920            // === Stack switching ===
921            // creating a continuation does not change the control flow (yet)
922            wasmparser::Operator::ContNew { .. } | wasmparser::Operator::ContBind { .. } => {
923                Some(false)
924            }
925            // we need to save before diverging control flow by suspending or
926            //  resuming a continuation diverg
927            wasmparser::Operator::Suspend { .. }
928            | wasmparser::Operator::Resume { .. }
929            | wasmparser::Operator::ResumeThrow { .. } => Some(true),
930            // we need to save before diverging control flow by switching to a
931            //  different continuation
932            wasmparser::Operator::Switch { .. } => Some(true),
933            // === Wide Arithmetic ===
934            // no control flow
935            wasmparser::Operator::I64Add128
936            | wasmparser::Operator::I64Sub128
937            | wasmparser::Operator::I64MulWideS
938            | wasmparser::Operator::I64MulWideU => Some(false),
939            // === FIXME ===
940            #[cfg(not(test))]
941            #[expect(clippy::panic)]
942            _ => panic!("unsupported instruction"),
943            #[cfg(test)]
944            #[expect(unsafe_code)]
945            _ => {
946                unsafe extern "C" {
947                    fn instruction_counter_unhandled_operator() -> !;
948                }
949                unsafe { instruction_counter_unhandled_operator() }
950            }
951        }
952    }
953}
954
955pub struct PerfWitInterfaces {
956    pub perf: InterfaceIdentifier,
957    pub instruction_counter: String,
958}
959
960impl PerfWitInterfaces {
961    #[must_use]
962    pub fn get() -> &'static Self {
963        static PERF_WIT_INTERFACES: OnceLock<PerfWitInterfaces> = OnceLock::new();
964
965        PERF_WIT_INTERFACES.get_or_init(|| Self {
966            perf: InterfaceIdentifier::new(
967                PackageIdentifier::new(
968                    PackageName::new("numcodecs", "wasm"),
969                    Some(Version::new(0, 1, 0)),
970                ),
971                "perf",
972            ),
973            instruction_counter: String::from("instruction-counter"),
974        })
975    }
976}