Coverage Report

Created: 2026-09-14 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wasm-mutate/src/mutators/peephole.rs
Line
Count
Source
1
//! This mutator applies a random peephole transformation to the input Wasm module.
2
//!
3
//! It builds a minimal DFG (Data Flow Graph) from a random operator selected
4
//! from a random function inside the input Wasm. If this DFG is consistent and
5
//! has no side-effects, and egraph is constructed with
6
//! several hand-made rewriting rules. Random rewriting rules are selected and
7
//! the DFG is replaced by a new one. The final step assembles all together with the
8
//! new DFG, constructing a new equivalent Wasm binary.
9
//!
10
//!
11
//! To contribute with this specific mutator you can augment the defined
12
//! [rules][rules]. Those rewriting rules should be designed to
13
//! preserve the semantic of the original DFG or, in other case, should follow the filter
14
//! of the top config `preserve_semantics`.
15
//!
16
//! # Example
17
//!
18
//! ```ignore
19
//! rules.extend(rewrite!("strength-reduction";  "(i32.shl ?x i32.const.1)" <=> "(i32.mul ?x i32.const.2)"));
20
//! ```
21
//!
22
23
pub mod dfg;
24
pub mod eggsy;
25
pub mod rules;
26
27
use self::{
28
    dfg::DFGBuilder,
29
    eggsy::{
30
        analysis::PeepholeMutationAnalysis,
31
        encoder::{Encoder, expr2wasm::ResourceRequest},
32
        expr_enumerator::lazy_expand_aux,
33
        lang::*,
34
    },
35
};
36
use super::{Mutator, OperatorAndByteOffset};
37
use crate::{
38
    Error, ErrorKind, ModuleInfo, Result, WasmMutate,
39
    module::{PrimitiveTypeInfo, map_type},
40
};
41
use egg::{Rewrite, Runner};
42
use rand::RngExt;
43
use wasm_encoder::reencode::{Reencode, RoundtripReencoder};
44
use wasm_encoder::{CodeSection, ConstExpr, Function, GlobalSection, Module, ValType};
45
use wasmparser::{CodeSectionReader, FunctionBody, GlobalSectionReader, LocalsReader};
46
47
/// This mutator applies a random peephole transformation to the input Wasm module
48
#[derive(Clone)]
49
pub struct PeepholeMutator {
50
    max_tree_depth: u32,
51
    rules: Option<Vec<Rewrite<Lang, PeepholeMutationAnalysis>>>,
52
}
53
54
type EG = egg::EGraph<Lang, PeepholeMutationAnalysis>;
55
56
impl PeepholeMutator {
57
    /// Initializes a new PeepholeMutator with fuel
58
0
    pub const fn new(max_depth: u32) -> Self {
59
0
        PeepholeMutator {
60
0
            max_tree_depth: max_depth,
61
0
            rules: None,
62
0
        }
63
0
    }
64
65
    #[cfg(test)]
66
    pub fn new_with_rules(
67
        max_tree_depth: u32,
68
        rules: Vec<Rewrite<Lang, PeepholeMutationAnalysis>>,
69
    ) -> Self {
70
        PeepholeMutator {
71
            max_tree_depth,
72
            rules: Some(rules),
73
        }
74
    }
75
76
    // Collect and unfold params and locals, [x, ty, y, ty2] -> [ty....ty, ty2...ty2]
77
1.56k
    fn get_func_locals(
78
1.56k
        &self,
79
1.56k
        info: &ModuleInfo,
80
1.56k
        funcidx: u32,
81
1.56k
        localsreader: &mut LocalsReader,
82
1.56k
    ) -> Result<Vec<PrimitiveTypeInfo>> {
83
1.56k
        let ftype = info.get_functype_idx(funcidx);
84
1.56k
        match ftype {
85
1.56k
            crate::module::TypeInfo::Func(tpe) => {
86
1.56k
                let mut all_locals = Vec::new();
87
88
9.19k
                for primitive in &tpe.params {
89
9.19k
                    all_locals.push(*primitive)
90
                }
91
1.56k
                for _ in 0..localsreader.get_count() {
92
6.54k
                    let (count, ty) = localsreader.read()?;
93
6.54k
                    let tymapped = PrimitiveTypeInfo::try_from(ty)?;
94
6.54k
                    for _ in 0..count {
95
6.54k
                        all_locals.push(tymapped);
96
6.54k
                    }
97
                }
98
99
1.56k
                Ok(all_locals)
100
            }
101
        }
102
1.56k
    }
103
104
660
    fn random_mutate<'a>(
105
660
        &self,
106
660
        config: &'a mut WasmMutate,
107
660
        rules: &[Rewrite<Lang, PeepholeMutationAnalysis>],
108
660
    ) -> Result<Box<dyn Iterator<Item = Result<Module>> + 'a>> {
109
660
        let code_section = config.info().code.unwrap();
110
660
        let reader = config.info().get_binary_reader(code_section);
111
660
        let input_code_section = config.info().get_code_section().data;
112
660
        let sectionreader = CodeSectionReader::new(reader)?;
113
660
        let function_count = sectionreader.count();
114
660
        let mut function_to_mutate = config.rng().random_range(0..function_count);
115
116
660
        let mut visited_functions = 0;
117
118
660
        let readers = sectionreader.into_iter().collect::<Result<Vec<_>, _>>()?;
