diff --git a/Cargo.lock b/Cargo.lock index 015c7f82289f..8465d6f6f40b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -570,6 +570,7 @@ dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", + "cranelift-control", "cranelift-entity", "cranelift-isle", "criterion", @@ -596,6 +597,13 @@ dependencies = [ name = "cranelift-codegen-shared" version = "0.95.0" +[[package]] +name = "cranelift-control" +version = "0.95.0" +dependencies = [ + "arbitrary", +] + [[package]] name = "cranelift-entity" version = "0.95.0" @@ -610,6 +618,7 @@ dependencies = [ "anyhow", "cranelift", "cranelift-codegen", + "cranelift-control", "cranelift-frontend", "cranelift-interpreter", "cranelift-jit", @@ -650,6 +659,7 @@ dependencies = [ "anyhow", "arbitrary", "cranelift", + "cranelift-control", "cranelift-native", "target-lexicon", ] @@ -684,6 +694,7 @@ dependencies = [ "anyhow", "cranelift", "cranelift-codegen", + "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-module", @@ -703,6 +714,7 @@ version = "0.95.0" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-control", "hashbrown 0.13.1", ] @@ -711,6 +723,7 @@ name = "cranelift-native" version = "0.95.0" dependencies = [ "cranelift-codegen", + "cranelift-control", "libc", "target-lexicon", ] @@ -721,6 +734,7 @@ version = "0.95.0" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-module", @@ -735,6 +749,7 @@ version = "0.95.0" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-control", "smallvec", "target-lexicon", ] @@ -3583,6 +3598,7 @@ version = "8.0.0" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-native", @@ -3682,6 +3698,7 @@ dependencies = [ "component-fuzz-util", "component-test-util", "cranelift-codegen", + "cranelift-control", "cranelift-filetests", "cranelift-fuzzgen", "cranelift-interpreter", diff --git a/Cargo.toml b/Cargo.toml index 6f081ca40a3e..f94fbd6d1724 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,6 +161,7 @@ cranelift-object = { path = "cranelift/object", version = "0.95.0" } cranelift-jit = { path = "cranelift/jit", version = "0.95.0" } cranelift-fuzzgen = { path = "cranelift/fuzzgen" } cranelift-bforest = { path = "cranelift/bforest", version = "0.95.0" } +cranelift-control = { path = "cranelift/control", version = "0.95.0" } cranelift = { path = "cranelift/umbrella", version = "0.95.0" } winch-codegen = { path = "winch/codegen", version = "=0.6.0" } diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 6ea37cbd480b..9db523bfdf08 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -19,6 +19,7 @@ capstone = { workspace = true, optional = true } cranelift-codegen-shared = { path = "./shared", version = "0.95.0" } cranelift-entity = { workspace = true } cranelift-bforest = { workspace = true } +cranelift-control = { workspace = true } hashbrown = { workspace = true, features = ["raw"] } target-lexicon = { workspace = true } log = { workspace = true } @@ -113,6 +114,8 @@ isle-errors = ["cranelift-isle/fancy-errors"] # inspection, rather than inside of target/. isle-in-source-tree = [] +chaos = ["cranelift-control/chaos"] + [badges] maintenance = { status = "experimental" } diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index bd8ae3b646d6..e85731c0db93 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -33,6 +33,7 @@ use crate::{timing, CompileError}; #[cfg(feature = "souper-harvest")] use alloc::string::String; use alloc::vec::Vec; +use cranelift_control::ControlPlane; #[cfg(feature = "souper-harvest")] use crate::souper_harvest::do_souper_harvest; @@ -56,6 +57,10 @@ pub struct Context { /// Flag: do we want a disassembly with the CompiledCode? pub want_disasm: bool, + + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl Context { @@ -63,15 +68,15 @@ impl Context { /// /// The returned instance should be reused for compiling multiple functions in order to avoid /// needless allocator thrashing. - pub fn new() -> Self { - Self::for_function(Function::new()) + pub fn new(control_plane: ControlPlane) -> Self { + Self::for_function(Function::new(), control_plane) } /// Allocate a new compilation context with an existing Function. /// /// The returned instance should be reused for compiling multiple functions in order to avoid /// needless allocator thrashing. - pub fn for_function(func: Function) -> Self { + pub fn for_function(func: Function, control_plane: ControlPlane) -> Self { Self { func, cfg: ControlFlowGraph::new(), @@ -79,6 +84,7 @@ impl Context { loop_analysis: LoopAnalysis::new(), compiled_code: None, want_disasm: false, + control_plane, } } diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index 65c628b430f1..013c9e26240d 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -15,6 +15,7 @@ use crate::result::CodegenResult; use crate::settings as shared_settings; use alloc::{boxed::Box, vec::Vec}; use core::fmt; +use cranelift_control::ControlPlane; use regalloc2::MachineEnv; use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple}; @@ -34,6 +35,9 @@ pub struct AArch64Backend { flags: shared_settings::Flags, isa_flags: aarch64_settings::Flags, machine_env: MachineEnv, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl AArch64Backend { @@ -42,6 +46,7 @@ impl AArch64Backend { triple: Triple, flags: shared_settings::Flags, isa_flags: aarch64_settings::Flags, + control_plane: ControlPlane, ) -> AArch64Backend { let machine_env = create_reg_env(&flags); AArch64Backend { @@ -49,6 +54,7 @@ impl AArch64Backend { flags, isa_flags, machine_env, + control_plane, } } @@ -62,7 +68,15 @@ impl AArch64Backend { let emit_info = EmitInfo::new(self.flags.clone()); let sigs = SigSet::new::(func, &self.flags)?; let abi = abi::AArch64Callee::new(func, self, &self.isa_flags, &sigs)?; - compile::compile::(func, domtree, self, abi, emit_info, sigs) + compile::compile::( + func, + domtree, + self, + abi, + emit_info, + sigs, + self.control_plane.clone(), + ) } } @@ -231,14 +245,16 @@ impl fmt::Display for AArch64Backend { } /// Create a new `isa::Builder`. -pub fn isa_builder(triple: Triple) -> IsaBuilder { +pub fn isa_builder(triple: Triple, control_plane: ControlPlane) -> IsaBuilder { assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64)); IsaBuilder { triple, + control_plane, setup: aarch64_settings::builder(), - constructor: |triple, shared_flags, builder| { + constructor: |triple, shared_flags, builder, control_plane| { let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder); - let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags); + let backend = + AArch64Backend::new_with_flags(triple, shared_flags, isa_flags, control_plane); Ok(backend.wrapped()) }, } diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index 3baf230f7a6a..3b47913f267a 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -57,6 +57,7 @@ use crate::CodegenResult; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::fmt; use core::fmt::{Debug, Formatter}; +use cranelift_control::ControlPlane; use target_lexicon::{triple, Architecture, PointerWidth, Triple}; // This module is made public here for benchmarking purposes. No guarantees are @@ -80,10 +81,10 @@ mod call_conv; /// Returns a builder that can create a corresponding `TargetIsa` /// or `Err(LookupError::SupportDisabled)` if not enabled. macro_rules! isa_builder { - ($name: ident, $cfg_terms: tt, $triple: ident) => {{ + ($name: ident, $cfg_terms: tt, $triple: ident, $control_plane: ident) => {{ #[cfg $cfg_terms] { - Ok($name::isa_builder($triple)) + Ok($name::isa_builder($triple, $control_plane)) } #[cfg(not $cfg_terms)] { @@ -94,14 +95,20 @@ macro_rules! isa_builder { /// Look for an ISA for the given `triple`. /// Return a builder that can create a corresponding `TargetIsa`. -pub fn lookup(triple: Triple) -> Result { +pub fn lookup(triple: Triple, control_plane: ControlPlane) -> Result { match triple.architecture { Architecture::X86_64 => { - isa_builder!(x64, (feature = "x86"), triple) + isa_builder!(x64, (feature = "x86"), triple, control_plane) + } + Architecture::Aarch64 { .. } => { + isa_builder!(aarch64, (feature = "arm64"), triple, control_plane) + } + Architecture::S390x { .. } => { + isa_builder!(s390x, (feature = "s390x"), triple, control_plane) + } + Architecture::Riscv64 { .. } => { + isa_builder!(riscv64, (feature = "riscv64"), triple, control_plane) } - Architecture::Aarch64 { .. } => isa_builder!(aarch64, (feature = "arm64"), triple), - Architecture::S390x { .. } => isa_builder!(s390x, (feature = "s390x"), triple), - Architecture::Riscv64 { .. } => isa_builder!(riscv64, (feature = "riscv64"), triple), _ => Err(LookupError::Unsupported), } } @@ -113,9 +120,9 @@ pub const ALL_ARCHITECTURES: &[&str] = &["x86_64", "aarch64", "s390x", "riscv64" /// Look for a supported ISA with the given `name`. /// Return a builder that can create a corresponding `TargetIsa`. -pub fn lookup_by_name(name: &str) -> Result { +pub fn lookup_by_name(name: &str, control_plane: ControlPlane) -> Result { use alloc::str::FromStr; - lookup(triple!(name)) + lookup(triple!(name), control_plane) } /// Describes reason for target lookup failure @@ -154,8 +161,11 @@ pub type Builder = IsaBuilder>; #[derive(Clone)] pub struct IsaBuilder { triple: Triple, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, setup: settings::Builder, - constructor: fn(Triple, settings::Flags, &settings::Builder) -> T, + constructor: fn(Triple, settings::Flags, &settings::Builder, ControlPlane) -> T, } impl IsaBuilder { @@ -164,11 +174,13 @@ impl IsaBuilder { /// function to generate the ISA from its components. pub fn new( triple: Triple, + control_plane: ControlPlane, setup: settings::Builder, - constructor: fn(Triple, settings::Flags, &settings::Builder) -> T, + constructor: fn(Triple, settings::Flags, &settings::Builder, ControlPlane) -> T, ) -> Self { IsaBuilder { triple, + control_plane, setup, constructor, } @@ -191,7 +203,12 @@ impl IsaBuilder { /// platform-independent features, like general SIMD support, may /// need certain ISA extensions to be enabled. pub fn finish(&self, shared_flags: settings::Flags) -> T { - (self.constructor)(self.triple.clone(), shared_flags, &self.setup) + (self.constructor)( + self.triple.clone(), + shared_flags, + &self.setup, + self.control_plane.clone(), + ) } } diff --git a/cranelift/codegen/src/isa/riscv64/mod.rs b/cranelift/codegen/src/isa/riscv64/mod.rs index 69711e0c8228..55cbf470e299 100644 --- a/cranelift/codegen/src/isa/riscv64/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/mod.rs @@ -15,6 +15,7 @@ use crate::result::CodegenResult; use crate::settings as shared_settings; use alloc::{boxed::Box, vec::Vec}; use core::fmt; +use cranelift_control::ControlPlane; use regalloc2::MachineEnv; use target_lexicon::{Architecture, Triple}; mod abi; @@ -34,6 +35,9 @@ pub struct Riscv64Backend { flags: shared_settings::Flags, isa_flags: riscv_settings::Flags, mach_env: MachineEnv, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl Riscv64Backend { @@ -42,6 +46,7 @@ impl Riscv64Backend { triple: Triple, flags: shared_settings::Flags, isa_flags: riscv_settings::Flags, + control_plane: ControlPlane, ) -> Riscv64Backend { let mach_env = crate_reg_eviroment(&flags); Riscv64Backend { @@ -49,6 +54,7 @@ impl Riscv64Backend { flags, isa_flags, mach_env, + control_plane, } } @@ -62,7 +68,15 @@ impl Riscv64Backend { let emit_info = EmitInfo::new(self.flags.clone(), self.isa_flags.clone()); let sigs = SigSet::new::(func, &self.flags)?; let abi = abi::Riscv64Callee::new(func, self, &self.isa_flags, &sigs)?; - compile::compile::(func, domtree, self, abi, emit_info, sigs) + compile::compile::( + func, + domtree, + self, + abi, + emit_info, + sigs, + self.control_plane.clone(), + ) } } @@ -203,17 +217,19 @@ impl fmt::Display for Riscv64Backend { } /// Create a new `isa::Builder`. -pub fn isa_builder(triple: Triple) -> IsaBuilder { +pub fn isa_builder(triple: Triple, control_plane: ControlPlane) -> IsaBuilder { match triple.architecture { Architecture::Riscv64(..) => {} _ => unreachable!(), } IsaBuilder { triple, + control_plane, setup: riscv_settings::builder(), - constructor: |triple, shared_flags, builder| { + constructor: |triple, shared_flags, builder, control_plane| { let isa_flags = riscv_settings::Flags::new(&shared_flags, builder); - let backend = Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags); + let backend = + Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags, control_plane); Ok(backend.wrapped()) }, } diff --git a/cranelift/codegen/src/isa/s390x/mod.rs b/cranelift/codegen/src/isa/s390x/mod.rs index 9ba81d14ac39..baec5bd8b6b5 100644 --- a/cranelift/codegen/src/isa/s390x/mod.rs +++ b/cranelift/codegen/src/isa/s390x/mod.rs @@ -15,6 +15,7 @@ use crate::result::CodegenResult; use crate::settings as shared_settings; use alloc::{boxed::Box, vec::Vec}; use core::fmt; +use cranelift_control::ControlPlane; use regalloc2::MachineEnv; use target_lexicon::{Architecture, Triple}; @@ -34,6 +35,9 @@ pub struct S390xBackend { flags: shared_settings::Flags, isa_flags: s390x_settings::Flags, machine_env: MachineEnv, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl S390xBackend { @@ -42,6 +46,7 @@ impl S390xBackend { triple: Triple, flags: shared_settings::Flags, isa_flags: s390x_settings::Flags, + control_plane: ControlPlane, ) -> S390xBackend { let machine_env = create_machine_env(&flags); S390xBackend { @@ -49,6 +54,7 @@ impl S390xBackend { flags, isa_flags, machine_env, + control_plane, } } @@ -62,7 +68,15 @@ impl S390xBackend { let emit_info = EmitInfo::new(self.isa_flags.clone()); let sigs = SigSet::new::(func, &self.flags)?; let abi = abi::S390xCallee::new(func, self, &self.isa_flags, &sigs)?; - compile::compile::(func, domtree, self, abi, emit_info, sigs) + compile::compile::( + func, + domtree, + self, + abi, + emit_info, + sigs, + self.control_plane.clone(), + ) } } @@ -203,14 +217,16 @@ impl fmt::Display for S390xBackend { } /// Create a new `isa::Builder`. -pub fn isa_builder(triple: Triple) -> IsaBuilder { +pub fn isa_builder(triple: Triple, control_plane: ControlPlane) -> IsaBuilder { assert!(triple.architecture == Architecture::S390x); IsaBuilder { triple, + control_plane, setup: s390x_settings::builder(), - constructor: |triple, shared_flags, builder| { + constructor: |triple, shared_flags, builder, control_plane| { let isa_flags = s390x_settings::Flags::new(&shared_flags, builder); - let backend = S390xBackend::new_with_flags(triple, shared_flags, isa_flags); + let backend = + S390xBackend::new_with_flags(triple, shared_flags, isa_flags, control_plane); Ok(backend.wrapped()) }, } diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index dd84f4395a43..1092652f5179 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -17,6 +17,7 @@ use crate::result::{CodegenError, CodegenResult}; use crate::settings::{self as shared_settings, Flags}; use alloc::{boxed::Box, vec::Vec}; use core::fmt; +use cranelift_control::ControlPlane; use regalloc2::MachineEnv; use target_lexicon::Triple; @@ -32,17 +33,26 @@ pub(crate) struct X64Backend { flags: Flags, x64_flags: x64_settings::Flags, reg_env: MachineEnv, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl X64Backend { /// Create a new X64 backend with the given (shared) flags. - fn new_with_flags(triple: Triple, flags: Flags, x64_flags: x64_settings::Flags) -> Self { + fn new_with_flags( + triple: Triple, + flags: Flags, + x64_flags: x64_settings::Flags, + control_plane: ControlPlane, + ) -> Self { let reg_env = create_reg_env_systemv(&flags); Self { triple, flags, x64_flags, reg_env, + control_plane, } } @@ -56,7 +66,15 @@ impl X64Backend { let emit_info = EmitInfo::new(self.flags.clone(), self.x64_flags.clone()); let sigs = SigSet::new::(func, &self.flags)?; let abi = abi::X64Callee::new(func, self, &self.x64_flags, &sigs)?; - compile::compile::(func, domtree, self, abi, emit_info, sigs) + compile::compile::( + func, + domtree, + self, + abi, + emit_info, + sigs, + self.control_plane.clone(), + ) } } @@ -201,9 +219,10 @@ impl fmt::Display for X64Backend { } /// Create a new `isa::Builder`. -pub(crate) fn isa_builder(triple: Triple) -> IsaBuilder { +pub(crate) fn isa_builder(triple: Triple, control_plane: ControlPlane) -> IsaBuilder { IsaBuilder { triple, + control_plane, setup: x64_settings::builder(), constructor: isa_constructor, } @@ -213,6 +232,7 @@ fn isa_constructor( triple: Triple, shared_flags: Flags, builder: &shared_settings::Builder, + control_plane: ControlPlane, ) -> CodegenResult { let isa_flags = x64_settings::Flags::new(&shared_flags, builder); @@ -230,7 +250,7 @@ fn isa_constructor( } } - let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags); + let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags, control_plane); Ok(backend.wrapped()) } @@ -246,7 +266,7 @@ mod test { let mut shared_flags_builder = settings::builder(); shared_flags_builder.set("enable_simd", "true").unwrap(); let shared_flags = settings::Flags::new(shared_flags_builder); - let mut isa_builder = crate::isa::lookup_by_name("x86_64").unwrap(); + let mut isa_builder = crate::isa::lookup_by_name("x86_64", ControlPlane::noop()).unwrap(); isa_builder.set("has_sse3", "false").unwrap(); isa_builder.set("has_ssse3", "false").unwrap(); isa_builder.set("has_sse41", "false").unwrap(); diff --git a/cranelift/codegen/src/machinst/buffer.rs b/cranelift/codegen/src/machinst/buffer.rs index 6dc25184560e..1d95c66db750 100644 --- a/cranelift/codegen/src/machinst/buffer.rs +++ b/cranelift/codegen/src/machinst/buffer.rs @@ -148,6 +148,7 @@ use crate::machinst::{ }; use crate::timing; use crate::trace; +use cranelift_control::ControlPlane; use cranelift_entity::{entity_impl, SecondaryMap}; use smallvec::SmallVec; use std::convert::TryFrom; @@ -261,6 +262,9 @@ pub struct MachBuffer { labels_at_tail_off: CodeOffset, /// Map used constants to their [MachLabel]. constant_labels: SecondaryMap, + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } impl MachBufferFinalized { @@ -358,7 +362,7 @@ pub enum StackMapExtent { impl MachBuffer { /// Create a new section, known to start at `start_offset` and with a size limited to /// `length_limit`. - pub fn new() -> MachBuffer { + pub fn new(control_plane: ControlPlane) -> MachBuffer { MachBuffer { data: SmallVec::new(), relocs: SmallVec::new(), @@ -378,6 +382,7 @@ impl MachBuffer { labels_at_tail: SmallVec::new(), labels_at_tail_off: 0, constant_labels: SecondaryMap::new(), + control_plane, } } @@ -771,6 +776,11 @@ impl MachBuffer { } fn optimize_branches(&mut self) { + #[cfg(feature = "chaos")] + if let Ok(true) = self.control_plane.get_arbitrary_bool() { + return; + } + self.lazily_clear_labels_at_tail(); // Invariants valid at this point. @@ -1613,7 +1623,7 @@ impl MachTextSectionBuilder { /// Creates a new text section builder which will have `num_funcs` functions /// pushed into it. pub fn new(num_funcs: usize) -> MachTextSectionBuilder { - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); buf.reserve_labels_for_blocks(num_funcs); MachTextSectionBuilder { buf, @@ -1696,7 +1706,7 @@ mod test { #[test] fn test_elide_jump_to_next() { let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(2); @@ -1711,7 +1721,7 @@ mod test { #[test] fn test_elide_trivial_jump_blocks() { let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(4); @@ -1741,7 +1751,7 @@ mod test { #[test] fn test_flip_cond() { let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(4); @@ -1768,7 +1778,7 @@ mod test { let buf = buf.finish(); - let mut buf2 = MachBuffer::new(); + let mut buf2 = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); let inst = Inst::TrapIf { kind: CondBrKind::NotZero(xreg(0)), @@ -1786,7 +1796,7 @@ mod test { #[test] fn test_island() { let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(4); @@ -1820,7 +1830,7 @@ mod test { assert_eq!(2000000 + 8, buf.total_size()); - let mut buf2 = MachBuffer::new(); + let mut buf2 = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); let inst = Inst::CondBr { kind: CondBrKind::NotZero(xreg(0)), @@ -1853,7 +1863,7 @@ mod test { #[test] fn test_island_backward() { let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(4); @@ -1884,7 +1894,7 @@ mod test { assert_eq!(2000000 + 12, buf.total_size()); - let mut buf2 = MachBuffer::new(); + let mut buf2 = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); let inst = Inst::CondBr { kind: CondBrKind::NotZero(xreg(0)), @@ -1938,7 +1948,7 @@ mod test { // ret let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(8); @@ -2014,7 +2024,7 @@ mod test { // label0, label1, ..., label4: // b label0 let info = EmitInfo::new(settings::Flags::new(settings::builder())); - let mut buf = MachBuffer::new(); + let mut buf = MachBuffer::new(ControlPlane::noop()); let mut state = Default::default(); buf.reserve_labels_for_blocks(5); @@ -2050,7 +2060,7 @@ mod test { #[test] fn metadata_records() { - let mut buf = MachBuffer::::new(); + let mut buf = MachBuffer::::new(ControlPlane::noop()); buf.reserve_labels_for_blocks(1); diff --git a/cranelift/codegen/src/machinst/compile.rs b/cranelift/codegen/src/machinst/compile.rs index 254b4ec55750..483de6e69d55 100644 --- a/cranelift/codegen/src/machinst/compile.rs +++ b/cranelift/codegen/src/machinst/compile.rs @@ -7,6 +7,7 @@ use crate::machinst::*; use crate::timing; use crate::trace; +use cranelift_control::ControlPlane; use regalloc2::RegallocOptions; /// Compile the given function down to VCode with allocated registers, ready @@ -18,6 +19,7 @@ pub fn compile( abi: Callee<<::MInst as MachInst>::ABIMachineSpec>, emit_info: ::Info, sigs: SigSet, + control_plane: ControlPlane, ) -> CodegenResult<(VCode, regalloc2::Output)> { let machine_env = b.machine_env(); @@ -25,7 +27,15 @@ pub fn compile( let block_order = BlockLoweringOrder::new(f, domtree); // Build the lowering context. - let lower = crate::machinst::Lower::new(f, machine_env, abi, emit_info, block_order, sigs)?; + let lower = crate::machinst::Lower::new( + f, + machine_env, + abi, + emit_info, + block_order, + sigs, + control_plane, + )?; // Lower the IR. let vcode = { diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index d6688c054d29..60d9d482d2a5 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -20,6 +20,7 @@ use crate::machinst::{ }; use crate::{trace, CodegenResult}; use alloc::vec::Vec; +use cranelift_control::ControlPlane; use regalloc2::{MachineEnv, PRegSet}; use smallvec::{smallvec, SmallVec}; use std::fmt::Debug; @@ -333,6 +334,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { emit_info: I::Info, block_order: BlockLoweringOrder, sigs: SigSet, + control_plane: ControlPlane, ) -> CodegenResult { let constants = VCodeConstants::with_capacity(f.dfg.constants.len()); let vcode = VCodeBuilder::new( @@ -342,6 +344,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { block_order, constants, VCodeBuildDirection::Backward, + control_plane, ); let mut vregs = VRegAllocator::new(); diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 81aaf54862e9..181c59391720 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -26,6 +26,7 @@ use crate::timing; use crate::trace; use crate::CodegenError; use crate::ValueLocRange; +use cranelift_control::ControlPlane; use regalloc2::{ Edit, Function as RegallocFunction, InstOrEdit, InstRange, Operand, OperandKind, PRegSet, RegClass, VReg, @@ -174,6 +175,10 @@ pub struct VCode { debug_value_labels: Vec<(VReg, InsnIndex, InsnIndex, u32)>, pub(crate) sigs: SigSet, + + /// Only used during fuzz-testing. Otherwise, this is a zero-sized struct + /// and compiled away. See [cranelift_control]. + control_plane: ControlPlane, } /// The result of `VCode::emit`. Contains all information computed @@ -284,8 +289,9 @@ impl VCodeBuilder { block_order: BlockLoweringOrder, constants: VCodeConstants, direction: VCodeBuildDirection, + control_plane: ControlPlane, ) -> VCodeBuilder { - let vcode = VCode::new(sigs, abi, emit_info, block_order, constants); + let vcode = VCode::new(sigs, abi, emit_info, block_order, constants, control_plane); VCodeBuilder { vcode, @@ -642,6 +648,7 @@ impl VCode { emit_info: I::Info, block_order: BlockLoweringOrder, constants: VCodeConstants, + control_plane: ControlPlane, ) -> VCode { let n_blocks = block_order.lowered_order().len(); VCode { @@ -670,6 +677,7 @@ impl VCode { constants, debug_value_labels: vec![], vreg_aliases: FxHashMap::with_capacity_and_hasher(10 * n_blocks, Default::default()), + control_plane, } } @@ -771,7 +779,7 @@ impl VCode { use core::fmt::Write; let _tt = timing::vcode_emit(); - let mut buffer = MachBuffer::new(); + let mut buffer = MachBuffer::new(self.control_plane.clone()); let mut bb_starts: Vec> = vec![]; // The first M MachLabels are reserved for block indices, the next N MachLabels for diff --git a/cranelift/control/Cargo.toml b/cranelift/control/Cargo.toml new file mode 100644 index 000000000000..3b0cc11c5756 --- /dev/null +++ b/cranelift/control/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["The Cranelift Project Developers"] +name = "cranelift-control" +version = "0.95.0" +description = "White-box fuzz testing framework" +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasmtime" +readme = "README.md" +keywords = ["fuzz", "test"] +edition.workspace = true + +[dependencies] +arbitrary = { version = "1.1.0" } + +[features] + +# Turn on chaos mode. +# Without this feature, a zero-sized dummy will be compiled +# for the control plane. +chaos = [] diff --git a/cranelift/control/LICENSE b/cranelift/control/LICENSE new file mode 100644 index 000000000000..f9d81955f4bc --- /dev/null +++ b/cranelift/control/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/cranelift/control/README.md b/cranelift/control/README.md new file mode 100644 index 000000000000..3eecb3d47b6b --- /dev/null +++ b/cranelift/control/README.md @@ -0,0 +1,4 @@ +This crate contains the control plane for "chaos mode". It can be used to +inject pseudo-random perturbations into specific sections in the code while +fuzzing. Its compilation is feature-gated to prevent any performance +impact on release builds. diff --git a/cranelift/control/src/chaos.rs b/cranelift/control/src/chaos.rs new file mode 100644 index 000000000000..be13c81fae03 --- /dev/null +++ b/cranelift/control/src/chaos.rs @@ -0,0 +1,111 @@ +use std::{ + fmt::Debug, + sync::{Arc, Mutex}, +}; + +use arbitrary::{Arbitrary, Unstructured}; + +#[derive(Debug, Default, Arbitrary)] +struct ControlPlaneData { + bools: Vec, +} + +/// The control plane of chaos mode. +/// Please see the [crate-level documentation](crate). +/// +/// **Clone liberally!** The control plane is reference counted. +#[derive(Debug, Clone)] +pub struct ControlPlane { + data: Arc>, + is_todo: bool, +} + +impl ControlPlane { + fn new(data: ControlPlaneData, is_todo: bool) -> Self { + Self { + data: Arc::new(Mutex::new(data)), + is_todo, + } + } + + /// This is a zero-sized dummy for use during any builds without the + /// feature `chaos` enabled, especially release builds. It has no + /// functionality, so the programmer is prevented from using it in any + /// way in release builds, which could degrade performance. + /// + /// This should not be used on code paths that may execute while the + /// feature `chaos` is enabled. That would break the assumption that + /// [ControlPlane] is a singleton, responsible for centrally managing + /// the pseudo-randomness injected at runtimme. + /// + /// Use [todo](ControlPlane::todo) instead, for stubbing out code paths + /// you don't expect to be reached (yet) during chaos mode fuzzing. + /// + /// # Panics + /// + /// Panics if it is called while the feature `chaos` is enabled. + #[track_caller] + pub fn noop() -> Self { + panic!( + "attempted to create a NOOP control plane \ + (while chaos mode was enabled)" + ); + } + + /// This is the same as [noop](ControlPlane::noop) when the the feature + /// `chaos` is *disabled*. When `chaos` is enabled, it returns a + /// control plane that returns [Error::Todo] when + /// [get_arbitrary](ControlPlane::get_arbitrary) is called. + /// + /// This may be used during development, in places which are (supposed + /// to be) unreachable during fuzzing. Use of this function should be + /// reduced as the chaos mode is introduced in more parts of the + /// wasmtime codebase. Eventually, it should be deleted. + pub fn todo() -> Self { + Self::new(ControlPlaneData::default(), true) + } +} + +impl<'a> Arbitrary<'a> for ControlPlane { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + Ok(Self::new(u.arbitrary()?, false)) + } + fn arbitrary_take_rest(u: Unstructured<'a>) -> arbitrary::Result { + Ok(Self::new(Arbitrary::arbitrary_take_rest(u)?, false)) + } +} + +/// An enumeration of control plane API errors. +#[derive(Debug, Clone, Copy)] +pub enum Error { + /// There was not enough underlying data to fulfill some request for raw + /// bytes. + NotEnoughData, + /// The control plane API was accessed on a [ControlPlane::todo]. + Todo, +} + +impl ControlPlane { + /// Request an arbitrary bool from the control plane. + /// + /// # Errors + /// + /// - There was not enough underlying data to fulfill some request for + /// raw bytes: [Error::NotEnoughData]. + /// - Calling this function on a control plane received from a call to + /// [todo] will return an [Error::Todo]. + /// + /// [arbitrary]: arbitrary::Arbitrary::arbitrary + /// [todo]: ControlPlane::todo + pub fn get_arbitrary_bool(&self) -> Result { + if self.is_todo { + return Err(Error::Todo); + } + self.data + .lock() + .expect("poisoned ControlPlaneData mutex") + .bools + .pop() + .ok_or(Error::NotEnoughData) + } +} diff --git a/cranelift/control/src/lib.rs b/cranelift/control/src/lib.rs new file mode 100644 index 000000000000..1f7d593d4401 --- /dev/null +++ b/cranelift/control/src/lib.rs @@ -0,0 +1,34 @@ +//! # Cranelift Control +//! +//! This is the home of the control plane of chaos mode, a compilation feature +//! intended to be turned on for certain fuzz targets. When the feature is +//! turned off, as is normally the case, [ControlPlane] will be a zero-sized +//! type and optimized away. +//! +//! While the feature is turned on, the struct [ControlPlane] +//! provides functionality to tap into pseudo-randomness at specific locations +//! in the code. It may be used for targeted fuzzing of compiler internals, +//! e.g. manipulate heuristic optimizations, clobber undefined register bits +//! etc. +//! +//! There are three ways to acquire a [ControlPlane]: +//! - [arbitrary] for the real deal +//! - [noop] for the zero-sized type when `chaos` is disabled +//! - [todo] for stubbing out code paths during development +//! +//! The reason both [noop] and [todo] exist is so that [todo] can easily +//! be searched for and removed later. +//! +//! [arbitrary]: ControlPlane#method.arbitrary +//! [noop]: ControlPlane::noop +//! [todo]: ControlPlane::todo + +#[cfg(not(any(feature = "chaos", doc)))] +mod zero_sized; +#[cfg(not(any(feature = "chaos", doc)))] +pub use zero_sized::*; + +#[cfg(any(feature = "chaos", doc))] +mod chaos; +#[cfg(any(feature = "chaos", doc))] +pub use chaos::*; diff --git a/cranelift/control/src/zero_sized.rs b/cranelift/control/src/zero_sized.rs new file mode 100644 index 000000000000..e443a4f0cdcb --- /dev/null +++ b/cranelift/control/src/zero_sized.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Clone)] +pub struct ControlPlane { + /// prevent direct instantiation (use `noop` or `todo` instead) + _private: (), +} + +impl ControlPlane { + pub fn noop() -> Self { + Self { _private: () } + } + + pub fn todo() -> Self { + Self::noop() + } +} diff --git a/cranelift/filetests/Cargo.toml b/cranelift/filetests/Cargo.toml index 7ed94698ce0c..789dfa5c66a1 100644 --- a/cranelift/filetests/Cargo.toml +++ b/cranelift/filetests/Cargo.toml @@ -17,6 +17,7 @@ cranelift-native = { workspace = true } cranelift-reader = { workspace = true } cranelift-jit = { workspace = true, features = ["selinux-fix"] } cranelift-module = { workspace = true } +cranelift-control = { workspace = true } file-per-thread-logger = "0.1.2" filecheck = "0.5.0" gimli = { workspace = true } diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 1bc159451887..87000f9574e3 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -7,6 +7,7 @@ use cranelift_codegen::ir::{ }; use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::{ir, settings, CodegenError, Context}; +use cranelift_control::ControlPlane; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{FuncId, Linkage, Module, ModuleError}; @@ -101,8 +102,8 @@ impl TestFileCompiler { /// Build a [TestFileCompiler] using the host machine's ISA and the passed flags. pub fn with_host_isa(flags: settings::Flags) -> Result { - let builder = - builder_with_options(true).expect("Unable to build a TargetIsa for the current host"); + let builder = builder_with_options(true, ControlPlane::noop()) + .expect("Unable to build a TargetIsa for the current host"); let isa = builder.finish(flags)?; Ok(Self::new(isa)) } diff --git a/cranelift/filetests/src/test_alias_analysis.rs b/cranelift/filetests/src/test_alias_analysis.rs index 8d155811ba1c..835675419b61 100644 --- a/cranelift/filetests/src/test_alias_analysis.rs +++ b/cranelift/filetests/src/test_alias_analysis.rs @@ -9,6 +9,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -32,7 +33,8 @@ impl SubTest for TestAliasAnalysis { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); comp_ctx.flowgraph(); comp_ctx diff --git a/cranelift/filetests/src/test_compile.rs b/cranelift/filetests/src/test_compile.rs index 94c975c147b9..074e1839afe0 100644 --- a/cranelift/filetests/src/test_compile.rs +++ b/cranelift/filetests/src/test_compile.rs @@ -8,6 +8,7 @@ use cranelift_codegen::ir; use cranelift_codegen::ir::function::FunctionParameters; use cranelift_codegen::isa; use cranelift_codegen::CompiledCode; +use cranelift_control::ControlPlane; use cranelift_reader::{TestCommand, TestOption}; use log::info; use similar::TextDiff; @@ -52,7 +53,8 @@ impl SubTest for TestCompile { fn run(&self, func: Cow, context: &Context) -> Result<()> { let isa = context.isa.expect("compile needs an ISA"); let params = func.params.clone(); - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); // With `MachBackend`s, we need to explicitly request dissassembly results. comp_ctx.set_disasm(true); diff --git a/cranelift/filetests/src/test_dce.rs b/cranelift/filetests/src/test_dce.rs index 48d28c7e72bb..48db483ddb3b 100644 --- a/cranelift/filetests/src/test_dce.rs +++ b/cranelift/filetests/src/test_dce.rs @@ -8,6 +8,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -31,7 +32,8 @@ impl SubTest for TestDCE { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); comp_ctx.flowgraph(); comp_ctx.compute_loop_analysis(); diff --git a/cranelift/filetests/src/test_legalizer.rs b/cranelift/filetests/src/test_legalizer.rs index 8af819bed234..e5cb989bbc0e 100644 --- a/cranelift/filetests/src/test_legalizer.rs +++ b/cranelift/filetests/src/test_legalizer.rs @@ -6,6 +6,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -33,7 +34,8 @@ impl SubTest for TestLegalizer { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); let isa = context.isa.expect("legalizer needs an ISA"); comp_ctx.compute_cfg(); diff --git a/cranelift/filetests/src/test_licm.rs b/cranelift/filetests/src/test_licm.rs index b02bac1e74c6..db5b03ccb2cc 100644 --- a/cranelift/filetests/src/test_licm.rs +++ b/cranelift/filetests/src/test_licm.rs @@ -8,6 +8,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -36,7 +37,8 @@ impl SubTest for TestLICM { fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { let isa = context.isa.expect("LICM needs an ISA"); - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); comp_ctx.flowgraph(); comp_ctx.compute_loop_analysis(); diff --git a/cranelift/filetests/src/test_optimize.rs b/cranelift/filetests/src/test_optimize.rs index dfab6a1c4aa1..2198ff6cbf5f 100644 --- a/cranelift/filetests/src/test_optimize.rs +++ b/cranelift/filetests/src/test_optimize.rs @@ -10,6 +10,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use anyhow::Result; use cranelift_codegen::ir; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -35,7 +36,8 @@ impl SubTest for TestOptimize { fn run(&self, func: Cow, context: &Context) -> Result<()> { let isa = context.isa.expect("optimize needs an ISA"); - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); comp_ctx .optimize(isa) diff --git a/cranelift/filetests/src/test_run.rs b/cranelift/filetests/src/test_run.rs index 8b80ae99efcf..489d003c8992 100644 --- a/cranelift/filetests/src/test_run.rs +++ b/cranelift/filetests/src/test_run.rs @@ -11,6 +11,7 @@ use cranelift_codegen::ir::Type; use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::{Configurable, Flags}; use cranelift_codegen::{ir, settings}; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use cranelift_reader::{parse_run_command, TestFile}; use log::{info, trace}; @@ -35,8 +36,9 @@ fn build_host_isa( flags: settings::Flags, isa_flags: Vec, ) -> OwnedTargetIsa { - let mut builder = cranelift_native::builder_with_options(infer_native_flags) - .expect("Unable to build a TargetIsa for the current host"); + let mut builder = + cranelift_native::builder_with_options(infer_native_flags, ControlPlane::noop()) + .expect("Unable to build a TargetIsa for the current host"); // Copy ISA Flags for value in isa_flags { diff --git a/cranelift/filetests/src/test_safepoint.rs b/cranelift/filetests/src/test_safepoint.rs index 49676e01be20..54707328ac90 100644 --- a/cranelift/filetests/src/test_safepoint.rs +++ b/cranelift/filetests/src/test_safepoint.rs @@ -1,5 +1,6 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -19,7 +20,8 @@ impl SubTest for TestSafepoint { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); let isa = context.isa.expect("register allocator needs an ISA"); comp_ctx.compute_cfg(); diff --git a/cranelift/filetests/src/test_simple_gvn.rs b/cranelift/filetests/src/test_simple_gvn.rs index bb563f4315cb..53784a2dd244 100644 --- a/cranelift/filetests/src/test_simple_gvn.rs +++ b/cranelift/filetests/src/test_simple_gvn.rs @@ -8,6 +8,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -31,7 +32,8 @@ impl SubTest for TestSimpleGVN { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); comp_ctx.flowgraph(); comp_ctx diff --git a/cranelift/filetests/src/test_simple_preopt.rs b/cranelift/filetests/src/test_simple_preopt.rs index 9a591ef023f0..12089187cc3f 100644 --- a/cranelift/filetests/src/test_simple_preopt.rs +++ b/cranelift/filetests/src/test_simple_preopt.rs @@ -5,6 +5,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen; use cranelift_codegen::ir::Function; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use std::borrow::Cow; @@ -32,7 +33,8 @@ impl SubTest for TestSimplePreopt { } fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); let isa = context.isa.expect("preopt needs an ISA"); comp_ctx.compute_cfg(); diff --git a/cranelift/filetests/src/test_unwind.rs b/cranelift/filetests/src/test_unwind.rs index 5174b91da0b9..c75d1e0a1173 100644 --- a/cranelift/filetests/src/test_unwind.rs +++ b/cranelift/filetests/src/test_unwind.rs @@ -5,6 +5,7 @@ use crate::subtest::{run_filecheck, Context, SubTest}; use cranelift_codegen::{self, ir, isa::unwind::UnwindInfo}; +use cranelift_control::ControlPlane; use cranelift_reader::TestCommand; use gimli::{ write::{Address, EhFrame, EndianVec, FrameTable}, @@ -37,7 +38,8 @@ impl SubTest for TestUnwind { fn run(&self, func: Cow, context: &Context) -> anyhow::Result<()> { let isa = context.isa.expect("unwind needs an ISA"); - let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + let mut comp_ctx = + cranelift_codegen::Context::for_function(func.into_owned(), ControlPlane::noop()); let code = comp_ctx.compile(isa).expect("failed to compile function"); diff --git a/cranelift/filetests/src/test_wasm.rs b/cranelift/filetests/src/test_wasm.rs index 0ecba3510a59..522bd5a8fe79 100644 --- a/cranelift/filetests/src/test_wasm.rs +++ b/cranelift/filetests/src/test_wasm.rs @@ -5,6 +5,7 @@ mod env; use anyhow::{bail, ensure, Context, Result}; use config::TestConfig; +use cranelift_control::ControlPlane; use env::ModuleEnv; use similar::TextDiff; use std::{fmt::Write, path::Path}; @@ -55,7 +56,8 @@ pub fn run(path: &Path, wat: &str) -> Result<()> { let mut actual = String::new(); for (_index, func) in env.inner.info.function_bodies.iter() { if config.compile { - let mut ctx = cranelift_codegen::Context::for_function(func.clone()); + let mut ctx = + cranelift_codegen::Context::for_function(func.clone(), ControlPlane::noop()); ctx.set_disasm(true); let code = ctx .compile(isa) @@ -63,7 +65,8 @@ pub fn run(path: &Path, wat: &str) -> Result<()> { writeln!(&mut actual, "function {}:", func.name).unwrap(); writeln!(&mut actual, "{}", code.vcode.as_ref().unwrap()).unwrap(); } else if config.optimize { - let mut ctx = cranelift_codegen::Context::for_function(func.clone()); + let mut ctx = + cranelift_codegen::Context::for_function(func.clone(), ControlPlane::noop()); ctx.optimize(isa) .map_err(|e| crate::pretty_anyhow_error(&ctx.func, e))?; writeln!(&mut actual, "{}", ctx.func.display()).unwrap(); diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index 140b39a1274a..f125140ae67f 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -13,7 +13,11 @@ publish = false [dependencies] cranelift = { workspace = true } cranelift-native = { workspace = true } +cranelift-control = { workspace = true } anyhow = { workspace = true } arbitrary = "1.0.0" target-lexicon = { workspace = true, features = ["std"] } + +[features] +chaos = ["cranelift-control/chaos"] diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 26cca8363d5d..bea51f13dbf3 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -9,6 +9,7 @@ use cranelift::codegen::ir::{Function, LibCall}; use cranelift::codegen::Context; use cranelift::prelude::*; use cranelift_arbitrary::CraneliftArbitrary; +use cranelift_control::ControlPlane; use cranelift_native::builder_with_options; use target_lexicon::{Architecture, Triple}; @@ -28,17 +29,24 @@ where { pub u: &'r mut Unstructured<'data>, pub config: Config, + pub control_plane: ControlPlane, } impl<'r, 'data> FuzzGen<'r, 'data> where 'data: 'r, { - pub fn new(u: &'r mut Unstructured<'data>) -> Self { - Self { + pub fn new(u: &'r mut Unstructured<'data>) -> anyhow::Result { + #[cfg(feature = "chaos")] + let control_plane = u.arbitrary()?; + #[cfg(not(feature = "chaos"))] + let control_plane = cranelift_control::ControlPlane::noop(); + + Ok(Self { u, config: Config::default(), - } + control_plane, + }) } pub fn generate_signature(&mut self, architecture: Architecture) -> Result { @@ -95,7 +103,7 @@ where // This is something that we can enable via flags for the compiled version, however // the interpreter won't get that version, so call that pass manually here. - let mut ctx = Context::for_function(func); + let mut ctx = Context::for_function(func, self.control_plane.clone()); // Assume that we are generating this function for the current ISA. // We disable the verifier here, since if it fails it prevents a test case from // being generated and formatted by `cargo fuzz fmt`. @@ -106,7 +114,7 @@ where builder }); - let isa = builder_with_options(false) + let isa = builder_with_options(false, self.control_plane.clone()) .expect("Unable to build a TargetIsa for the current host") .finish(flags) .expect("Failed to build TargetISA"); diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml index ec4154a93523..7a23aa3a57d4 100644 --- a/cranelift/jit/Cargo.toml +++ b/cranelift/jit/Cargo.toml @@ -14,6 +14,7 @@ cranelift-module = { workspace = true } cranelift-native = { workspace = true } cranelift-codegen = { workspace = true, features = ["std"] } cranelift-entity = { workspace = true } +cranelift-control = { workspace = true } anyhow = { workspace = true } region = "2.2.0" libc = { version = "0.2.42" } diff --git a/cranelift/jit/src/backend.rs b/cranelift/jit/src/backend.rs index e3d00a1872ff..becfa4594dc3 100644 --- a/cranelift/jit/src/backend.rs +++ b/cranelift/jit/src/backend.rs @@ -5,6 +5,7 @@ use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::Configurable; use cranelift_codegen::{self, ir, settings, MachReloc}; use cranelift_codegen::{binemit::Reloc, CodegenError}; +use cranelift_control::ControlPlane; use cranelift_entity::SecondaryMap; use cranelift_module::{ DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction, @@ -920,7 +921,7 @@ impl Module for JITModule { } fn make_context(&self) -> cranelift_codegen::Context { - let mut ctx = cranelift_codegen::Context::new(); + let mut ctx = cranelift_codegen::Context::new(ControlPlane::noop()); ctx.func.signature.call_conv = self.isa().default_call_conv(); ctx } diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml index 1352ce4a916b..cdf613de80e3 100644 --- a/cranelift/module/Cargo.toml +++ b/cranelift/module/Cargo.toml @@ -12,6 +12,7 @@ edition.workspace = true [dependencies] cranelift-codegen = { workspace = true } +cranelift-control = { workspace = true } hashbrown = { workspace = true, optional = true } anyhow = { workspace = true } diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index d6fa93b834cb..abb7b7887899 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -14,6 +14,7 @@ use cranelift_codegen::ir::Function; use cranelift_codegen::settings::SetError; use cranelift_codegen::{binemit, MachReloc}; use cranelift_codegen::{ir, isa, CodegenError, CompileError, Context}; +use cranelift_control::ControlPlane; use std::borrow::ToOwned; use std::string::String; @@ -550,7 +551,7 @@ pub trait Module { /// This ensures that the `Context` is initialized with the default calling /// convention for the `TargetIsa`. fn make_context(&self) -> Context { - let mut ctx = Context::new(); + let mut ctx = Context::new(ControlPlane::noop()); ctx.func.signature.call_conv = self.isa().default_call_conv(); ctx } diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml index 15c515609832..58f8f7ede828 100644 --- a/cranelift/native/Cargo.toml +++ b/cranelift/native/Cargo.toml @@ -12,6 +12,7 @@ edition.workspace = true [dependencies] cranelift-codegen = { workspace = true } +cranelift-control = { workspace = true } target-lexicon = { workspace = true } [target.'cfg(any(target_arch = "s390x", target_arch = "riscv64"))'.dependencies] diff --git a/cranelift/native/src/lib.rs b/cranelift/native/src/lib.rs index 993fb35e09e1..bbb3b8e942de 100644 --- a/cranelift/native/src/lib.rs +++ b/cranelift/native/src/lib.rs @@ -25,13 +25,14 @@ use cranelift_codegen::isa; use cranelift_codegen::settings::Configurable; +use cranelift_control::ControlPlane; use target_lexicon::Triple; /// Return an `isa` builder configured for the current host /// machine, or `Err(())` if the host machine is not supported /// in the current configuration. pub fn builder() -> Result { - builder_with_options(true) + builder_with_options(true, ControlPlane::noop()) } /// Return an `isa` builder configured for the current host @@ -41,8 +42,11 @@ pub fn builder() -> Result { /// Selects the given backend variant specifically; this is /// useful when more than oen backend exists for a given target /// (e.g., on x86-64). -pub fn builder_with_options(infer_native_flags: bool) -> Result { - let mut isa_builder = isa::lookup(Triple::host()).map_err(|err| match err { +pub fn builder_with_options( + infer_native_flags: bool, + control_plane: ControlPlane, +) -> Result { + let mut isa_builder = isa::lookup(Triple::host(), control_plane).map_err(|err| match err { isa::LookupError::SupportDisabled => "support for architecture disabled at compile time", isa::LookupError::Unsupported => "unsupported architecture", })?; diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 0811fb682458..84bd558ecee6 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -12,6 +12,7 @@ edition.workspace = true [dependencies] cranelift-module = { workspace = true } cranelift-codegen = { workspace = true, features = ["std"] } +cranelift-control = { workspace = true } object = { workspace = true, features = ["write"] } target-lexicon = { workspace = true } anyhow = { workspace = true } diff --git a/cranelift/object/tests/basic.rs b/cranelift/object/tests/basic.rs index d918f55e343d..e3a2171bad6e 100644 --- a/cranelift/object/tests/basic.rs +++ b/cranelift/object/tests/basic.rs @@ -2,6 +2,7 @@ use cranelift_codegen::ir::*; use cranelift_codegen::isa::CallConv; use cranelift_codegen::settings; use cranelift_codegen::{ir::types::I16, Context}; +use cranelift_control::ControlPlane; use cranelift_entity::EntityRef; use cranelift_frontend::*; use cranelift_module::*; @@ -10,7 +11,9 @@ use cranelift_object::*; #[test] fn error_on_incompatible_sig_in_declare_function() { let flag_builder = settings::builder(); - let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa_builder = + cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu", ControlPlane::noop()) + .unwrap(); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); @@ -42,7 +45,7 @@ fn define_simple_function(module: &mut ObjectModule) -> FuncId { .declare_function("abc", Linkage::Local, &sig) .unwrap(); - let mut ctx = Context::new(); + let mut ctx = Context::new(ControlPlane::noop()); ctx.func = Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), sig); let mut func_ctx = FunctionBuilderContext::new(); { @@ -61,7 +64,9 @@ fn define_simple_function(module: &mut ObjectModule) -> FuncId { #[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")] fn panic_on_define_after_finalize() { let flag_builder = settings::builder(); - let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa_builder = + cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu", ControlPlane::noop()) + .unwrap(); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); @@ -147,7 +152,9 @@ fn switch_error() { #[test] fn libcall_function() { let flag_builder = settings::builder(); - let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa_builder = + cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu", ControlPlane::noop()) + .unwrap(); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); @@ -164,7 +171,7 @@ fn libcall_function() { .declare_function("function", Linkage::Local, &sig) .unwrap(); - let mut ctx = Context::new(); + let mut ctx = Context::new(ControlPlane::noop()); ctx.func = Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), sig); let mut func_ctx = FunctionBuilderContext::new(); { @@ -201,7 +208,9 @@ fn libcall_function() { #[should_panic(expected = "has a null byte, which is disallowed")] fn reject_nul_byte_symbol_for_func() { let flag_builder = settings::builder(); - let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa_builder = + cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu", ControlPlane::noop()) + .unwrap(); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); @@ -223,7 +232,9 @@ fn reject_nul_byte_symbol_for_func() { #[should_panic(expected = "has a null byte, which is disallowed")] fn reject_nul_byte_symbol_for_data() { let flag_builder = settings::builder(); - let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa_builder = + cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu", ControlPlane::noop()) + .unwrap(); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml index 4c8e7f6b0756..c3052e1352f9 100644 --- a/cranelift/reader/Cargo.toml +++ b/cranelift/reader/Cargo.toml @@ -12,6 +12,7 @@ edition.workspace = true [dependencies] anyhow.workspace = true cranelift-codegen = { workspace = true } +cranelift-control = { workspace = true } smallvec = { workspace = true } target-lexicon = { workspace = true } diff --git a/cranelift/reader/src/lib.rs b/cranelift/reader/src/lib.rs index a691970bca21..a39233505692 100644 --- a/cranelift/reader/src/lib.rs +++ b/cranelift/reader/src/lib.rs @@ -45,6 +45,7 @@ mod testfile; use anyhow::{Error, Result}; use cranelift_codegen::isa::{self, OwnedTargetIsa}; use cranelift_codegen::settings::{self, FlagsOrIsa}; +use cranelift_control::ControlPlane; use std::str::FromStr; use target_lexicon::Triple; @@ -95,15 +96,16 @@ pub fn parse_sets_and_triple(flag_set: &[String], flag_triple: &str) -> Result return Err(Error::from(parse_error)), }; - let mut isa_builder = isa::lookup(triple).map_err(|err| match err { - isa::LookupError::SupportDisabled => { - anyhow::anyhow!("support for triple '{}' is disabled", triple_name) - } - isa::LookupError::Unsupported => anyhow::anyhow!( - "support for triple '{}' is not implemented yet", - triple_name - ), - })?; + let mut isa_builder = + isa::lookup(triple, ControlPlane::noop()).map_err(|err| match err { + isa::LookupError::SupportDisabled => { + anyhow::anyhow!("support for triple '{}' is disabled", triple_name) + } + isa::LookupError::Unsupported => anyhow::anyhow!( + "support for triple '{}' is not implemented yet", + triple_name + ), + })?; // Try to parse system-wide unknown settings as target-specific settings. parse_options( diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 1e2be48243ce..842cc0e0304c 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -24,6 +24,7 @@ use cranelift_codegen::ir::{ use cranelift_codegen::isa::{self, CallConv}; use cranelift_codegen::packed_option::ReservedValue; use cranelift_codegen::{settings, settings::Configurable, timing}; +use cranelift_control::ControlPlane; use smallvec::SmallVec; use std::mem; use std::str::FromStr; @@ -995,7 +996,7 @@ impl<'a> Parser<'a> { Ok(triple) => triple, Err(err) => return err!(loc, err), }; - let isa_builder = match isa::lookup(triple) { + let isa_builder = match isa::lookup(triple, ControlPlane::noop()) { Err(isa::LookupError::SupportDisabled) => { return err!(loc, "support disabled target '{}'", targ); } @@ -1084,7 +1085,7 @@ impl<'a> Parser<'a> { Ok(triple) => triple, Err(err) => return err!(loc, err), }; - let mut isa_builder = match isa::lookup(triple) { + let mut isa_builder = match isa::lookup(triple, ControlPlane::noop()) { Err(isa::LookupError::SupportDisabled) => { continue; } diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml index 7c31add30a5a..40a0a9bfc2d6 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -19,6 +19,7 @@ cranelift-codegen = { workspace = true } cranelift-frontend = { workspace = true } cranelift-entity = { workspace = true } cranelift-native = { workspace = true } +cranelift-control = { workspace = true } wasmtime-cranelift-shared = { workspace = true } wasmparser = { workspace = true } target-lexicon = { workspace = true } diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index 1e32eed6fed6..1754943fa6cd 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -8,6 +8,7 @@ use cranelift_codegen::{ isa::{self, OwnedTargetIsa}, CodegenResult, }; +use cranelift_control::ControlPlane; use std::fmt; use std::sync::Arc; use wasmtime_cranelift_shared::isa_builder::IsaBuilder; @@ -34,7 +35,9 @@ pub struct LinkOptions { pub fn builder() -> Box { Box::new(Builder { - inner: IsaBuilder::new(|triple| isa::lookup(triple).map_err(|e| e.into())), + inner: IsaBuilder::new(|triple| { + isa::lookup(triple, ControlPlane::noop()).map_err(|e| e.into()) + }), linkopts: LinkOptions::default(), cache_store: None, }) diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 5733a998edb3..2b08acd22593 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -13,6 +13,7 @@ use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; use cranelift_codegen::{CompiledCode, MachSrcLoc, MachStackMap}; use cranelift_codegen::{MachReloc, MachTrap}; +use cranelift_control::ControlPlane; use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_frontend::FunctionBuilder; use cranelift_wasm::{ @@ -57,7 +58,7 @@ impl Default for CompilerContext { fn default() -> Self { Self { func_translator: FuncTranslator::new(), - codegen_context: Context::new(), + codegen_context: Context::new(ControlPlane::noop()), incremental_cache_ctx: None, validator_allocations: Default::default(), } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f9774cac38c4..7909dfcdcaf7 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -17,6 +17,7 @@ cranelift-filetests = { workspace = true } cranelift-interpreter = { workspace = true } cranelift-fuzzgen = { workspace = true } cranelift-native = { workspace = true } +cranelift-control = { workspace = true } libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } target-lexicon = { workspace = true } smallvec = { workspace = true } @@ -36,6 +37,11 @@ component-fuzz-util = { workspace = true } [features] default = ['fuzz-spec-interpreter'] fuzz-spec-interpreter = ['wasmtime-fuzzing/fuzz-spec-interpreter'] +chaos = [ + "cranelift-codegen/chaos", + "cranelift-control/chaos", + "cranelift-fuzzgen/chaos", +] [[bin]] name = "compile" diff --git a/fuzz/fuzz_targets/cranelift-fuzzgen.rs b/fuzz/fuzz_targets/cranelift-fuzzgen.rs index b20755509d16..7463d16e78b0 100644 --- a/fuzz/fuzz_targets/cranelift-fuzzgen.rs +++ b/fuzz/fuzz_targets/cranelift-fuzzgen.rs @@ -173,14 +173,14 @@ impl<'a> Arbitrary<'a> for TestCase { impl TestCase { pub fn generate(u: &mut Unstructured) -> anyhow::Result { - let mut gen = FuzzGen::new(u); + let mut gen = FuzzGen::new(u)?; let compare_against_host = gen.u.arbitrary()?; // 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 = - builder_with_options(true).expect("Unable to build a TargetIsa for the current host"); + let builder = builder_with_options(true, gen.control_plane.clone()) + .expect("Unable to build a TargetIsa for the current host"); let flags = gen.generate_flags(builder.triple().architecture)?; let isa = builder.finish(flags)?; diff --git a/fuzz/fuzz_targets/cranelift-icache.rs b/fuzz/fuzz_targets/cranelift-icache.rs index 4c0ca54553e4..7cdc7da0d6a9 100644 --- a/fuzz/fuzz_targets/cranelift-icache.rs +++ b/fuzz/fuzz_targets/cranelift-icache.rs @@ -9,6 +9,7 @@ use cranelift_codegen::{ }, isa, Context, }; +use cranelift_control::ControlPlane; use libfuzzer_sys::{ arbitrary::{self, Arbitrary, Unstructured}, fuzz_target, @@ -39,6 +40,8 @@ pub struct FunctionWithIsa { /// Function under test pub func: Function, + + control_plane: ControlPlane, } impl FunctionWithIsa { @@ -48,10 +51,11 @@ 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 gen = FuzzGen::new(u)?; + let builder = isa::lookup_by_name(target, gen.control_plane.clone()) + .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)?; @@ -82,7 +86,11 @@ impl FunctionWithIsa { ) .map_err(|_| arbitrary::Error::IncorrectFormat)?; - Ok(FunctionWithIsa { isa, func }) + Ok(FunctionWithIsa { + isa, + func, + control_plane: gen.control_plane, + }) } } @@ -101,11 +109,15 @@ impl<'a> Arbitrary<'a> for FunctionWithIsa { } fuzz_target!(|func: FunctionWithIsa| { - let FunctionWithIsa { mut func, isa } = func; + let FunctionWithIsa { + mut func, + isa, + control_plane, + } = func; let cache_key_hash = icache::compute_cache_key(&*isa, &mut func); - let mut context = Context::for_function(func.clone()); + let mut context = Context::for_function(func.clone(), control_plane.clone()); let prev_stencil = match context.compile_stencil(&*isa) { Ok(stencil) => stencil, Err(_) => return, @@ -195,7 +207,7 @@ fuzz_target!(|func: FunctionWithIsa| { assert!(cache_key_hash != new_cache_key_hash); } - context = Context::for_function(func.clone()); + context = Context::for_function(func.clone(), control_plane); let after_mutation_result = match context.compile(&*isa) { Ok(info) => info,