Make use of Lightbeam configurable.

This adds a `--always-lightbeam` option as well as an `--always-cranelift`
option, to allow the compilation strategy to be selected via the
command-line. This also enables regular testing for Lightbeam.
This commit is contained in:
Dan Gohman
2019-10-02 11:54:06 -07:00
parent 8c524815c3
commit 65b8afabe6
9 changed files with 162 additions and 59 deletions

129
build.rs
View File

@@ -14,12 +14,24 @@ fn main() {
let mut out = File::create(out_dir.join("wast_testsuite_tests.rs")) let mut out = File::create(out_dir.join("wast_testsuite_tests.rs"))
.expect("error generating test source file"); .expect("error generating test source file");
test_directory(&mut out, "misc_testsuite").expect("generating tests"); for strategy in &["AlwaysCranelift", "AlwaysLightbeam"] {
test_directory(&mut out, "spec_testsuite").expect("generating tests"); writeln!(out, "#[allow(non_snake_case)]").expect("generating tests");
test_file(&mut out, "spec_testsuite/proposals/simd/simd_const.wast").expect("generating tests"); writeln!(out, "mod {} {{", strategy).expect("generating tests");
test_directory(&mut out, "misc_testsuite", strategy).expect("generating tests");
test_directory(&mut out, "spec_testsuite", strategy).expect("generating tests");
test_file(
&mut out,
"spec_testsuite/proposals/simd/simd_const.wast",
strategy,
)
.expect("generating tests");
writeln!(out, "}}").expect("generating tests");
}
} }
fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> { fn test_directory(out: &mut File, testsuite: &str, strategy: &str) -> io::Result<()> {
let mut dir_entries: Vec<_> = read_dir(testsuite) let mut dir_entries: Vec<_> = read_dir(testsuite)
.expect("reading testsuite directory") .expect("reading testsuite directory")
.map(|r| r.expect("reading testsuite directory entry")) .map(|r| r.expect("reading testsuite directory entry"))
@@ -44,76 +56,127 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> {
dir_entries.sort_by_key(|dir| dir.path()); dir_entries.sort_by_key(|dir| dir.path());
start_test_module(out, testsuite)?;
for dir_entry in dir_entries {
write_testsuite_tests(out, &dir_entry.path(), testsuite, strategy)?;
}
finish_test_module(out)
}
fn test_file(out: &mut File, testfile: &str, strategy: &str) -> io::Result<()> {
let testsuite = "single_file_spec_test";
let path = Path::new(testfile);
start_test_module(out, testsuite)?;
write_testsuite_tests(out, path, testsuite, strategy)?;
finish_test_module(out)
}
fn start_test_module(out: &mut File, testsuite: &str) -> io::Result<()> {
writeln!( writeln!(
out, out,
"mod {} {{", " mod {} {{",
Path::new(testsuite) Path::new(testsuite)
.file_stem() .file_stem()
.expect("testsuite filename should have a stem") .expect("testsuite filename should have a stem")
.to_str() .to_str()
.expect("testsuite filename should be representable as a string") .expect("testsuite filename should be representable as a string")
.replace("-", "_") .replace("-", "_"),
)?; )?;
writeln!( writeln!(
out, out,
" use super::{{native_isa, Path, WastContext, Compiler, Features}};" " use super::super::{{native_isa, Path, WastContext, Compiler, Features, CompilationStrategy}};"
)?; )
for dir_entry in dir_entries {
write_testsuite_tests(out, &dir_entry.path(), testsuite)?;
}
writeln!(out, "}}")?;
Ok(())
} }
fn test_file(out: &mut File, testfile: &str) -> io::Result<()> { fn finish_test_module(out: &mut File) -> io::Result<()> {
let path = Path::new(testfile); writeln!(out, " }}")
write_testsuite_tests(out, path, "single_file_spec_test")
} }
fn write_testsuite_tests(out: &mut File, path: &Path, testsuite: &str) -> io::Result<()> { fn write_testsuite_tests(
out: &mut File,
path: &Path,
testsuite: &str,
strategy: &str,
) -> io::Result<()> {
let stemstr = path let stemstr = path
.file_stem() .file_stem()
.expect("file_stem") .expect("file_stem")
.to_str() .to_str()
.expect("to_str"); .expect("to_str");
writeln!(out, " #[test]")?; writeln!(out, " #[test]")?;
if ignore(testsuite, stemstr) { if ignore(testsuite, stemstr, strategy) {
writeln!(out, " #[ignore]")?; writeln!(out, " #[ignore]")?;
} }
writeln!(out, " fn r#{}() {{", &stemstr.replace("-", "_"))?; writeln!(out, " fn r#{}() {{", &stemstr.replace("-", "_"))?;
writeln!(out, " let isa = native_isa();")?; writeln!(out, " let isa = native_isa();")?;
writeln!(out, " let compiler = Compiler::new(isa);")?;
writeln!( writeln!(
out, out,
" let features = Features {{ simd: true, ..Default::default() }};" " let compiler = Compiler::new(isa, CompilationStrategy::{});",
strategy
)?; )?;
writeln!( writeln!(
out, out,
" let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);" " let features = Features {{ simd: true, ..Default::default() }};"
)?; )?;
writeln!(out, " wast_context")?;
writeln!(out, " .register_spectest()")?;
writeln!( writeln!(
out, out,
" .expect(\"instantiating \\\"spectest\\\"\");" " let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);"
)?; )?;
writeln!(out, " wast_context")?; writeln!(out, " wast_context")?;
write!(out, " .run_file(Path::new(\"")?; writeln!(out, " .register_spectest()")?;
writeln!(
out,
" .expect(\"instantiating \\\"spectest\\\"\");"
)?;
writeln!(out, " wast_context")?;
write!(out, " .run_file(Path::new(\"")?;
// Write out the string with escape_debug to prevent special characters such // Write out the string with escape_debug to prevent special characters such
// as backslash from being reinterpreted. // as backslash from being reinterpreted.
for c in path.display().to_string().chars() { for c in path.display().to_string().chars() {
write!(out, "{}", c.escape_debug())?; write!(out, "{}", c.escape_debug())?;
} }
writeln!(out, "\"))")?; writeln!(out, "\"))")?;
writeln!(out, " .expect(\"error running wast file\");",)?; writeln!(out, " .expect(\"error running wast file\");",)?;
writeln!(out, " }}")?; writeln!(out, " }}")?;
writeln!(out)?; writeln!(out)?;
Ok(()) Ok(())
} }
/// Ignore tests that aren't supported yet. /// Ignore tests that aren't supported yet.
fn ignore(testsuite: &str, name: &str) -> bool { fn ignore(testsuite: &str, name: &str, strategy: &str) -> bool {
match strategy {
#[cfg(feature = "lightbeam")]
"AlwaysLightbeam" => match (testsuite, name) {
("misc_testsuite", "memory_grow")
| ("misc_testsuite", "misc_traps")
| ("single_file_spec_test", "simd_const")
| ("spec_testsuite", "address")
| ("spec_testsuite", "align")
| ("spec_testsuite", "call")
| ("spec_testsuite", "call_indirect")
| ("spec_testsuite", "conversions")
| ("spec_testsuite", "elem")
| ("spec_testsuite", "func_ptrs")
| ("spec_testsuite", "globals")
| ("spec_testsuite", "i32")
| ("spec_testsuite", "i64")
| ("spec_testsuite", "if")
| ("spec_testsuite", "imports")
| ("spec_testsuite", "int_exprs")
| ("spec_testsuite", "linking")
| ("spec_testsuite", "memory_grow")
| ("spec_testsuite", "memory_trap")
| ("spec_testsuite", "select")
| ("spec_testsuite", "traps")
| ("spec_testsuite", "unreachable")
| ("spec_testsuite", "unwind") => return true,
_ => (),
},
"AlwaysCranelift" => {}
_ => panic!("unrecognized strategy"),
}
if cfg!(windows) { if cfg!(windows) {
return match (testsuite, name) { return match (testsuite, name) {
("spec_testsuite", "address") => true, ("spec_testsuite", "address") => true,

View File

@@ -69,7 +69,7 @@ pub fn instantiate(
isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder)) isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))
}; };
let mut context = wasmtime_jit::Context::with_isa(isa); let mut context = wasmtime_jit::Context::with_isa(isa, wasmtime_jit::CompilationStrategy::Auto);
context.set_debug_info(generate_debug_info); context.set_debug_info(generate_debug_info);
let global_exports = context.get_global_exports(); let global_exports = context.get_global_exports();

View File

@@ -34,14 +34,14 @@ use serde::Deserialize;
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use wasmtime_environ::{cache_create_new_config, cache_init}; use wasmtime_environ::{cache_create_new_config, cache_init};
use wasmtime_jit::{Compiler, Features}; use wasmtime_jit::{CompilationStrategy, Compiler, Features};
use wasmtime_wast::WastContext; use wasmtime_wast::WastContext;
const USAGE: &str = " const USAGE: &str = "
Wast test runner. Wast test runner.
Usage: Usage:
wast [-do] [--enable-simd] [--disable-cache | --cache-config=<cache_config_file>] <file>... wast [-do] [--enable-simd] [--disable-cache | --cache-config=<cache_config_file>] [--always-lightmean | --always-cranelift] <file>...
wast --create-cache-config [--cache-config=<cache_config_file>] wast --create-cache-config [--cache-config=<cache_config_file>]
wast --help | --version wast --help | --version
@@ -57,6 +57,8 @@ Options:
creates default configuration and writes it to the disk, creates default configuration and writes it to the disk,
use with --cache-config to specify custom config file use with --cache-config to specify custom config file
instead of default one instead of default one
--always-lightbeam use Lightbeam for all compilation
--always-cranelift use Cranelift for all compilation
-d, --debug enable debug output on stderr/stdout -d, --debug enable debug output on stderr/stdout
--enable-simd enable proposed SIMD instructions --enable-simd enable proposed SIMD instructions
"; ";
@@ -71,6 +73,8 @@ struct Args {
flag_cache_config: Option<String>, flag_cache_config: Option<String>,
flag_create_cache_config: bool, flag_create_cache_config: bool,
flag_enable_simd: bool, flag_enable_simd: bool,
flag_always_lightbeam: bool,
flag_always_cranelift: bool,
} }
fn main() { fn main() {
@@ -148,8 +152,18 @@ fn main() {
features.simd = true; features.simd = true;
} }
// Decide how to compile.
let strategy = match (args.flag_always_lightbeam, args.flag_always_cranelift) {
(true, false) => CompilationStrategy::AlwaysLightbeam,
(false, true) => CompilationStrategy::AlwaysCranelift,
(false, false) => CompilationStrategy::Auto,
(true, true) => {
panic!("Can't enable --always-cranelift and --always-lightbeam at the same time")
}
};
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let engine = Compiler::new(isa); let engine = Compiler::new(isa, strategy);
let mut wast_context = WastContext::new(Box::new(engine)).with_features(features); let mut wast_context = WastContext::new(Box::new(engine)).with_features(features);
wast_context wast_context

View File

@@ -8,7 +8,7 @@ use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use wabt; use wabt;
use wasmtime_jit::{instantiate, Compiler, NullResolver}; use wasmtime_jit::{instantiate, CompilationStrategy, Compiler, NullResolver};
#[cfg(test)] #[cfg(test)]
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat"; const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
@@ -40,7 +40,7 @@ fn test_environ_translate() {
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let mut resolver = NullResolver {}; let mut resolver = NullResolver {};
let mut compiler = Compiler::new(isa); let mut compiler = Compiler::new(isa, CompilationStrategy::Auto);
let global_exports = Rc::new(RefCell::new(HashMap::new())); let global_exports = Rc::new(RefCell::new(HashMap::new()));
let instance = instantiate(&mut compiler, &data, &mut resolver, global_exports, false); let instance = instantiate(&mut compiler, &data, &mut resolver, global_exports, false);
assert!(instance.is_ok()); assert!(instance.is_ok());

View File

@@ -4,7 +4,7 @@ use cranelift_codegen::isa;
use cranelift_codegen::settings; use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use std::path::Path; use std::path::Path;
use wasmtime_jit::{Compiler, Features}; use wasmtime_jit::{CompilationStrategy, Compiler, Features};
use wasmtime_wast::WastContext; use wasmtime_wast::WastContext;
include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs")); include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs"));

View File

@@ -2,7 +2,7 @@ use std::cell::{RefCell, RefMut};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::rc::Rc; use std::rc::Rc;
use wasmtime_jit::{Compiler, Features}; use wasmtime_jit::{CompilationStrategy, Compiler, Features};
use cranelift_codegen::settings; use cranelift_codegen::settings;
@@ -59,5 +59,5 @@ pub(crate) fn create_compiler(flags: settings::Flags) -> Compiler {
isa_builder.finish(flags) isa_builder.finish(flags)
}; };
Compiler::new(isa) Compiler::new(isa, CompilationStrategy::Auto)
} }