119
120
        loop {
121
1.59k
            if visited_functions == function_count {
122
23
                return Err(Error::no_mutations_applicable());
123
1.56k
            }
124
125
1.56k
            let reader = readers[function_to_mutate as usize].clone();
126
1.56k
            let operatorreader = reader.get_operators_reader()?;
127
1.56k
            let mut localsreader = reader.get_locals_reader()?;
128
1.56k
            let operators = operatorreader
129
1.56k
                .into_iter_with_offsets()
130
1.56k
                .collect::<wasmparser::Result<Vec<OperatorAndByteOffset>>>()?;
131
1.56k
            let operatorscount = operators.len();
132
133
1.56k
            let mut opcode_to_mutate = config.rng().random_range(0..operatorscount);
134
1.56k
            log::trace!(
135
                "Selecting operator {opcode_to_mutate}/{operatorscount} from function {function_to_mutate}",
136
            );
137
1.56k
            let locals = self.get_func_locals(
138
1.56k
                config.info(),
139
1.56k
                function_to_mutate + config.info().num_imported_functions(), /* the function type is shifted
140
                                                                            by the imported functions*/
141
1.56k
                &mut localsreader,
142
0
            )?;
143
1.56k
            let mut count = 0;
144
            loop {
145
5.99k
                config.consume_fuel(1)?;
146
5.99k
                if count == operatorscount {
147
932
                    break;
148
5.06k
                }
149
5.06k
                let mut dfg = DFGBuilder::new(config);
150
5.06k
                let basicblock = dfg.get_bb_from_operator(opcode_to_mutate, &operators);
151
152
5.06k
                let basicblock = match basicblock {
153
                    None => {
154
2.09k
                        log::trace!(
155
                            "Basic block cannot be constructed for opcode {:?}",
156
0
                            &operators[opcode_to_mutate]
157
                        );
158
2.09k
                        opcode_to_mutate = (opcode_to_mutate + 1) % operatorscount;
159
2.09k
                        count += 1;
160
2.09k
                        continue;
161
                    }
162
2.97k
                    Some(basicblock) => basicblock,
163
                };
164
2.97k
                let minidfg = dfg.get_dfg(config.info(), &operators, &basicblock);
165
166
2.97k
                let minidfg = match minidfg {
167
                    None => {
168
1.87k
                        log::trace!("DFG cannot be constructed for opcode {opcode_to_mutate}");
169
170
1.87k
                        opcode_to_mutate = (opcode_to_mutate + 1) % operatorscount;
171
1.87k
                        count += 1;
172
1.87k
                        continue;
173
                    }
174
1.09k
                    Some(minidfg) => minidfg,
175
                };
176
177
1.09k
                if !minidfg.map.contains_key(&opcode_to_mutate) {
178
0
                    opcode_to_mutate = (opcode_to_mutate + 1) % operatorscount;
179
0
                    count += 1;
180
0
                    continue;
181
1.09k
                }
182
183
                // Create an eterm expression from the basic block starting at oidx
184
1.09k
                let start = minidfg.get_expr(opcode_to_mutate);
185
186
1.09k
                if !minidfg.is_subtree_consistent_from_root() {
187
465
                    log::trace!("{start} is not consistent");
188
465
                    opcode_to_mutate = (opcode_to_mutate + 1) % operatorscount;
189
465
                    count += 1;
190
465
                    continue;
191
633
                };
192
193
633
                log::trace!(
194
                    "Trying to mutate\n\
195
                     {}\n\
196
                     at opcode {opcode_to_mutate} in function {function_to_mutate}",
197
0
                    start.pretty(30).trim(),
198
                );
199
200
633
                let analysis = PeepholeMutationAnalysis::new(config.info(), locals.clone());
201
633
                let runner = Runner::<Lang, PeepholeMutationAnalysis, ()>::new(analysis)
202
633
                    .with_iter_limit(1) // FIXME, the iterations should consume fuel from the actual mutator. Be careful with inner set time limits that can lead us to non-deterministic behavior
203
633
                    .with_expr(&start)
204
633
                    .run(rules);
205
633
                let mut egraph = runner.egraph;
206
                // In theory this will return the Id of the operator eterm
207
633
                let root = egraph.add_expr(&start);
208
633
                let startcmp = start.clone();
209
210
                // If the number of nodes in the egraph is not large, then
211
                // continue the search
212
633
                if egraph.total_number_of_nodes() <= 1 {
213
0
                    opcode_to_mutate = (opcode_to_mutate + 1) % operatorscount;
214
0
                    count += 1;
215
0
                    continue;
216
633
                };
217
218
633
                log::trace!(
219
                    "Egraph built, nodes count = {}",
220
0
                    egraph.total_number_of_nodes()
221
                );
222
223
                // At this point we spent some resource calculating basic block,
224
                // and constructing the egraph
225
633
                config.consume_fuel(1)?;
226
227
                // If reduction mode is requested then yield back the smallest
228
                // graph to start off with. For reduction cases that are
229
                // specifically trying to find an interesting test case though
230
                // the first reduction may not be interesting, so continue to
231
                // chain up the lazy expansions afterwards like we always do.
232
633
                let iter = if config.reduce {
233
0
                    let mut extractor = egg::Extractor::new(&egraph, egg::AstSize);
234
0
                    let (_best_cost, best_expr) = extractor.find_best(root);
235
0
                    Some(best_expr).into_iter()
236
                } else {
237
633
                    None.into_iter()
238
                };
239
633
                let iter = iter.chain(lazy_expand_aux(
240
633
                    root,
241
633
                    egraph.clone(),
242
633
                    self.max_tree_depth,
243
633
                    config.rng().random(),
244
                ));
245
246
                // Filter expression equal to the original one
247
633
                let iterator = iter
248
6.39k
                    .filter(move |expr| !expr.to_string().eq(&startcmp.to_string()))
249
6.33k
                    .map(move |expr| {
250
6.33k
                        log::trace!("Yielding expression:\n{}", expr.pretty(60));
251
252
6.33k
                        config.consume_fuel(1)?;
253
254
6.33k
                        let mut newfunc = copy_locals(reader.clone())?;
255
6.33k
                        let needed_resources = Encoder::build_function(
256
6.33k
                            config,
257
6.33k
                            opcode_to_mutate,
258
6.33k
                            &expr,
259
6.33k
                            &operators,
260
6.33k
                            &basicblock,
261
6.33k
                            &mut newfunc,
262
6.33k
                            &minidfg,
263
6.33k
                            &egraph,
264
6.33k
                            input_code_section,
265
0
                        )?;
266
267
6.33k
                        let mut codes = CodeSection::new();
268
6.33k
                        let code_section = config.info().code.unwrap();
269
6.33k
                        let reader = config.info().get_binary_reader(code_section);
270
6.33k
                        let sectionreader = CodeSectionReader::new(reader)?;
271
272
                        // this mutator is applicable to internal functions, so
273
                        // it starts by randomly selecting an index between
274
                        // the imported functions and the total count, total=imported + internal
275
208k
                        for (fidx, func) in sectionreader.into_iter().enumerate() {
276
208k
                            let reader = func?;
277
208k
                            if fidx as u32 == function_to_mutate {
278
6.33k
                                codes.function(&newfunc);
279
202k
                            } else {
280
202k
                                codes.raw(reader.as_bytes());
281
202k
                            }
282
                        }
283
284
                        // Process the outside function needed resources
285
                        // Needed globals
286
6.33k
                        let mut new_global_section = GlobalSection::new();
287
                        // Reparse and reencode global section
288
6.33k
                        if let Some(global_section) = config.info().globals {
289
                            // If the global section was already there, try to copy it to the
290
                            // new raw section
291
5.14k
                            let reader = config.info().get_binary_reader(global_section);
292
5.14k
                            let globalreader = GlobalSectionReader::new(reader)?;
293
5.14k
                            RoundtripReencoder
294
5.14k
                                .parse_global_section(&mut new_global_section, globalreader)?;
295
1.19k
                        }
296
297
6.33k
                        if needed_resources.len() > 0 {
298
633
                            log::trace!("Adding {} additional resources", needed_resources.len());
299
5.69k
                        }
300
301
6.33k
                        for resource in &needed_resources {
302
669
                            match resource {
303
                                ResourceRequest::Global {
304
669
                                    tpe: ty,
305
669
                                    mutable,
306
669
                                    shared,
307
                                } => {
308
669
                                    let (init, ty) = match ty {
309
                                        PrimitiveTypeInfo::I32 => {
310
235
                                            (ConstExpr::i32_const(0), ValType::I32)
311
                                        }
312
                                        PrimitiveTypeInfo::I64 => {
313
102
                                            (ConstExpr::i64_const(0), ValType::I64)
314
                                        }
315
                                        PrimitiveTypeInfo::F32 => {
316
141
                                            (ConstExpr::f32_const(0.0.into()), ValType::F32)
317
                                        }
318
                                        PrimitiveTypeInfo::F64 => {
319
191
                                            (ConstExpr::f64_const(0.0.into()), ValType::F64)
320
                                        }
321
                                        PrimitiveTypeInfo::V128 => {
322
0
                                            (ConstExpr::v128_const(0), ValType::V128)
323
                                        }
324
0
                                        _ => unreachable!("Not valid for globals"),
325
                                    };
326
669
                                    let ty = wasm_encoder::GlobalType {
327
669
                                        val_type: ty,
328
669
                                        mutable: *mutable,
329
669
                                        shared: *shared,
330
669
                                    };
331
                                    // Add to globals
332
669
                                    new_global_section.global(ty, &init);
333
                                }
334
                            }
335
                        }
336
337
6.33k
                        let code_index = config.info().code;
338
6.33k
                        let global_index = config.info().globals;
339
340
                        // This conditional placing enforces to write the global
341
                        // section by respecting its relative order in the Wasm module
342
6.33k
                        let insert_globals_before = config
343
6.33k
                            .info()
344
6.33k
                            .globals
345
6.33k
                            .or(config.info().exports)
346
6.33k
                            .or(config.info().start)
347
6.33k
                            .or(config.info().elements)
348
6.33k
                            .or(config.info().data_count)
349
6.33k
                            .or(code_index);
350
351
                        // If the mutator is in this staeg, then it passes the can_mutate filter,
352
                        // which checks for code section existence
353
6.33k
                        let insert_globals_before = insert_globals_before.unwrap();
354
6.33k
                        let module = config.info().replace_multiple_sections(
355
43.2k
                            move |index, _sectionid, module: &mut wasm_encoder::Module| {
356
43.2k
                                if insert_globals_before == index
357
                            // Write if needed or if it wasm in the init Wasm
358
6.33k
                            && (new_global_section.len() > 0 || global_index.is_some() )
359
5.28k
                                {
360
5.28k
                                    // Insert the new globals here
361
5.28k
                                    module.section(&new_global_section);
362
37.9k
                                }
363
43.2k
                                if index == code_index.unwrap() {
364
                                    // Replace code section
365
6.33k
                                    module.section(&codes);
366
367
6.33k
                                    return true;
368
36.9k
                                }
369
36.9k
                                if let Some(gidx) = global_index {
370
                                    // return true since the global section is written by the
371
                                    // conditional position writer
372
32.2k
                                    return gidx == index;
373
4.75k
                                }
374
                                // False to say the underlying encoder to write the preexisting
375
                                // section
376
4.75k
                                false
377
43.2k
                            },
378
                        );
379
380
6.33k
                        Ok(module)
381
6.33k
                    })
382
633
                    .map_while(|module: Result<Module>| match module {
383
6.33k
                        Ok(module) => Some(Ok(module)),
384
0
                        Err(e) if matches!(e.kind(), ErrorKind::OutOfFuel) => None,
385
0
                        Err(e) => Some(Err(e)),
386
6.33k
                    });
387
388
633
                return Ok(Box::new(iterator));
389
            }
390
932
            function_to_mutate = (function_to_mutate + 1) % function_count;
391
932
            visited_functions += 1;
392
        }
