Skip to content

Commit

Permalink
Add a method to share Config across machines (#2608)
Browse files Browse the repository at this point in the history
With `Module::{serialize,deserialize}` it should be possible to share
wasmtime modules across machines or CPUs. Serialization, however, embeds
a hash of all configuration values, including cranelift compilation
settings. By default wasmtime's selection of the native ISA would enable
ISA flags according to CPU features available on the host, but the same
CPU features may not be available across two machines.

This commit adds a `Config::cranelift_clear_cpu_flags` method which
allows clearing the target-specific ISA flags that are automatically
inferred by default for the native CPU. Options can then be
incrementally built back up as-desired with teh `cranelift_other_flag`
method.
  • Loading branch information
alexcrichton authored Jan 26, 2021
1 parent e594c43 commit 503129a
Show file tree
Hide file tree
Showing 16 changed files with 125 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cranelift/codegen/meta/src/gen_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {

fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
// Generate struct.
fmtln!(fmt, "#[derive(Clone)]");
fmtln!(fmt, "#[derive(Clone, Hash)]");
fmt.doc_comment(format!("Flags group `{}`.", group.name));
fmtln!(fmt, "pub struct Flags {");
fmt.indent(|fmt| {
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/isa/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::result::CodegenResult;
use crate::settings;

use alloc::boxed::Box;
use core::hash::{Hash, Hasher};

use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
Expand Down Expand Up @@ -95,6 +96,10 @@ impl MachBackend for AArch64Backend {
&self.flags
}

fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
}

fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe
}
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/isa/arm32/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::result::CodegenResult;
use crate::settings;

use alloc::boxed::Box;
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Architecture, ArmArchitecture, Triple};

Expand Down Expand Up @@ -90,6 +91,10 @@ impl MachBackend for Arm32Backend {
&self.flags
}

fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
}

fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe
}
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/isa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ use alloc::boxed::Box;
use core::any::Any;
use core::fmt;
use core::fmt::{Debug, Formatter};
use core::hash::Hasher;
use target_lexicon::{triple, Architecture, PointerWidth, Triple};
use thiserror::Error;

Expand Down Expand Up @@ -265,6 +266,10 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
/// Get the ISA-independent flags that were used to make this trait object.
fn flags(&self) -> &settings::Flags;

/// Hashes all flags, both ISA-independent and ISA-specific, into the
/// specified hasher.
fn hash_all_flags(&self, hasher: &mut dyn Hasher);

/// Get the default calling convention of this target.
fn default_call_conv(&self) -> CallConv {
CallConv::triple_default(self.triple())
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/riscv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use alloc::borrow::Cow;
use alloc::boxed::Box;
use core::any::Any;
use core::fmt;
use core::hash::{Hash, Hasher};
use target_lexicon::{PointerWidth, Triple};

#[allow(dead_code)]
Expand Down Expand Up @@ -69,6 +70,11 @@ impl TargetIsa for Isa {
&self.shared_flags
}

fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.shared_flags.hash(&mut hasher);
self.isa_flags.hash(&mut hasher);
}

fn register_info(&self) -> RegInfo {
registers::INFO.clone()
}
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter,
use crate::result::CodegenResult;
use crate::settings::{self as shared_settings, Flags};
use alloc::boxed::Box;
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
use target_lexicon::Triple;

Expand Down Expand Up @@ -82,6 +83,11 @@ impl MachBackend for X64Backend {
&self.flags
}

fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
self.x64_flags.hash(&mut hasher);
}

fn name(&self) -> &'static str {
"x64"
}
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/x86/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use alloc::borrow::Cow;
use alloc::boxed::Box;
use core::any::Any;
use core::fmt;
use core::hash::{Hash, Hasher};
use target_lexicon::{PointerWidth, Triple};

#[allow(dead_code)]
Expand Down Expand Up @@ -78,6 +79,11 @@ impl TargetIsa for Isa {
&self.shared_flags
}

fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.shared_flags.hash(&mut hasher);
self.isa_flags.hash(&mut hasher);
}

fn uses_cpu_flags(&self) -> bool {
true
}
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/machinst/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::regalloc::RegDiversions;
use crate::isa::unwind::systemv::RegisterMappingError;

use core::any::Any;
use core::hash::Hasher;
use std::borrow::Cow;
use std::fmt;
use target_lexicon::Triple;
Expand Down Expand Up @@ -58,6 +59,10 @@ impl TargetIsa for TargetIsaAdapter {
self.backend.flags()
}

fn hash_all_flags(&self, hasher: &mut dyn Hasher) {
self.backend.hash_all_flags(hasher)
}

