Remove dependency on TargetIsa from Wasmtime crates (#3178)

This commit started off by deleting the `cranelift_codegen::settings`
reexport in the `wasmtime-environ` crate and then basically played
whack-a-mole until everything compiled again. The main result of this is
that the `wasmtime-*` family of crates have generally less of a
dependency on the `TargetIsa` trait and type from Cranelift. While the
dependency isn't entirely severed yet this is at least a significant
start.

This commit is intended to be largely refactorings, no functional
changes are intended here. The refactorings are:

* A `CompilerBuilder` trait has been added to `wasmtime_environ` which
  server as an abstraction used to create compilers and configure them
  in a uniform fashion. The `wasmtime::Config` type now uses this
  instead of cranelift-specific settings. The `wasmtime-jit` crate
  exports the ability to create a compiler builder from a
  `CompilationStrategy`, which only works for Cranelift right now. In a
  cranelift-less build of Wasmtime this is expected to return a trait
  object that fails all requests to compile.

* The `Compiler` trait in the `wasmtime_environ` crate has been souped
  up with a number of methods that Wasmtime and other crates needed.

* The `wasmtime-debug` crate is now moved entirely behind the
  `wasmtime-cranelift` crate.

* The `wasmtime-cranelift` crate is now only depended on by the
  `wasmtime-jit` crate.

* Wasm types in `cranelift-wasm` no longer contain their IR type,
  instead they only contain the `WasmType`. This is required to get
  everything to align correctly but will also be required in a future
  refactoring where the types used by `cranelift-wasm` will be extracted
  to a separate crate.

* I moved around a fair bit of code in `wasmtime-cranelift`.

* Some gdb-specific jit-specific code has moved from `wasmtime-debug` to
  `wasmtime-jit`.
This commit is contained in:
Alex Crichton
2021-08-16 09:55:39 -05:00
committed by GitHub
parent 7c0948fe0b
commit 0313e30d76
47 changed files with 1529 additions and 1384 deletions

View File

@@ -13,7 +13,6 @@ edition = "2018"
rustdoc-args = ["--cfg", "nightlydoc"]
[dependencies]
cranelift-native = { path = '../../cranelift/native', version = '0.76.0' }
wasmtime-runtime = { path = "../runtime", version = "0.29.0" }
wasmtime-environ = { path = "../environ", version = "0.29.0" }
wasmtime-jit = { path = "../jit", version = "0.29.0" }

View File

@@ -10,8 +10,7 @@ use std::sync::Arc;
use wasmparser::WasmFeatures;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
use wasmtime_environ::settings::{self, Configurable, SetError};
use wasmtime_environ::{isa, isa::TargetIsa, Tunables};
use wasmtime_environ::{CompilerBuilder, Tunables};
use wasmtime_jit::{CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{
@@ -337,12 +336,9 @@ impl Default for InstanceAllocationStrategy {
///
/// This structure exposed a builder-like interface and is primarily consumed by
/// [`Engine::new()`](crate::Engine::new)
#[derive(Clone)]
pub struct Config {
pub(crate) flags: settings::Builder,
pub(crate) isa_flags: isa::Builder,
pub(crate) compiler: Box<dyn CompilerBuilder>,
pub(crate) tunables: Tunables,
pub(crate) strategy: CompilationStrategy,
#[cfg(feature = "cache")]
pub(crate) cache_config: CacheConfig,
pub(crate) profiler: Arc<dyn ProfilingAgent>,
@@ -362,24 +358,9 @@ impl Config {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
let mut flags = settings::builder();
// There are two possible traps for division, and this way
// we get the proper one if code traps.
flags
.enable("avoid_div_traps")
.expect("should be valid flag");
// We don't use probestack as a stack limit mechanism
flags
.set("enable_probestack", "false")
.expect("should be valid flag");
let mut ret = Self {
tunables: Tunables::default(),
flags,
isa_flags: cranelift_native::builder().expect("host machine is not a supported target"),
strategy: CompilationStrategy::Auto,
compiler: Compiler::builder(CompilationStrategy::Auto),
#[cfg(feature = "cache")]
cache_config: CacheConfig::new_cache_disabled(),
profiler: Arc::new(NullProfilerAgent),
@@ -417,9 +398,8 @@ impl Config {
/// This method will error if the given target triple is not supported.
pub fn target(&mut self, target: &str) -> Result<&mut Self> {
use std::str::FromStr;
self.isa_flags = wasmtime_environ::isa::lookup(
target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?,
)?;
self.compiler
.target(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?)?;
Ok(self)
}
@@ -675,7 +655,7 @@ impl Config {
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.features.reference_types = enable;
self.flags
self.compiler
.set("enable_safepoints", if enable { "true" } else { "false" })
.unwrap();
@@ -710,7 +690,7 @@ impl Config {
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.features.simd = enable;
let val = if enable { "true" } else { "false" };
self.flags
self.compiler
.set("enable_simd", val)
.expect("should be valid flag");
self
@@ -801,7 +781,7 @@ impl Config {
/// itself to be set, but if they're not set and the strategy is specified
/// here then an error will be returned.
pub fn strategy(&mut self, strategy: Strategy) -> Result<&mut Self> {
self.strategy = match strategy {
let strategy = match strategy {
Strategy::Auto => CompilationStrategy::Auto,
Strategy::Cranelift => CompilationStrategy::Cranelift,
#[cfg(feature = "lightbeam")]
@@ -811,6 +791,7 @@ impl Config {
anyhow::bail!("lightbeam compilation strategy wasn't enabled at compile time");
}
};
self.compiler = Compiler::builder(strategy);
Ok(self)
}
@@ -837,7 +818,7 @@ impl Config {
/// The default value for this is `false`
pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
self.flags
self.compiler
.set("enable_verifier", val)
.expect("should be valid flag");
self
@@ -856,7 +837,7 @@ impl Config {
OptLevel::Speed => "speed",
OptLevel::SpeedAndSize => "speed_and_size",
};
self.flags
self.compiler
.set("opt_level", val)
.expect("should be valid flag");
self
@@ -872,7 +853,7 @@ impl Config {
/// The default value for this is `false`
pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
self.flags
self.compiler
.set("enable_nan_canonicalization", val)
.expect("should be valid flag");
self
@@ -893,15 +874,7 @@ impl Config {
///
/// This method can fail if the flag's name does not exist.
pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> Result<&mut Self> {
if let Err(err) = self.flags.enable(flag) {
match err {
SetError::BadName(_) => {
// Try the target-specific flags.
self.isa_flags.enable(flag)?;
}
_ => bail!(err),
}
}
self.compiler.enable(flag)?;
Ok(self)
}
@@ -919,15 +892,7 @@ impl Config {
/// This method can fail if the flag's name does not exist, or the value is not appropriate for
/// the flag type.
pub unsafe fn cranelift_flag_set(&mut self, name: &str, value: &str) -> Result<&mut Self> {
if let Err(err) = self.flags.set(name, value) {
match err {
SetError::BadName(_) => {
// Try the target-specific flags.
self.isa_flags.set(name, value)?;
}
_ => bail!(err),
}
}
self.compiler.set(name, value)?;
Ok(self)
}
@@ -1238,25 +1203,11 @@ impl Config {
self
}
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
self.isa_flags
.clone()
.finish(settings::Flags::new(self.flags.clone()))
}
pub(crate) fn target_isa_with_reference_types(&self) -> Box<dyn TargetIsa> {
let mut flags = self.flags.clone();
flags.set("enable_safepoints", "true").unwrap();
self.isa_flags.clone().finish(settings::Flags::new(flags))
}
pub(crate) fn build_compiler(&self, allocator: &dyn InstanceAllocator) -> Compiler {
let isa = self.target_isa();
let mut tunables = self.tunables.clone();
allocator.adjust_tunables(&mut tunables);
Compiler::new(
isa,
self.strategy,
&*self.compiler,
tunables,
self.features,
self.parallel_compilation,
@@ -1304,12 +1255,33 @@ impl Default for Config {
}
}
impl Clone for Config {
fn clone(&self) -> Config {
Config {
compiler: self.compiler.clone(),
tunables: self.tunables.clone(),
#[cfg(feature = "cache")]
cache_config: self.cache_config.clone(),
profiler: self.profiler.clone(),
features: self.features.clone(),
mem_creator: self.mem_creator.clone(),
allocation_strategy: self.allocation_strategy.clone(),
max_wasm_stack: self.max_wasm_stack,
wasm_backtrace_details_env_used: self.wasm_backtrace_details_env_used,
async_support: self.async_support,
#[cfg(feature = "async")]
async_stack_size: self.async_stack_size,
deserialize_check_wasmtime_version: self.deserialize_check_wasmtime_version,
parallel_compilation: self.parallel_compilation,
}
}
}
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Config")
.field("debug_info", &self.tunables.generate_native_debuginfo)
.field("parse_wasm_debuginfo", &self.tunables.parse_wasm_debuginfo)
.field("strategy", &self.strategy)
.field("wasm_threads", &self.features.threads)
.field("wasm_reference_types", &self.features.reference_types)
.field("wasm_bulk_memory", &self.features.bulk_memory)
@@ -1333,10 +1305,8 @@ impl fmt::Debug for Config {
"guard_before_linear_memory",
&self.tunables.guard_before_linear_memory,
)
.field(
"flags",
&settings::Flags::new(self.flags.clone()).to_string(),
)
.field("parallel_compilation", &self.parallel_compilation)
.field("compiler", &self.compiler)
.finish()
}
}

View File

@@ -279,7 +279,7 @@ impl Module {
/// ```
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
// Check to see that the config's target matches the host
let target = engine.config().isa_flags.triple();
let target = engine.compiler().compiler().triple();
if *target != target_lexicon::Triple::host() {
bail!(
"target '{}' specified in the configuration does not match the host",
@@ -318,9 +318,8 @@ impl Module {
let modules = CompiledModule::from_artifacts_list(
artifacts,
engine.compiler().isa(),
&*engine.config().profiler,
engine.compiler(),
&*engine.config().profiler,
)?;
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])

View File

@@ -1,18 +1,14 @@
//! Implements module serialization.
use crate::{Engine, Module, OptLevel};
use crate::{Engine, Module};
use anyhow::{anyhow, bail, Context, Result};
use bincode::Options;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use std::{collections::HashMap, fmt::Display};
use wasmtime_environ::{isa::TargetIsa, settings, Tunables};
use wasmtime_jit::{
CompilationArtifacts, CompilationStrategy, CompiledModule, Compiler, TypeTables,
};
use wasmtime_environ::{FlagValue, Tunables};
use wasmtime_jit::{CompilationArtifacts, CompiledModule, Compiler, TypeTables};
const HEADER: &[u8] = b"\0wasmtime-aot";
@@ -111,16 +107,6 @@ impl<'a, 'b, T: Deserialize<'a>> Deserialize<'a> for MyCow<'b, T> {
}
}
impl From<settings::OptLevel> for OptLevel {
fn from(level: settings::OptLevel) -> Self {
match level {
settings::OptLevel::Speed => OptLevel::Speed,
settings::OptLevel::SpeedAndSize => OptLevel::SpeedAndSize,
settings::OptLevel::None => OptLevel::None,
}
}
}
/// A small helper struct for serialized module upvars.
#[derive(Serialize, Deserialize)]
pub struct SerializedModuleUpvar {
@@ -163,40 +149,11 @@ impl SerializedModuleUpvar {
}
}
#[derive(Serialize, Deserialize, Eq, PartialEq)]
enum FlagValue {
Enum(Cow<'static, str>),
Num(u8),
Bool(bool),
}
impl From<settings::Value> for FlagValue {
fn from(v: settings::Value) -> Self {
match v.kind() {
settings::SettingKind::Enum => Self::Enum(v.as_enum().unwrap().into()),
settings::SettingKind::Num => Self::Num(v.as_num().unwrap()),
settings::SettingKind::Bool => Self::Bool(v.as_bool().unwrap()),
settings::SettingKind::Preset => unreachable!(),
}
}
}
impl Display for FlagValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Enum(v) => v.fmt(f),
Self::Num(v) => v.fmt(f),
Self::Bool(v) => v.fmt(f),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct SerializedModule<'a> {
target: String,
shared_flags: HashMap<String, FlagValue>,
isa_flags: HashMap<String, FlagValue>,
strategy: CompilationStrategy,
tunables: Tunables,
features: WasmFeatures,
artifacts: Vec<MyCow<'a, CompilationArtifacts>>,
@@ -250,21 +207,10 @@ impl<'a> SerializedModule<'a> {
module_upvars: Vec<SerializedModuleUpvar>,
types: MyCow<'a, TypeTables>,
) -> Self {
let isa = compiler.isa();
Self {
target: isa.triple().to_string(),
shared_flags: isa
.flags()
.iter()
.map(|v| (v.name.to_owned(), v.into()))
.collect(),
isa_flags: isa
.isa_flags()
.into_iter()
.map(|v| (v.name.to_owned(), v.into()))
.collect(),
strategy: compiler.strategy(),
target: compiler.triple().to_string(),
shared_flags: compiler.compiler().flags(),
isa_flags: compiler.compiler().isa_flags(),
tunables: compiler.tunables().clone(),
features: compiler.features().into(),
artifacts,
@@ -275,12 +221,10 @@ impl<'a> SerializedModule<'a> {
pub fn into_module(mut self, engine: &Engine) -> Result<Module> {
let compiler = engine.compiler();
let isa = compiler.isa();
self.check_triple(isa)?;
self.check_shared_flags(isa)?;
self.check_isa_flags(isa)?;
self.check_strategy(compiler)?;
self.check_triple(compiler)?;
self.check_shared_flags(compiler)?;
self.check_isa_flags(compiler)?;
self.check_tunables(compiler)?;
self.check_features(compiler)?;
@@ -289,9 +233,8 @@ impl<'a> SerializedModule<'a> {
.into_iter()
.map(|i| i.unwrap_owned())
.collect(),
engine.compiler().isa(),
&*engine.config().profiler,
engine.compiler(),
&*engine.config().profiler,
)?;
assert!(!modules.is_empty());
@@ -361,17 +304,17 @@ impl<'a> SerializedModule<'a> {
.context("deserialize compilation artifacts")?)
}
fn check_triple(&self, isa: &dyn TargetIsa) -> Result<()> {
fn check_triple(&self, compiler: &Compiler) -> Result<()> {
let triple = target_lexicon::Triple::from_str(&self.target).map_err(|e| anyhow!(e))?;
if triple.architecture != isa.triple().architecture {
if triple.architecture != compiler.triple().architecture {
bail!(
"Module was compiled for architecture '{}'",
triple.architecture
);
}
if triple.operating_system != isa.triple().operating_system {
if triple.operating_system != compiler.triple().operating_system {
bail!(
"Module was compiled for operating system '{}'",
triple.operating_system
@@ -381,13 +324,11 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
fn check_shared_flags(&mut self, isa: &dyn TargetIsa) -> Result<()> {
fn check_shared_flags(&mut self, compiler: &Compiler) -> Result<()> {
let mut shared_flags = std::mem::take(&mut self.shared_flags);
for value in isa.flags().iter() {
let name = value.name;
match shared_flags.remove(name) {
for (name, host) in compiler.compiler().flags() {
match shared_flags.remove(&name) {
Some(v) => {
let host: FlagValue = value.into();
if v != host {
bail!("Module was compiled with a different '{}' setting: expected '{}' but host has '{}'", name, v, host);
}
@@ -406,12 +347,10 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
fn check_isa_flags(&mut self, isa: &dyn TargetIsa) -> Result<()> {
fn check_isa_flags(&mut self, compiler: &Compiler) -> Result<()> {
let mut isa_flags = std::mem::take(&mut self.isa_flags);
for value in isa.isa_flags().into_iter() {
let name = value.name;
let host: FlagValue = value.into();
match isa_flags.remove(name) {
for (name, host) in compiler.compiler().isa_flags() {
match isa_flags.remove(&name) {
Some(v) => match (&v, &host) {
(FlagValue::Bool(v), FlagValue::Bool(host)) => {
// ISA flags represent CPU features; for boolean values, only
@@ -441,25 +380,6 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
fn check_strategy(&self, compiler: &Compiler) -> Result<()> {
#[allow(unreachable_patterns)]
let matches = match (self.strategy, compiler.strategy()) {
(CompilationStrategy::Auto, CompilationStrategy::Auto)
| (CompilationStrategy::Auto, CompilationStrategy::Cranelift)
| (CompilationStrategy::Cranelift, CompilationStrategy::Auto)
| (CompilationStrategy::Cranelift, CompilationStrategy::Cranelift) => true,
#[cfg(feature = "lightbeam")]
(CompilationStrategy::Lightbeam, CompilationStrategy::Lightbeam) => true,
_ => false,
};
if !matches {
bail!("Module was compiled with strategy '{:?}'", self.strategy);
}
Ok(())
}
fn check_int<T: Eq + std::fmt::Display>(found: T, expected: T, feature: &str) -> Result<()> {
if found == expected {
return Ok(());
@@ -610,6 +530,7 @@ impl<'a> SerializedModule<'a> {
mod test {
use super::*;
use crate::Config;
use std::borrow::Cow;
#[test]
fn test_architecture_mismatch() -> Result<()> {

View File

@@ -77,19 +77,14 @@ pub fn create_function(
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync>,
engine: &Engine,
) -> Result<(InstanceHandle, VMTrampoline)> {
// Note that we specifically enable reference types here in our ISA because
// `Func::new` is intended to be infallible, but our signature may use
// reference types which requires safepoints.
let isa = &*engine.config().target_isa_with_reference_types();
let wasm_trampoline = engine.compiler().compiler().wasm_to_host_trampoline(
isa,
ft.as_wasm_func_type(),
stub_fn as usize,
)?;
let wasm_trampoline = engine
.compiler()
.compiler()
.wasm_to_host_trampoline(ft.as_wasm_func_type(), stub_fn as usize)?;
let host_trampoline = engine
.compiler()
.compiler()
.host_to_wasm_trampoline(isa, ft.as_wasm_func_type())?;
.host_to_wasm_trampoline(ft.as_wasm_func_type())?;
let mut code_memory = CodeMemory::new();
let host_trampoline = code_memory
@@ -98,7 +93,7 @@ pub fn create_function(
let wasm_trampoline =
code_memory.allocate_for_function(&wasm_trampoline)? as *mut [VMFunctionBody];
code_memory.publish(isa);
code_memory.publish(engine.compiler());
let sig = engine.signatures().register(ft.as_wasm_func_type());

View File

@@ -17,7 +17,6 @@ pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) ->
let global = wasm::Global {
wasm_ty: gt.content().to_wasm_type(),
ty: gt.content().get_wasmtime_type(),
mutability: match gt.mutability() {
Mutability::Const => false,
Mutability::Var => true,

View File

@@ -1,6 +1,6 @@
use std::fmt;
use wasmtime_environ::wasm;
use wasmtime_environ::wasm::{EntityType, WasmFuncType};
use wasmtime_environ::{ir, wasm};
use wasmtime_jit::TypeTables;
pub(crate) mod matching;
@@ -71,18 +71,6 @@ impl ValType {
}
}
pub(crate) fn get_wasmtime_type(&self) -> ir::Type {
match self {
ValType::I32 => ir::types::I32,
ValType::I64 => ir::types::I64,
ValType::F32 => ir::types::F32,
ValType::F64 => ir::types::F64,
ValType::V128 => ir::types::I8X16,
ValType::ExternRef => wasmtime_runtime::ref_type(),
ValType::FuncRef => wasmtime_runtime::pointer_type(),
}
}
pub(crate) fn to_wasm_type(&self) -> wasm::WasmType {
match self {
Self::I32 => wasm::WasmType::I32,
@@ -334,10 +322,6 @@ impl TableType {
pub fn new(element: ValType, min: u32, max: Option<u32>) -> TableType {
TableType {
ty: wasm::Table {
ty: match element {
ValType::FuncRef => wasm::TableElementType::Func,
_ => wasm::TableElementType::Val(element.get_wasmtime_type()),
},
wasm_ty: element.to_wasm_type(),
minimum: min,
maximum: max,

View File

@@ -22,10 +22,7 @@ impl MatchCx<'_> {
}
fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> {
if expected.ty == actual.ty
&& expected.wasm_ty == actual.wasm_ty
&& expected.mutability == actual.mutability
{
if expected.wasm_ty == actual.wasm_ty && expected.mutability == actual.mutability {
Ok(())
} else {
bail!("global types incompatible")
@@ -38,7 +35,6 @@ impl MatchCx<'_> {
fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> {
if expected.wasm_ty == actual.wasm_ty
&& expected.ty == actual.ty
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {