diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 26cca8363d..e8e6493146 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -6,7 +6,9 @@ use arbitrary::{Arbitrary, Unstructured}; use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::{types::*, UserExternalName, UserFuncName}; use cranelift::codegen::ir::{Function, LibCall}; +use cranelift::codegen::isa::{self, Builder}; use cranelift::codegen::Context; +use cranelift::prelude::settings::SettingKind; use cranelift::prelude::*; use cranelift_arbitrary::CraneliftArbitrary; use cranelift_native::builder_with_options; @@ -22,6 +24,15 @@ pub use print::PrintableTestCase; pub type TestCaseInput = Vec; +pub enum IsaFlagGen { + /// When generating ISA flags, ensure that they are all supported by + /// the current host. + Host, + /// All flags available in cranelift are allowed to be generated. + /// We also allow generating all possible values for each enum flag. + All, +} + pub struct FuzzGen<'r, 'data> where 'data: 'r, @@ -228,4 +239,56 @@ where Ok(Flags::new(builder)) } + + /// Generate a random set of ISA flags and apply them to a Builder. + /// + /// Based on `mode` we can either allow all flags, or just the subset that is + /// supported by the current host. + /// + /// In all cases only a subset of the allowed flags is applied to the builder. + pub fn set_isa_flags(&mut self, builder: &mut Builder, mode: IsaFlagGen) -> Result<()> { + // `max_isa` is the maximal set of flags that we can use. + let max_builder = match mode { + IsaFlagGen::All => { + let mut max_builder = isa::lookup(builder.triple().clone())?; + + for flag in max_builder.iter() { + match flag.kind { + SettingKind::Bool => { + max_builder.enable(flag.name)?; + } + SettingKind::Enum => { + // Since these are enums there isn't a "max" value per se, pick one at random. + let value = self.u.choose(flag.values.unwrap())?; + max_builder.set(flag.name, value)?; + } + SettingKind::Preset => { + // Presets are just special flags that combine other flags, we don't + // want to enable them directly, just the underlying flags. + } + _ => todo!(), + }; + } + max_builder + } + // Use `cranelift-native` to do feature detection for us. + IsaFlagGen::Host => builder_with_options(true) + .expect("Unable to build a TargetIsa for the current host"), + }; + // Cranelift has a somwhat weird API for this, but we need to build the final `TargetIsa` to be able + // to extract the values for the ISA flags. We need that to use the `string_value()` that formats + // the values so that we can pass it into the builder again. + let max_isa = max_builder.finish(Flags::new(settings::builder()))?; + + // We give each of the flags a chance of being copied over. Otherwise we keep the default. + for value in max_isa.isa_flags().iter() { + let should_copy = bool::arbitrary(self.u)?; + if !should_copy { + continue; + } + builder.set(value.name, &value.value_string())?; + } + + Ok(()) + } } diff --git a/cranelift/fuzzgen/src/print.rs b/cranelift/fuzzgen/src/print.rs index e74f0f3a84..238f92fcff 100644 --- a/cranelift/fuzzgen/src/print.rs +++ b/cranelift/fuzzgen/src/print.rs @@ -1,6 +1,6 @@ use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::Function; -use cranelift::prelude::settings; +use cranelift::prelude::settings::{self, SettingKind}; use cranelift::prelude::*; use std::fmt; @@ -70,7 +70,9 @@ impl<'a> fmt::Debug for PrintableTestCase<'a> { write_non_default_flags(f, self.isa.flags())?; - writeln!(f, "target {}\n", self.isa.triple().architecture)?; + write!(f, "target {} ", self.isa.triple().architecture)?; + write_non_default_isa_flags(f, &self.isa)?; + write!(f, "\n\n")?; // Print the functions backwards, so that the main function is printed last // and near the test inputs for run test cases. @@ -128,3 +130,33 @@ fn write_non_default_flags(f: &mut fmt::Formatter<'_>, flags: &settings::Flags) Ok(()) } + +/// Print non default ISA flags in a single line, as used in `target` declarations. +fn write_non_default_isa_flags( + f: &mut fmt::Formatter<'_>, + isa: &isa::OwnedTargetIsa, +) -> fmt::Result { + let default_isa = isa::lookup(isa.triple().clone()) + .unwrap() + .finish(isa.flags().clone()) + .unwrap(); + + for (default, flag) in default_isa.isa_flags().iter().zip(isa.isa_flags()) { + assert_eq!(default.name, flag.name); + + // Skip default flags, putting them all out there is too verbose. + if default.value_string() == flag.value_string() { + continue; + } + + // On boolean flags we can use the shorthand syntax instead of just specifying the flag name. + // This is slightly neater than the full syntax. + if flag.kind() == SettingKind::Bool && flag.value_string() == "true" { + write!(f, "{} ", flag.name)?; + } else { + write!(f, "{}={} ", flag.name, flag.value_string())?; + } + } + + Ok(()) +} diff --git a/fuzz/fuzz_targets/cranelift-fuzzgen.rs b/fuzz/fuzz_targets/cranelift-fuzzgen.rs index b20755509d..addf4f71f4 100644 --- a/fuzz/fuzz_targets/cranelift-fuzzgen.rs +++ b/fuzz/fuzz_targets/cranelift-fuzzgen.rs @@ -179,9 +179,10 @@ impl TestCase { // TestCase is meant to be consumed by a runner, so we make the assumption here that we're // generating a TargetIsa for the host. - let builder = + let mut builder = builder_with_options(true).expect("Unable to build a TargetIsa for the current host"); let flags = gen.generate_flags(builder.triple().architecture)?; + gen.set_isa_flags(&mut builder, IsaFlagGen::Host)?; let isa = builder.finish(flags)?; // When generating functions, we allow each function to call any function that has diff --git a/fuzz/fuzz_targets/cranelift-icache.rs b/fuzz/fuzz_targets/cranelift-icache.rs index 886d158ac1..aa4dc7c793 100644 --- a/fuzz/fuzz_targets/cranelift-icache.rs +++ b/fuzz/fuzz_targets/cranelift-icache.rs @@ -48,13 +48,15 @@ impl FunctionWithIsa { // a supported one, so that the same fuzz input works across different build // configurations. let target = u.choose(isa::ALL_ARCHITECTURES)?; - let builder = isa::lookup_by_name(target).map_err(|_| arbitrary::Error::IncorrectFormat)?; + let mut builder = + isa::lookup_by_name(target).map_err(|_| arbitrary::Error::IncorrectFormat)?; let architecture = builder.triple().architecture; let mut gen = FuzzGen::new(u); let flags = gen .generate_flags(architecture) .map_err(|_| arbitrary::Error::IncorrectFormat)?; + gen.set_isa_flags(&mut builder, IsaFlagGen::All)?; let isa = builder .finish(flags) .map_err(|_| arbitrary::Error::IncorrectFormat)?;