numcodecs_wasm_host_reproducible/transform/
instcnt.rs

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