393
394
6.33k
        fn copy_locals(reader: FunctionBody) -> Result<Function> {
395
            // Create the new function
396
6.33k
            let mut localreader = reader.get_locals_reader()?;
397
            // Get current locals and map to encoder types
398
6.33k
            let mut local_count = 0;
399
6.33k
            let current_locals = (0..localreader.get_count())
400
56.1k
                .map(|_| {
401
56.1k
                    let (count, ty) = localreader.read().unwrap();
402
56.1k
                    local_count += count;
403
56.1k
                    (count, map_type(ty).unwrap())
404
56.1k
                })
405
6.33k
                .collect::<Vec<(u32, ValType)>>();
406
407
6.33k
            Ok(Function::new(current_locals /*copy locals here*/))
408
6.33k
        }
409
660
    }
410
411
    /// To separate the methods will allow us to test rule by rule
412
660
    fn mutate_with_rules<'a>(
413
660
        &self,
414
660
        config: &'a mut WasmMutate,
415
660
        rules: &[Rewrite<Lang, PeepholeMutationAnalysis>],
416
660
    ) -> Result<Box<dyn Iterator<Item = Result<Module>> + 'a>> {
417
660
        self.random_mutate(config, rules)
418
660
    }
419
}
420
421
/// Meta mutator for peephole
422
impl Mutator for PeepholeMutator {
423
660
    fn mutate<'a>(
424
660
        &self,
425
660
        config: &'a mut crate::WasmMutate,
426
660
    ) -> Result<Box<dyn Iterator<Item = Result<Module>> + 'a>> {
427
660
        let rules = match self.rules.clone() {
428
0
            Some(rules) => rules,
429
            // Calculate here type related information for parameters, locals and returns
430
            // This information could be passed to the conditions to check for type correctness rewriting
431
            // Write the new rules in the rules.rs file
432
660
            None => self.get_rules(config),
433
        };
434
660
        let modules = self.mutate_with_rules(config, &rules)?;
435
436
633
        Ok(modules)
437
660
    }
438
439
792
    fn can_mutate<'a>(&self, config: &'a WasmMutate) -> bool {
440
792
        config.info().has_code() && config.info().num_local_functions() > 0
441
792
    }