fn register_info(&self) -> RegInfo {
// Called from function's Display impl, so we need a stub here.
RegInfo {
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/machinst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ use regalloc::{
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
};
use smallvec::{smallvec, SmallVec};
use std::hash::Hasher;
use std::string::String;
use target_lexicon::Triple;

Expand Down Expand Up @@ -373,6 +374,10 @@ pub trait MachBackend {
/// Return flags for this backend.
fn flags(&self) -> &Flags;

/// Hashes all flags, both ISA-independent and ISA-specific, into the
/// specified hasher.
fn hash_all_flags(&self, hasher: &mut dyn Hasher);

/// Return triple for this backend.
fn triple(&self) -> Triple;

Expand Down
2 changes: 1 addition & 1 deletion cranelift/codegen/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ pub type SetResult<T> = Result<T, SetError>;
/// The settings objects themselves are generated and appear in the `isa/*/settings.rs` modules.
/// Each settings object provides a `predicate_view()` method that makes it possible to query
/// ISA predicates by number.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Hash)]
pub struct PredicateView<'a>(&'a [u8]);

impl<'a> PredicateView<'a> {
Expand Down
7 changes: 4 additions & 3 deletions cranelift/native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use raw_cpuid::CpuId;
/// machine, or `Err(())` if the host machine is not supported
/// in the current configuration.
pub fn builder() -> Result<isa::Builder, &'static str> {
builder_with_backend_variant(isa::BackendVariant::Any)
builder_with_options(isa::BackendVariant::Any, true)
}

/// Return an `isa` builder configured for the current host
Expand All @@ -44,8 +44,9 @@ pub fn builder() -> Result<isa::Builder, &'static str> {
/// 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_backend_variant(
pub fn builder_with_options(
variant: isa::BackendVariant,
infer_native_flags: bool,
) -> Result<isa::Builder, &'static str> {
let mut isa_builder =
isa::lookup_variant(Triple::host(), variant).map_err(|err| match err {
Expand All @@ -55,7 +56,7 @@ pub fn builder_with_backend_variant(
isa::LookupError::Unsupported => "unsupported architecture",
})?;

if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
if infer_native_flags && cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
parse_x86_cpuid(&mut isa_builder)?;
}

Expand Down
8 changes: 2 additions & 6 deletions crates/jit/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,10 @@ impl Hash for Compiler {
// misc tunables.
strategy.hash(hasher);
isa.triple().hash(hasher);
features.hash(hasher);
// TODO: if this `to_string()` is too expensive then we should upstream
// a native hashing ability of flags into cranelift itself, but
// compilation and/or cache loading is relatively expensive so seems
// unlikely.
isa.flags().to_string().hash(hasher);
isa.hash_all_flags(hasher);
isa.frontend_config().hash(hasher);
tunables.hash(hasher);
features.hash(hasher);

// Catch accidental bugs of reusing across crate versions.
env!("CARGO_PKG_VERSION").hash(hasher);
Expand Down
5 changes: 5 additions & 0 deletions crates/jit/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ pub fn builder() -> cranelift_codegen::isa::Builder {
cranelift_native::builder().expect("host machine is not a supported target")
}

pub fn builder_without_flags() -> cranelift_codegen::isa::Builder {
cranelift_native::builder_with_options(cranelift_codegen::isa::BackendVariant::Any, false)
.expect("host machine is not a supported target")
}

pub fn call_conv() -> cranelift_codegen::isa::CallConv {
use target_lexicon::HOST;
cranelift_codegen::isa::CallConv::triple_default(&HOST)
Expand Down
14 changes: 14 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,20 @@ impl Config {
self
}

/// Clears native CPU flags inferred from the host.
///
/// By default Wasmtime will tune generated code for the host that Wasmtime
/// itself is running on. If you're compiling on one host, however, and
/// shipping artifacts to another host then this behavior may not be
/// desired. This function will clear all inferred native CPU features.
///
/// To enable CPU features afterwards it's recommended to use the
/// [`Config::cranelift_other_flag`] method.
pub fn cranelift_clear_cpu_flags(&mut self) -> &mut Self {
self.isa_flags = native::builder_without_flags();
self
}

/// Allows settings another Cranelift flag defined by a flag name and value. This allows
/// fine-tuning of Cranelift settings.
///
Expand Down
1 change: 1 addition & 0 deletions tests/all/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod instance;
mod invoke_func_via_table;
mod linker;
mod memory_creator;
mod module;
mod module_linking;
mod module_serialize;
mod name;
Expand Down
54 changes: 54 additions & 0 deletions tests/all/module.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use wasmtime::*;

#[test]
fn caches_across_engines() {
let mut c = Config::new();
c.cranelift_clear_cpu_flags();

let bytes = Module::new(&Engine::new(&c), "(module)")
.unwrap()
.serialize()
.unwrap();

let res = Module::deserialize(
&Engine::new(&Config::new().cranelift_clear_cpu_flags()),
&bytes,
);
assert!(res.is_ok());

// differ in shared cranelift flags
let res = Module::deserialize(
&Engine::new(
&Config::new()
.cranelift_clear_cpu_flags()
.cranelift_nan_canonicalization(true),
),
&bytes,
);
assert!(res.is_err());

// differ in cranelift settings
let res = Module::deserialize(
&Engine::new(
&Config::new()
.cranelift_clear_cpu_flags()
.cranelift_opt_level(OptLevel::None),
),
&bytes,
);
assert!(res.is_err());

// differ in cpu-specific flags
if cfg!(target_arch = "x86_64") {
let res = Module::deserialize(
&Engine::new(unsafe {
&Config::new()
.cranelift_clear_cpu_flags()
.cranelift_other_flag("has_sse3", "true")
.unwrap()
}),
&bytes,
);
assert!(res.is_err());
}
}

0 comments on commit 503129a

Please sign in to comment.