Coverage Report

Created: 2026-07-05 07:39

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