442
}
443
444
/// This macro is meant to be used for testing deep mutators
445
/// It receives the original wat text variable, the expression returning the mutated function and the expected wat
446
/// For an example, look at SwapCommutativeOperator
447
#[cfg(test)]
448
#[macro_export]
449
macro_rules! match_code_mutation {
450
    ($wat: ident, $mutation:expr, $expected:ident) => {{
451
        let original = &wat::parse_str($wat).unwrap();
452
453
        let mut parser = Parser::new(0);
454
        let config = WasmMutate::default();
455
456
        let mut offset = 0;
457
458
        let mut modu = Module::new();
459
        let mut codesection = CodeSection::new();
460
461
        loop {
462
            let (payload, chunksize) = match parser.parse(&original[offset..], true).unwrap() {
463
                Chunk::NeedMoreData(_) => {
464
                    panic!("This should not be reached");
465
                }
466
                Chunk::Parsed { consumed, payload } => (payload, consumed),
467
            };
468
            offset += chunksize;
469
470
            match payload {
471
                Payload::TypeSection(reader) => {
472
                    modu.section(&RawSection {
473
                        id: SectionId::Type.into(),
474
                        data: &original[reader.range().start..reader.range().end],
475
                    });
476
                }
477
                Payload::FunctionSection(reader) => {
478
                    modu.section(&RawSection {
479
                        id: SectionId::Function.into(),
480
                        data: &original[reader.range().start..reader.range().end],
481
                    });
482
                }
483
                Payload::ExportSection(reader) => {
484
                    modu.section(&RawSection {
485
                        id: SectionId::Export.into(),
486
                        data: &original[reader.range().start..reader.range().end],
487
                    });
488
                }
489
                Payload::CodeSectionEntry(reader) => {
490
                    let operatorsreader = reader.get_operators_reader().unwrap();
491
                    let range = operatorsreader.get_binary_reader().range();
492
                    let operators = operatorsreader
493
                        .into_iter_with_offsets()
494
                        .collect::<wasmparser::Result<Vec<OperatorAndByteOffset>>>()
495
                        .unwrap();
496
                    let mutated = $mutation(&config, operators, reader, range, original);
497
                    codesection.function(&mutated);
498
                }
499
                wasmparser::Payload::End => break,
500
                _ => {
501
                    // do nothing
502
                }
503
            }
504
        }
505
        modu.section(&codesection);
506
        let mutated = modu.finish();
507
        crate::validate(&mutated);
508
509
        let text = wasmprinter::print_bytes(mutated).unwrap();
510
511
        // parse expected to use the same formatter
512
        let expected_bytes = &wat::parse_str($expected).unwrap();
513
        let expectedtext = wasmprinter::print_bytes(expected_bytes).unwrap();
514
        assert_eq!(text, expectedtext);
515
    }};
516
}
517
518
#[cfg(test)]
519
mod tests {
520
    use crate::{
521
        WasmMutate,
522
        info::ModuleInfo,
523
        module::PrimitiveTypeInfo,
524
        mutators::{Mutator, peephole::PeepholeMutator},
525
    };
526
    use egg::{Id, Rewrite, Subst, rewrite};
527
    use rand::{SeedableRng, rngs::SmallRng};
528
529
    use super::{EG, PeepholeMutationAnalysis};
530
    use crate::mutators::peephole::Lang;
531
532
    /// Condition to apply the unfold operator
533
    /// check that the var is a constant
534
    #[allow(dead_code)]
535
    fn is_const(vari: &'static str) -> impl Fn(&mut EG, Id, &Subst) -> bool {
536
        move |egraph: &mut EG, _, subst| {
537
            let var = vari.parse();
538
539
            match var {
540
                Ok(var) => {
541
                    let eclass = &egraph[subst[var]];
542
                    if eclass.nodes.len() == 1 {
543
                        let node = &eclass.nodes[0];
544
                        match node {
545
                            Lang::I32(_) => true,
546
                            Lang::I64(_) => true,
547
                            _ => false,
548
                        }
549
                    } else {
550
                        false
551
                    }
552
                }
553
                Err(_) => false,
554
            }
555
        }
556
    }
557
558
    fn is_type(vari: &'static str, t: PrimitiveTypeInfo) -> impl Fn(&mut EG, Id, &Subst) -> bool {
559
        move |egraph: &mut EG, _, subst| {
560
            let var = vari.parse();
561
            match var {
562
                Ok(var) => {
563
                    let eclass = &egraph[subst[var]];
564
                    match &eclass.data {
565
                        Some(d) => d.tpe == t,
566
                        None => false,
567
                    }
568
                }
569
                Err(_) => false,
570
            }
571
        }
572
    }
573
574
    // Random numbers vary by pointer-width presumably due to `usize` at some
575
    // point factoring in, and this test is only known to pass within a
576
    // reasonable amount of time on 64-bit platforms.
577
    #[test]
578
    #[cfg(target_pointer_width = "64")]
579
    fn test_peep_unfold2() {
580
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
581
            rewrite!("unfold-2";  "?x" => "(i32.unfold ?x)" if is_const("?x") if is_type("?x", PrimitiveTypeInfo::I32)),
582
        ];
583
584
        test_peephole_mutator(
585
            r#"
586
            (module
587
                (func (export "exported_func") (result i32) (local i32 i32)
588
                    i32.const 56
589
                )
590
            )
591
            "#,
592
            rules,
593
            r#"
594
            (module
595
                (type (;0;) (func (result i32)))
596
                (func (;0;) (type 0) (result i32)
597
                  (local i32 i32)
598
                  i32.const 1731343737
599
                  i32.const -1731343681
600
                  i32.add)
601
                (export "exported_func" (func 0)))
602
            "#,
603
            0,
604
        );
605
    }
606
607
    #[test]
608
    fn test_peep_stack_neutral2() {
609
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
610
            rewrite!("strength-undo";  "?x" => "(i32.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32)),
611
        ];
612
613
        test_peephole_mutator(
614
            r#"
615
        (module
616
            (func (export "exported_func")  (local i32 i32)
617
                i32.const 10
618
                drop
619
            )
620
        )
621
        "#,
622
            rules,
623
            r#"
624
            (module
625
                (type (;0;) (func ))
626
                (func (;0;) (type 0)
627
                    (local i32 i32)
628
                    i32.const 10
629
                    i32.const 10
630
                    i32.or
631
                    drop
632
                )
633
                (export "exported_func" (func 0)))
634
            "#,
635
            4,
636
        );
637
    }
638
639
    #[test]
640
    fn test_peep_select() {
641
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
642
            &[rewrite!("rule";  "(select ?x ?y ?z)" => "(select ?y ?x (i32.eqz ?z))")];
643
644
        test_peephole_mutator(
645
            r#"
646
        (module
647
            (func (export "exported_func") (result f32)  (local i32 i32)
648
                f32.const 10
649
                f32.const 20
650
                f32.add
651
                f32.const 200
652
                local.get 0
653
                select
654
            )
655
        )
656
        "#,
657
            rules,
658
            r#"
659
            (module
660
                (type (;0;) (func (result f32)))
661
                (func (;0;) (type 0) (result f32)
662
                  (local i32 i32)
663
                  f32.const 0x1.9p+7 (;=200;)
664
                  f32.const 0x1.4p+3 (;=10;)
665
                  f32.const 0x1.4p+4 (;=20;)
666
                  f32.add
667
                  local.get 0
668
                  i32.eqz
669
                  select)
670
                (export "exported_func" (func 0)))
671
            "#,
672
            4,
673
        );
674
    }
675
676
    #[test]
677
    fn test_peep_wrap() {
678
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
679
            rewrite!("strength-undo";  "?x" => "(i32.add ?x i32.const.0)" if is_type("?x", PrimitiveTypeInfo::I32)),
680
        ];
