Repository: bytecodealliance/wasmtime
Pull request #7621 (2023-11-30 19:42:49)

winch(fuzzing): Tighten fuzzing criteria
This commit tightens the fuzzing criteria for Winch. The previous implementation only accounted for unsupported instructions. However, unsupported types can also cause the fuzzer to crash.

Winch currently doesn't support `v128` and most of the `Ref` types.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/cranelift/codegen/src/opts/icmp.isle b/cranelift/codegen/src/opts/icmp.isle
index b0f57fdf1..b95d978c6 100644
--- a/cranelift/codegen/src/opts/icmp.isle
+++ b/cranelift/codegen/src/opts/icmp.isle
@@ -133,6 +133,28 @@
   (if-let signed (intcc_comparable cc1 cc2))
   (compose_icmp ty (u64_or (decompose_intcc cc1) (decompose_intcc cc2)) signed x y))
 
+;; Prefer comparing against zero
+;; uge(x, 1) == ne(x, 0)
+(rule (simplify (uge ty x (iconst cty (u64_from_imm64 1))))
+      (ne ty x (iconst cty (imm64 0))))
+;; ult(x, 1) == eq(x, 0)
+(rule (simplify (ult ty x (iconst cty (u64_from_imm64 1))))
+      (eq ty x (iconst cty (imm64 0))))
+;; sge(x, 1) == sgt(x, 0)
+(rule (simplify (sge ty x (iconst cty (u64_from_imm64 1))))
+      (sgt ty x (iconst cty (imm64 0))))
+;; slt(x, 1) == sle(x, 0)
+(rule (simplify (slt ty x (iconst cty (u64_from_imm64 1))))
+      (sle ty x (iconst cty (imm64 0))))
+;; sgt(x, -1) == sge(x, 0)
+(rule (simplify (sgt ty x (iconst cty c)))
+      (if-let -1 (i64_sextend_imm64 cty c))
+      (sge ty x (iconst cty (imm64 0))))
+;; sle(x, -1) == slt(x, 0)
+(rule (simplify (sle ty x (iconst cty c)))
+      (if-let -1 (i64_sextend_imm64 cty c))
+      (slt ty x (iconst cty (imm64 0))))
+
 (decl pure partial intcc_comparable (IntCC IntCC) bool)
 (rule (intcc_comparable x y)
   (if-let (u64_nonzero class) (u64_and (intcc_class x) (intcc_class y)))
diff --git a/cranelift/codegen/src/opts/selects.isle b/cranelift/codegen/src/opts/selects.isle
index e66250327..052a7b59c 100644
--- a/cranelift/codegen/src/opts/selects.isle
+++ b/cranelift/codegen/src/opts/selects.isle
@@ -4,6 +4,17 @@
 (rule (simplify (select    ty _ x x)) x)
 (rule (simplify (bitselect ty _ x x)) x)
 
+;; if icmp(x, y) { 1 } else { 0 } => uextend(icmp(x, y))
+(rule (simplify (select ty cmp@(icmp _ cc x y)
+                        (iconst _ (u64_from_imm64 1))
+                        (iconst _ (u64_from_imm64 0))))
+    (uextend_from_i8 ty cmp))
+;; if icmp(x, y) { 0 } else { 1 } => uextend(!icmp(x, y))
+(rule (simplify (select sty (icmp cty cc x y)
+                        (iconst _ (u64_from_imm64 0))
+                        (iconst _ (u64_from_imm64 1))))
+    (uextend_from_i8 sty (icmp cty (intcc_complement cc) x y)))
+
 ;; Transform select-of-icmp into {u,s}{min,max} instructions where possible.
 (rule (simplify (select ty (sgt _ x y) x y)) (smax ty x y))
 (rule (simplify (select ty (sge _ x y) x y)) (smax ty x y))
@@ -57,3 +68,9 @@
 (rule (simplify (bor (ty_vec128 ty) (band ty (bnot ty c) y) (band ty x c))) (bitselect ty c x y))
 (rule (simplify (bor (ty_vec128 ty) (band ty y (bnot ty c)) (band ty c x))) (bitselect ty c x y))
 (rule (simplify (bor (ty_vec128 ty) (band ty y (bnot ty c)) (band ty x c))) (bitselect ty c x y))
+
+;; extend from i8 to i8 is invalid CLIF, so this allows fixing that in the output
+;; rather than needing to duplicate rules for the different width categories
+(decl uextend_from_i8 (Type Value) Value)
+(rule 0 (uextend_from_i8 ty val) (uextend ty val))
+(rule 1 (uextend_from_i8 $I8 val) val)
diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs
index a884a8430..648923768 100644
--- a/fuzz/fuzz_targets/differential.rs
+++ b/fuzz/fuzz_targets/differential.rs
@@ -5,6 +5,7 @@ use libfuzzer_sys::fuzz_target;
 use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering::SeqCst;
 use std::sync::Once;
+use wasmparser::ValType;
 use wasmtime_fuzzing::generators::CompilerStrategy;
 use wasmtime_fuzzing::generators::{Config, DiffValue, DiffValueType, SingleInstModule};
 use wasmtime_fuzzing::oracles::diff_wasmtime::WasmtimeInstance;
@@ -283,12 +284,28 @@ impl RuntimeStats {
 fn winch_supports_module(module: &[u8]) -> bool {
     use wasmparser::{Operator::*, Parser, Payload};
 
+    fn is_type_supported(ty: &ValType) -> bool {
+        match ty {
+            ValType::V128 => false,
+            ValType::Ref(r) => r.is_func_ref(),
+            _ => true,
+        }
+    }
+
     let mut supported = true;
     let mut parser = Parser::new(0).parse_all(module);
 
     'main: while let Some(payload) = parser.next() {
         match payload.unwrap() {
             Payload::CodeSectionEntry(body) => {
+                let local_reader = body.get_locals_reader().unwrap();
+                for local in local_reader {
+                    let (_, ty) = local.unwrap();
+                    if !is_type_supported(&ty) {
+                        supported = false;
+                        break 'main;
+                    }
+                }
                 let op_reader = body.get_operators_reader().unwrap();
                 for op in op_reader {
                     match op.unwrap() {
@@ -419,6 +436,37 @@ fn winch_supports_module(module: &[u8]) -> bool {
                     }
                 }
             }
+            Payload::TypeSection(section) => {
+                for ty in section.into_iter_err_on_gc_types() {
+                    if let Ok(t) = ty {
+                        for p in t.params().iter().chain(t.results()) {
+                            if !is_type_supported(p) {
+                                supported = false;
+                                break 'main;
+                            }
+                        }
+                    } else {
+                        supported = false;
+                        break 'main;
+                    }
+                }
+            }
+            Payload::GlobalSection(section) => {
+                for global in section {
+                    if !is_type_supported(&global.unwrap().ty.content_type) {
+                        supported = false;
+                        break 'main;
+                    }
+                }
+            }
+            Payload::TableSection(section) => {
+                for t in section {
+                    if !t.unwrap().ty.element_type.is_func_ref() {
+                        supported = false;
+                        break 'main;
+                    }
+                }
+            }
             _ => {}
         }
     }


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/icmp-parameterized.clif b/cranelift/filetests/filetests/egraph/icmp-parameterized.clif
index 095361408..0ce76115c 100644
--- a/cranelift/filetests/filetests/egraph/icmp-parameterized.clif
+++ b/cranelift/filetests/filetests/egraph/icmp-parameterized.clif
@@ -567,9 +567,9 @@ block0(v0: i32):
 
 ; function %icmp_sle_umax(i32) -> i8 fast {
 ; block0(v0: i32):
-;     v1 = iconst.i32 0xffff_ffff
-;     v2 = icmp sle v0, v1  ; v1 = 0xffff_ffff
-;     return v2
+;     v3 = iconst.i32 0
+;     v4 = icmp slt v0, v3  ; v3 = 0
+;     return v4
 ; }
 
 function %icmp_sle_smin(i32) -> i8 {
@@ -622,9 +622,9 @@ block0(v0: i32):
 
 ; function %icmp_sgt_umax(i32) -> i8 fast {
 ; block0(v0: i32):
-;     v1 = iconst.i32 0xffff_ffff
-;     v2 = icmp sgt v0, v1  ; v1 = 0xffff_ffff
-;     return v2
+;     v3 = iconst.i32 0
+;     v4 = icmp sge v0, v3  ; v3 = 0
+;     return v4
 ; }
 
 function %icmp_sgt_smin(i32) -> i8 {
diff --git a/cranelift/filetests/filetests/egraph/icmp.clif b/cranelift/filetests/filetests/egraph/icmp.clif
index ef60cbf95..f82d4aaa3 100644
--- a/cranelift/filetests/filetests/egraph/icmp.clif
+++ b/cranelift/filetests/filetests/egraph/icmp.clif
@@ -53,3 +53,59 @@ block0:
 ;     v5 = icmp ne v2, v3  ; v3 = 0
 ;     return v5
 ; }
+
+function %sge_one(i64) -> i8 {
+block0(v0: i64):
+    v1 = iconst.i64 1
+    v2 = icmp sge v0, v1
+    return v2
+}
+
+; function %sge_one(i64) -> i8 fast {
+; block0(v0: i64):
+;     v3 = iconst.i64 0
+;     v4 = icmp sgt v0, v3  ; v3 = 0
+;     return v4
+; }
+
+function %slt_one(i16) -> i8 {
+block0(v0: i16):
+    v1 = iconst.i16 1
+    v2 = icmp slt v0, v1
+    return v2
+}
+
+; function %slt_one(i16) -> i8 fast {
+; block0(v0: i16):
+;     v3 = iconst.i16 0
+;     v4 = icmp sle v0, v3  ; v3 = 0
+;     return v4
+; }
+
+function %uge_one(i32) -> i8 {
+block0(v0: i32):
+    v1 = iconst.i32 1
+    v2 = icmp uge v0, v1
+    return v2
+}
+
+; function %uge_one(i32) -> i8 fast {
+; block0(v0: i32):
+;     v3 = iconst.i32 0
+;     v4 = icmp ne v0, v3  ; v3 = 0
+;     return v4
+; }
+
+function %ult_one(i8) -> i8 {
+block0(v0: i8):
+    v1 = iconst.i8 1
+    v2 = icmp ult v0, v1
+    return v2
+}
+
+; function %ult_one(i8) -> i8 fast {
+; block0(v0: i8):
+;     v3 = iconst.i8 0
+;     v4 = icmp eq v0, v3  ; v3 = 0
+;     return v4
+; }
diff --git a/cranelift/filetests/filetests/egraph/select.clif b/cranelift/filetests/filetests/egraph/select.clif
index 8f5339417..062909a81 100644
--- a/cranelift/filetests/filetests/egraph/select.clif
+++ b/cranelift/filetests/filetests/egraph/select.clif
@@ -141,3 +141,47 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32):
 ; check:    v4 = icmp ule v0, v1
 ; check:    v5 = select v4, v2, v3
 ; check:    return v5
+
+function %then_true_else_false(i32, i32) -> i8 {
+block0(v0: i32, v1: i32):
+    v2 = icmp ule v0, v1
+    v3 = iconst.i8 1
+    v4 = iconst.i8 0
+    v5 = select v2, v3, v4
+    return v5
+}
+; check: return v2
+
+function %then_false_else_true(i32, i32) -> i8 {
+block0(v0: i32, v1: i32):
+    v2 = icmp slt v0, v1
+    v3 = iconst.i8 0
+    v4 = iconst.i8 1
+    v5 = select v2, v3, v4
+    return v5
+}
+; check: v6 = icmp sge v0, v1
+; check: return v6
+
+function %then_one_else_zero(i32, i32) -> i64 {
+block0(v0: i32, v1: i32):
+    v2 = icmp ule v0, v1
+    v3 = iconst.i64 1
+    v4 = iconst.i64 0
+    v5 = select v2, v3, v4
+    return v5
+}
+; check: v6 = uextend.i64 v2
+; check: return v6
+
+function %then_zero_else_false(i32, i32) -> i64 {
+block0(v0: i32, v1: i32):
+    v2 = icmp slt v0, v1
+    v3 = iconst.i64 0
+    v4 = iconst.i64 1
+    v5 = select v2, v3, v4
+    return v5
+}
+; check: v6 = icmp sge v0, v1
+; check: v7 = uextend.i64 v6
+; check: return v7