View File

@@ -25,6 +25,20 @@ use wasmtime_runtime::{
VMFunctionBody, VMFunctionBody,
}; };
/// Select which kind of compilation to use.
#[derive(Copy, Clone, Debug)]
pub enum CompilationStrategy {
/// Let Wasmtime pick the strategy.
Auto,
/// Compile all functions with Cranelift.
AlwaysCranelift,
/// Compile all functions with Lightbeam.
#[cfg(feature = "lightbeam")]
AlwaysLightbeam,
}
/// A WebAssembly code JIT compiler. /// A WebAssembly code JIT compiler.
/// ///
/// A `Compiler` instance owns the executable memory that it allocates. /// A `Compiler` instance owns the executable memory that it allocates.
@@ -40,6 +54,7 @@ pub struct Compiler {
trap_registration_guards: Vec<TrapRegistrationGuard>, trap_registration_guards: Vec<TrapRegistrationGuard>,
trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>, trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
signatures: SignatureRegistry, signatures: SignatureRegistry,
strategy: CompilationStrategy,
/// The `FunctionBuilderContext`, shared between trampline function compilations. /// The `FunctionBuilderContext`, shared between trampline function compilations.
fn_builder_ctx: FunctionBuilderContext, fn_builder_ctx: FunctionBuilderContext,
@@ -47,7 +62,7 @@ pub struct Compiler {
impl Compiler { impl Compiler {
/// Construct a new `Compiler`. /// Construct a new `Compiler`.
pub fn new(isa: Box<dyn TargetIsa>) -> Self { pub fn new(isa: Box<dyn TargetIsa>, strategy: CompilationStrategy) -> Self {
Self { Self {
isa, isa,
code_memory: CodeMemory::new(), code_memory: CodeMemory::new(),
@@ -55,6 +70,7 @@ impl Compiler {
trampoline_park: HashMap::new(), trampoline_park: HashMap::new(),
signatures: SignatureRegistry::new(), signatures: SignatureRegistry::new(),
fn_builder_ctx: FunctionBuilderContext::new(), fn_builder_ctx: FunctionBuilderContext::new(),
strategy,
} }
} }
} }
@@ -73,11 +89,6 @@ impl Drop for Compiler {
} }
} }
#[cfg(feature = "lightbeam")]
type DefaultCompiler = wasmtime_environ::lightbeam::Lightbeam;
#[cfg(not(feature = "lightbeam"))]
type DefaultCompiler = wasmtime_environ::cranelift::Cranelift;
impl Compiler { impl Compiler {
/// Return the target's frontend configuration settings. /// Return the target's frontend configuration settings.
pub fn frontend_config(&self) -> TargetFrontendConfig { pub fn frontend_config(&self) -> TargetFrontendConfig {
@@ -105,12 +116,27 @@ impl Compiler {
SetupError, SetupError,
> { > {
let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) = let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) =
DefaultCompiler::compile_module( match self.strategy {
module, // For now, interpret `Auto` as `AlwaysCranelift` since that's the most stable
function_body_inputs, // implementation.
&*self.isa, CompilationStrategy::Auto | CompilationStrategy::AlwaysCranelift => {
debug_data.is_some(), wasmtime_environ::cranelift::Cranelift::compile_module(
) module,
function_body_inputs,
&*self.isa,
debug_data.is_some(),
)
}
#[cfg(feature = "lightbeam")]
CompilationStrategy::AlwaysLightbeam => {
wasmtime_environ::lightbeam::Lightbeam::compile_module(
module,
function_body_inputs,
&*self.isa,
debug_data.is_some(),
)
}
}
.map_err(SetupError::Compile)?; .map_err(SetupError::Compile)?;
let allocated_functions = let allocated_functions =

View File

@@ -1,7 +1,7 @@
use crate::action::{get, inspect_memory, invoke}; use crate::action::{get, inspect_memory, invoke};
use crate::{ use crate::{
instantiate, ActionError, ActionOutcome, Compiler, InstanceHandle, Namespace, RuntimeValue, instantiate, ActionError, ActionOutcome, CompilationStrategy, Compiler, InstanceHandle,
SetupError, Namespace, RuntimeValue, SetupError,
}; };
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use std::boxed::Box; use std::boxed::Box;
@@ -103,8 +103,8 @@ impl Context {
} }
/// Construct a new instance of `Context` with the given target. /// Construct a new instance of `Context` with the given target.
pub fn with_isa(isa: Box<dyn TargetIsa>) -> Self { pub fn with_isa(isa: Box<dyn TargetIsa>, strategy: CompilationStrategy) -> Self {
Self::new(Box::new(Compiler::new(isa))) Self::new(Box::new(Compiler::new(isa, strategy)))
} }
/// Retrieve the context features /// Retrieve the context features

View File

@@ -49,7 +49,7 @@ mod resolver;
mod target_tunables; mod target_tunables;
pub use crate::action::{ActionError, ActionOutcome, RuntimeValue}; pub use crate::action::{ActionError, ActionOutcome, RuntimeValue};
pub use crate::compiler::Compiler; pub use crate::compiler::{CompilationStrategy, Compiler};
pub use crate::context::{Context, ContextError, Features, UnknownInstance}; pub use crate::context::{Context, ContextError, Features, UnknownInstance};
pub use crate::instantiate::{instantiate, CompiledModule, SetupError}; pub use crate::instantiate::{instantiate, CompiledModule, SetupError};
pub use crate::link::link_module; pub use crate::link::link_module;