681
682
        test_peephole_mutator(
683
            r#"
684
        (module
685
            (func (export "exported_func") (result i32) (local i64)
686
                local.get 0
687
                i64.const 0
688
                i64.shl
689
                i32.wrap_i64
690
                i32.const -441701230
691
                i32.const 441701230
692
                i32.add
693
                i32.add
694
            )
695
        )
696
        "#,
697
            rules,
698
            r#"
699
        (module
700
            (func (export "exported_func") (result i32) (local i64)
701
                local.get 0
702
                i64.const 0
703
                i64.shl
704
                i32.wrap_i64
705
                i32.const -441701230
706
                i32.const 0
707
                i32.add
708
                i32.const 441701230
709
                i32.add
710
                i32.add
711
            )
712
        )
713
        "#,
714
            0,
715
        );
716
    }
717
718
    #[test]
719
    fn test_peep_irelop1() {
720
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
721
            &[rewrite!("strength-undo";  "(i64.eqz ?x)" => "(i64.eq ?x i64.const.0)")];
722
723
        test_peephole_mutator(
724
            r#"
725
        (module
726
            (func (export "exported_func") (result i32) (local i32 i32)
727
                i64.const 10
728
                i64.eqz
729
            )
730
        )
731
        "#,
732
            rules,
733
            r#"
734
            (module
735
                (type (;0;) (func (result i32) ))
736
                (func (;0;) (type 0)
737
                    (local i32 i32)
738
                    i64.const 10
739
                    i64.const 0
740
                    i64.eq
741
                )
742
                (export "exported_func" (func 0)))
743
            "#,
744
            2,
745
        );
746
    }
747
748
    #[test]
749
    fn test_peep_bug1() {
750
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
751
            rewrite!("strength-undo";  "?x" => "(i32.shl ?x i32.const.0)" if is_type("?x", PrimitiveTypeInfo::I32)),
752
        ];
753
754
        test_peephole_mutator(
755
            r#"
756
            (module
757
                (type (;0;) (func (result i32)))
758
                (func (;0;) (type 0) (result i32)
759
                  i32.const -14671840
760
                  i64.extend_i32_u
761
                  i32.const -1
762
                  i64.extend_i32_u
763
                  i64.rem_s
764
                  i64.const -1
765
                  i64.le_u)
766
                (data (;0;) ""))
767
        "#,
768
            rules,
769
            r#"
770
            (module
771
                (type (;0;) (func (result i32)))
772
                (func (;0;) (type 0) (result i32)
773
                  i32.const -14671840
774
                  i64.extend_i32_u
775
                  i32.const -1
776
                  i64.extend_i32_u
777
                  i64.rem_s
778
                  i64.const -1
779
                  i64.le_u
780
                  i32.const 0
781
                  i32.shl)
782
                (data (;0;) ""))
783
            "#,
784
            11494877297919394048,
785
        );
786
    }
787
788
    #[test]
789
    fn test_peep_commutative() {
790
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
791
            &[rewrite!("commutative-1";  "(i32.add ?x ?y)" => "(i32.add ?y ?x)")];
792
793
        test_peephole_mutator(
794
            r#"
795
        (module
796
            (func (export "exported_func") (result i32) (local i32 i32)
797
                i32.const 42
798
                i32.const 1
799
                i32.add
800
            )
801
        )
802
        "#,
803
            rules,
804
            r#"
805
            (module
806
                (type (;0;) (func (result i32)))
807
                (func (;0;) (type 0) (result i32)
808
                  (local i32 i32)
809
                  i32.const 1
810
                  i32.const 42
811
                  i32.add
812
                )
813
                (export "exported_func" (func 0)))
814
            "#,
815
            6,
816
        );
817
    }
818
819
    #[test]
820
    fn test_peep_inversion() {
821
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
822
            &[rewrite!("inversion-1";  "(i32.gt_s ?x ?y)" => "(i32.le_s ?y ?x)")];
823
824
        test_peephole_mutator(
825
            r#"
826
        (module
827
            (func (export "exported_func") (result i32) (local i32 i32)
828
                i32.const 42
829
                i32.const 1
830
                i32.gt_s
831
            )
832
        )
833
        "#,
834
            rules,
835
            r#"
836
            (module
837
                (type (;0;) (func (result i32)))
838
                (func (;0;) (type 0) (result i32)
839
                  (local i32 i32)
840
                  i32.const 1
841
                  i32.const 42
842
                  i32.le_s)
843
                (export "exported_func" (func 0)))
844
            "#,
845
            0,
846
        );
847
    }
848
849
    #[test]
850
    fn test_peep_integrtion() {
851
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
852
            &[rewrite!("inversion-1";  "(i32.gt_s ?x ?y)" => "(i32.le_s ?y ?x)")];
853
854
        test_peephole_mutator(
855
            r#"
856
        (module
857
            (func (export "exported_func") (result i32) (local i32 i32)
858
                i32.const 42
859
                i32.const 1
860
                i32.gt_s
861
            )
862
        )
863
        "#,
864
            rules,
865
            r#"
866
            (module
867
                (type (;0;) (func (result i32)))
868
                (func (;0;) (type 0) (result i32)
869
                  (local i32 i32)
870
                  i32.const 1
871
                  i32.const 42
872
                  i32.le_s)
873
                (export "exported_func" (func 0)))
874
            "#,
875
            0,
876
        );
877
    }
878
879
    #[test]
880
    fn test_peep_inversion2() {
881
        let original = r#"
882
            (module
883
                (type (;0;) (func (param i64 i32 f32)))
884
                (func (;0;) (type 0) (param i64 i32 f32)
885
                i32.const 100
886
                i32.const 200
887
                i32.store offset=600 align=1
888
                )
889
                (memory (;0;) 0)
890
                (export "\00" (memory 0)))
891
        "#;
892
        let original = &wat::parse_str(original).unwrap();
893
        let info = ModuleInfo::new(original).unwrap();
894
895
        let mut wasmmutate = WasmMutate::default();
896
        wasmmutate.fuel(3);
897
        wasmmutate.info = Some(info);
898
        let rnd = SmallRng::seed_from_u64(0);
899
        wasmmutate.rng = Some(rnd);
900
901
        let mutator = PeepholeMutator::new(2);
902
903
        let can_mutate = mutator.can_mutate(&wasmmutate);
904
905
        assert_eq!(can_mutate, true);
906
        let rules = mutator.get_rules(&wasmmutate);
907
908
        for mutated in mutator.mutate_with_rules(&mut wasmmutate, &rules).unwrap() {
909
            let module = mutated.unwrap();
910
911
            let mutated_bytes = &module.finish();
912
            let _text = wasmprinter::print_bytes(mutated_bytes).unwrap();
913
            crate::validate(mutated_bytes);
914
        }
915
    }
916
917
    #[test]
918
    fn test_mem_store1() {
919
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
920
            rewrite!("rule";  "(i32.store.600.0.0 ?value ?offset)" => "(i32.store.0.0.0 ?value (i32.add ?offset i32.const.600))" ),
921
        ];
922
923
        test_peephole_mutator(
924
            r#"
925
            (module
926
                (type (;0;) (func (param i64 i32 f32)))
927
                (func (;0;) (type 0) (param i64 i32 f32)
928
                  i32.const 100
929
                  i32.const 200
930
                  i32.store offset=600 align=1
931
                )
932
                (memory (;0;) 0)
933
                (export "\00" (memory 0)))
934
        "#,
935
            rules,
936
            r#"
937
            (module
938
                (type (;0;) (func (param i64 i32 f32)))
939
                (func (;0;) (type 0) (param i64 i32 f32)
940
                  i32.const 100
941
                  i32.const 200
942
                  i32.const 600
943
                  i32.add
944
                  i32.store align=1)
945
                (memory (;0;) 0)
946
                (export "\00" (memory 0)))
947
            "#,
948
            0,
949
        );
950
    }
951
952
    #[test]
953
    fn test_peep_shl0() {
954
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
955
            rewrite!("strength-undo3";  "(i64.shr_u ?x ?y)" => "(i64.shl (i64.shr_u ?x ?y) i64.const.0)" ),
956
        ];
957
958
        test_peephole_mutator(
959
            r#"
960
            (module
961
                (type (;0;) (func (param i64 i32 f32)))
962
                (func (;0;) (type 0) (param i64 i32 f32)
963
                  i64.const 89
964
                  local.get 1
965
                  i64.load align=2
966
                  local.get 1
967
                  i64.load align=1
968
                  i64.shr_u
969
                  drop
970
                  drop
971
                )
972
                (memory (;0;) 0)
973
                (export "\00" (memory 0)))
974
        "#,
975
            rules,
976
            r#"
977
            (module
978
                (type (;0;) (func (param i64 i32 f32)))
979
                (func (;0;) (type 0) (param i64 i32 f32)
980
                  i64.const 89
981
                  local.get 1
982
                  i64.load align=2
983
                  local.get 1
984
                  i64.load align=1
985
                  i64.shr_u
986
                  i64.const 0
987
                  i64.shl
988
                  drop
989
                  drop)
990
                (memory (;0;) 0)
991
                (export "\00" (memory 0)))
992
            "#,
993
            5,
994
        );
995
    }
996
997
    #[test]
998
    fn test_peep_idem1() {
999
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1000
            rewrite!("idempotent-1";  "?x" => "(i32.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32)),
1001
            rewrite!("idempotent-12";  "?x" => "(i64.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I64)),
1002
        ];
1003
1004
        test_peephole_mutator(
1005
            r#"
1006
        (module
1007
            (func (export "exported_func") (result i32) (local i32 i32)
1008
                i32.const 56
1009
            )
1010
        )
1011
        "#,
1012
            rules,
1013
            r#"
1014
        (module
1015
            (type (;0;) (func (result i32)))
1016
            (func (;0;) (type 0) (result i32)
1017
                (local i32 i32)
1018
                i32.const 56
1019
                i32.const 56
1020
                i32.or)
1021
            (export "exported_func" (func 0)))
1022
        "#,
1023
            0,
1024
        );
1025
    }
1026
1027
    #[test]
1028
    fn test_peep_cv() {
1029
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1030
            rewrite!("idempotent-1";  "?x" => "(i32.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32)),
1031
        ];
1032
1033
        test_peephole_mutator(
1034
            r#"
1035
        (module
1036
            (func (export "exported_func") (result i32) (local i32 i32)
1037
                i64.const 56
1038
                i64.const 2
1039
                i64.mul
1040
                i32.wrap_i64
1041
            )
1042
        )
1043
        "#,
1044
            rules,
1045
            r#"
1046
        (module
1047
            (type (;0;) (func (result i32)))
1048
            (func (;0;) (type 0) (result i32)
1049
                (local i32 i32)
1050
                i64.const 56
1051
                i64.const 2
1052
                i64.mul
1053
                i32.wrap_i64
1054
                i64.const 56
1055
                i64.const 2
1056
                i64.mul
1057
                i32.wrap_i64
1058
                i32.or)
1059
            (export "exported_func" (func 0)))
1060
        "#,
1061
            4,
1062
        );
1063
    }
1064
1065
    #[test]
1066
    fn test_peep_cv4() {
1067
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1068
            rewrite!("idempotent-1";  "?x" => "(i32.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32)),
1069
        ];
1070
1071
        test_peephole_mutator(
1072
            r#"
1073
        (module
1074
            (func (export "exported_func") (result i32) (local i32 i32)
1075
                i32.const 56
1076
                i32.extend8_s
1077
            )
1078
        )
1079
        "#,
1080
            rules,
1081
            r#"
1082
        (module
1083
            (func (;0;) (result i32)
1084
                (local i32 i32)
1085
                i32.const 56
1086
                i32.extend8_s
1087
                i32.const 56
1088
                i32.extend8_s
1089
                i32.or)
1090
            (export "exported_func" (func 0)))
1091
        "#,
1092
            8,
1093
        );
1094
    }
1095
1096
    #[test]
1097
    fn test_peep_cv5() {
1098
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1099
            &[rewrite!("cv4";  "?x" => "(i32.and ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32))];
1100
1101
        test_peephole_mutator(
1102
            r#"
1103
                (module
1104
                    (type (;0;) (func (result i32)))
1105
                    (func (;0;) (type 0) (result i32)
1106
                    i32.const -1
1107
                    i64.extend_i32_u
1108
                    i64.const -1
1109
                    i64.ge_s)
1110
                    (data (;0;) ""))
1111
            "#,
1112
            rules,
1113
            r#"
1114
                        (module
1115
                            (type (;0;) (func (result i32)))
1116
                            (func (;0;) (type 0) (result i32)
1117
                            i32.const -1
1118
                            i64.extend_i32_u
1119
                            i64.const -1
1120
                            i64.ge_s
1121
                            i32.const -1
1122
                            i64.extend_i32_u
1123
                            i64.const -1
1124
                            i64.ge_s
1125
                            i32.and)
1126
                            (data (;0;) ""))
1127
                    "#,
1128
            10,
1129
        );
1130
    }
1131
1132
    #[test]
1133
    fn test_use_global() {
1134
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1135
            &[rewrite!("rule";  "?x" => "(i32.use_of_global ?x)")];
1136
1137
        test_peephole_mutator(
1138
            r#"
1139
        (module
1140
            (func (export "exported_func") (result i32) (local i32 i32)
1141
                i32.const 10
1142
            )
1143
        )
1144
        "#,
1145
            rules,
1146
            r#"
1147
            (module
1148
                (type (;0;) (func (result i32)))
1149
                (func (;0;) (type 0) (result i32)
1150
                  (local i32 i32)
1151
                  i32.const 10
1152
                  global.set 0
1153
                  global.get 0)
1154
                (global (;0;) (mut i32) i32.const 0)
1155
                (export "exported_func" (func 0)))
1156
            "#,
1157
            4,
1158
        );
1159
    }
1160
1161
    #[test]
1162
    fn test_peep_idem3() {
1163
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1164
            rewrite!("idempotent-3";  "?x" => "(i32.add ?x i32.const.0)" if is_type("?x", PrimitiveTypeInfo::I32)),
1165
        ];
1166
1167
        test_peephole_mutator(
1168
            r#"
1169
        (module
1170
            (func (export "exported_func") (result i32) (local i32 i32)
1171
                i32.const 56
1172
            )
1173
        )
1174
        "#,
1175
            rules,
1176
            r#"
1177
        (module
1178
            (type (;0;) (func (result i32)))
1179
            (func (;0;) (type 0) (result i32)
1180
                (local i32 i32)
1181
                i32.const 56
1182
                i32.const 0
1183
                i32.add)
1184
            (export "exported_func" (func 0)))
1185
        "#,
1186
            0,
1187
        );
1188
    }
1189
1190
    #[test]
1191
    fn test_peep_idem4() {
1192
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1193
            rewrite!("idempotent-4";  "?x" => "(i32.mul ?x i32.const.1)" if is_type("?x", PrimitiveTypeInfo::I32)),
1194
            rewrite!("idempotent-4";  "?x" => "(i64.mul ?x i32.const.1)" if is_type("?x", PrimitiveTypeInfo::I64)),
1195
        ];
1196
1197
        test_peephole_mutator(
1198
            r#"
1199
        (module
1200
            (func (export "exported_func") (result i32) (local i32 i32)
1201
                i32.const 56
1202
            )
1203
        )
1204
        "#,
1205
            rules,
1206
            r#"
1207
        (module
1208
            (type (;0;) (func (result i32)))
1209
            (func (;0;) (type 0) (result i32)
1210
                (local i32 i32)
1211
                i32.const 56
1212
                i32.const 1
1213
                i32.mul)
1214
            (export "exported_func" (func 0)))
1215
        "#,
1216
            0,
1217
        );
1218
    }
1219
1220
    #[test]
1221
    fn test_peep_typeinfo() {
1222
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1223
            rewrite!("type1-1";  "?x" => "(i32.shr_u ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32) ),
1224
        ];
1225
1226
        test_peephole_mutator(
1227
            r#"
1228
        (module
1229
            (func (export "exported_func") (result i32) (local i32 i32)
1230
                i32.const 56
1231
            )
1232
        )
1233
        "#,
1234
            rules,
1235
            r#"
1236
        (module
1237
            (type (;0;) (func (result i32)))
1238
            (func (;0;) (type 0) (result i32)
1239
                (local i32 i32)
1240
                i32.const 56
1241
                i32.const 56
1242
                i32.shr_u)
1243
            (export "exported_func" (func 0)))
1244
        "#,
1245
            0,
1246
        );
1247
    }
1248
1249
    #[test]
1250
    fn test_peep_locals1() {
1251
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1252
            &[rewrite!("type1-1";  "(i32.add ?x ?y)" => "(i32.add ?y ?x)" )];
1253
1254
        test_peephole_mutator(
1255
            r#"
1256
        (module
1257
            (func (export "exported_func") (result i32) (local i32 i32)
1258
                local.get 0
1259
                local.get 1
1260
                i32.add
1261
            )
1262
        )
1263
        "#,
1264
            rules,
1265
            r#"
1266
        (module
1267
            (type (;0;) (func (result i32)))
1268
            (func (;0;) (type 0) (result i32)
1269
                (local i32 i32)
1270
                local.get 1
1271
                local.get 0
1272
                i32.add)
1273
            (export "exported_func" (func 0)))
1274
        "#,
1275
            5,
1276
        );
1277
    }
1278
1279
    #[test]
1280
    fn test_peep_locals3() {
1281
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1282
            &[rewrite!("type1-1";  "(local.set.1 i32.const.100)" => "(local.set.1 i32.const.0)" )];
1283
1284
        test_peephole_mutator(
1285
            r#"
1286
        (module
1287
            (func (export "exported_func") (local i32 i32)
1288
                i32.const 100
1289
                local.set 1
1290
1291
            )
1292
        )
1293
        "#,
1294
            rules,
1295
            r#"
1296
        (module
1297
            (type (;0;) (func ))
1298
            (func (;0;) (type 0)
1299
                (local i32 i32)
1300
                i32.const 0
1301
                local.set 1
1302
            )
1303
            (export "exported_func" (func 0)))
1304
        "#,
1305
            1,
1306
        );
1307
    }
1308
1309
    #[test]
1310
    fn test_peep_functions() {
1311
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1312
            &[rewrite!("type1-1";  "(call.0 ?x ?y)" => "(call.0 i64.const.1 i32.const.11) " )];
1313
1314
        test_peephole_mutator(
1315
            r#"
1316
        (module
1317
            (func (export "exported_func")(param i64 i32 )  (result i64)  (local i64 i32)
1318
                local.get 0
1319
                i32.const 10
1320
                call 0
1321
            )
1322
        )
1323
        "#,
1324
            rules,
1325
            r#"
1326
            (module
1327
                (func (export "exported_func") (param i64 i32)  (result i64)  (local i64 i32)
1328
                    i64.const 1
1329
                    i32.const 11
1330
                    call 0
1331
                )
1332
            )
1333
        "#,
1334
            0,
1335
        );
1336
    }
1337
1338
    #[test]
1339
    fn test_peep_functions2() {
1340
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1341
            rewrite!("type1-1";  "?x" => "(i32.or ?x ?x)" if is_type("?x", PrimitiveTypeInfo::I32)),
1342
        ];
1343
1344
        test_peephole_mutator(
1345
            r#"
1346
            (module
1347
                (type (;0;) (func (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) (result i32)))
1348
                (type (;1;) (func (param i64) (result i32)))
1349
                (import "ttttttttttttuttttttttttut\09" "" (func (;0;) (type 1)))
1350
                (func (;1;) (type 0) (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) (result i32)
1351
                  (local i32)
1352
                  local.get 6
1353
                  local.get 6
1354
                  i64.div_s
1355
                  local.get 6
1356
                  i64.div_s
1357
                  local.get 6
1358
                  i64.div_s
1359
                  local.get 6
1360
                  i64.div_s
1361
                  local.get 6
1362
                  i64.div_s
1363
                  local.get 6
1364
                  i64.div_s
1365
                  call 0)
1366
            )
1367
            "#,
1368
            rules,
1369
            r#"
1370
            (module
1371
                (type (;0;) (func (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) (result i32)))
1372
                (type (;1;) (func (param i64) (result i32)))
1373
                (import "ttttttttttttuttttttttttut\09" "" (func (;0;) (type 1)))
1374
                (func (;1;) (type 0) (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) (result i32)
1375
                  (local i32)
1376
                  local.get 6
1377
                  local.get 6
1378
                  i64.div_s
1379
                  local.get 6
1380
                  i64.div_s
1381
                  local.get 6
1382
                  i64.div_s
1383
                  local.get 6
1384
                  i64.div_s
1385
                  local.get 6
1386
                  i64.div_s
1387
                  local.get 6
1388
                  i64.div_s
1389
                  call 0
1390
                  local.get 6
1391
                  local.get 6
1392
                  i64.div_s
1393
                  local.get 6
1394
                  i64.div_s
1395
                  local.get 6
1396
                  i64.div_s
1397
                  local.get 6
1398
                  i64.div_s
1399
                  local.get 6
1400
                  i64.div_s
1401
                  local.get 6
1402
                  i64.div_s
1403
                  call 0
1404
                  local.get 6
1405
                  local.get 6
1406
                  i64.div_s
1407
                  local.get 6
1408
                  i64.div_s
1409
                  local.get 6
1410
                  i64.div_s
1411
                  local.get 6
1412
                  i64.div_s
1413
                  local.get 6
1414
                  i64.div_s
1415
                  local.get 6
1416
                  i64.div_s
1417
                  call 0
1418
                  i32.or
1419
                  i32.or
1420
                )
1421
            )
1422
        "#,
1423
            9,
1424
        );
1425
    }
1426
1427
    #[test]
1428
    fn test_peep_locals2() {
1429
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1430
            &[rewrite!("type1-1";  "(i64.add ?x ?y)" => "(i64.add ?y ?x)" )];
1431
1432
        test_peephole_mutator(
1433
            r#"
1434
        (module
1435
            (func (export "exported_func") (result i64) (local i64 i64)
1436
                local.get 0
1437
                local.get 1
1438
                i64.add
1439
            )
1440
        )
1441
        "#,
1442
            rules,
1443
            r#"
1444
        (module
1445
            (type (;0;) (func (result i64)))
1446
            (func (;0;) (type 0) (result i64)
1447
                (local i64 i64)
1448
                local.get 1
1449
                local.get 0
1450
                i64.add)
1451
            (export "exported_func" (func 0)))
1452
        "#,
1453
            0,
1454
        );
1455
    }
1456
1457
    #[test]
1458
    fn test_peep_floats1() {
1459
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] =
1460
            &[rewrite!("rule";  "f32.const.1,0" => "f32.const.0,0" )];
1461
1462
        test_peephole_mutator(
1463
            r#"
1464
        (module
1465
            (func (export "exported_func") (result f32) (local i64 i64)
1466
                f32.const 1.0
1467
            )
1468
        )
1469
        "#,
1470
            rules,
1471
            r#"
1472
            (module
1473
                (type (;0;) (func (result f32)))
1474
                (func (;0;) (type 0) (result f32)
1475
                  (local i64 i64)
1476
                  f32.const 0x0p+0 (;=0;))
1477
                (export "exported_func" (func 0)))
1478
        "#,
1479
            0,
1480
        );
1481
    }
1482
1483
    #[test]
1484
    fn test_peep_globals1() {
1485
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1486
            rewrite!("mem-load-shift";  "?x" => "(i32.add ?x i32.const.0)" if is_type("?x", PrimitiveTypeInfo::I32)),
1487
        ];
1488
1489
        test_peephole_mutator(
1490
            r#"
1491
        (module
1492
            (memory 1)
1493
            (global $0 i32 i32.const 0)
1494
            (func (export "exported_func") (param i32) (result i32)
1495
                global.get 0
1496
            )
1497
        )
1498
        "#,
1499
            rules,
1500
            r#"
1501
            (module
1502
                (type (;0;) (func (param i32) (result i32)))
1503
                (global $0 i32 i32.const 0)
1504
                (func (;0;) (type 0) (param i32) (result i32)
1505
                  global.get $0
1506
                  i32.const 0
1507
                  i32.add)
1508
                (memory (;0;) 1)
1509
                (export "exported_func" (func 0)))
1510
        "#,
1511
            0,
1512
        );
1513
    }
1514
1515
    #[test]
1516
    fn test_peep_globals2() {
1517
        let rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>] = &[
1518
            rewrite!("rule";  "?x" => "(i32.add ?x i32.const.0)" if is_type("?x", PrimitiveTypeInfo::I32)),
1519
        ];
1520
1521
        test_peephole_mutator(
1522
            r#"
1523
        (module
1524
            (memory 1)
1525
            (global $0 (mut i32) i32.const 0)
1526
            (func (export "exported_func") (param i32) (result i32)
1527
                i32.const 10
1528
                global.set 0
1529
                i32.const 20
1530
            )
1531
        )
1532
        "#,
1533
            rules,
1534
            r#"
1535
            (module
1536
                (type (;0;) (func (param i32) (result i32)))
1537
                (func (;0;) (type 0) (param i32) (result i32)
1538
                  i32.const 10
1539
                  global.set $0
1540
                  i32.const 20
1541
                  i32.const 0
1542
                  i32.add)
1543
                (memory (;0;) 1)
1544
                (global $0 (mut i32) i32.const 0)
1545
                (export "exported_func" (func 0)))
1546
        "#,
1547
            4,
1548
        );
1549
    }
1550
1551
    #[test]
1552
    fn remove_local_set() {
1553
        test_default_peephole_mutator(
1554
            "(module (func (local i32) (local.set 0 (i32.const 0))))",
1555
            "(module (func (local i32) nop))",
1556
            4,
1557
        );
1558
    }
1559
1560
    #[test]
1561
    fn remove_local_tee() {
1562
        test_default_peephole_mutator(
1563
            "(module (func (local i32) (local.tee 0 (i32.const 0)) drop))",
1564
            "(module (func (local i32) (i32.const 0) drop))",
1565
            4,
1566
        );
1567
    }
1568
1569
    fn test_peephole_mutator(
1570
        original: &str,
1571
        rules: &[Rewrite<super::Lang, PeepholeMutationAnalysis>],
1572
        expected: &str,
1573
        seed: u64,
1574
    ) {
1575
        let mut config = WasmMutate::default();
1576
        config.fuel(10000);
1577
        config.seed(seed);
1578
1579
        let mutator = PeepholeMutator::new_with_rules(3, rules.to_vec());
1580
        config.match_mutation(original, mutator, expected);
1581
    }
1582
1583
    fn test_default_peephole_mutator(original: &str, expected: &str, seed: u64) {
1584
        let original_wasm = wat::parse_str(original).unwrap();
1585
        let mut config = WasmMutate::default();
1586
        config.fuel(10000);
1587
        config.seed(seed);
1588
        config.info = Some(ModuleInfo::new(&original_wasm).unwrap());
1589
1590
        let mut mutator = PeepholeMutator::new(3);
1591
        let rules = mutator.get_rules(&config);
1592
        mutator.rules = Some(rules);
1593
        config.match_mutation(original, mutator, expected);
1594
    }
1595
1596
    #[test]
1597
    fn i8x16_shuffle_handled() {
1598
        test_default_peephole_mutator(
1599
            "(module (func (param v128 v128)
1600
                local.get 0
1601
                local.get 1
1602
                i8x16.shuffle 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1603
                drop)
1604
            )",
1605
            "(module (func (param v128 v128)))",
1606
            4,
1607
        );
1608
    }
1609
}