Add the ability to pass backend specific options through CompilerConfig.

Use this to replace wasmer_llvm_backend::GLOBAL_OPTIONS.
This commit is contained in:
Nick Lewycky
2019-12-04 10:38:28 -08:00
parent af7a368320
commit 8d3cf874cd
5 changed files with 95 additions and 46 deletions

View File

@ -2,6 +2,7 @@ use super::stackmap::StackmapRegistry;
use crate::{
intrinsics::Intrinsics,
structs::{Callbacks, LLVMModule, LLVMResult, MemProtect},
LLVMCallbacks,
};
use inkwell::{
memory_buffer::MemoryBuffer,
@ -13,8 +14,6 @@ use std::{
any::Any,
cell::RefCell,
ffi::{c_void, CString},
fs::File,
io::Write,
mem,
ops::Deref,
ptr::{self, NonNull},
@ -176,23 +175,22 @@ impl LLVMBackend {
_stackmaps: &StackmapRegistry,
_module_info: &ModuleInfo,
target_machine: &TargetMachine,
llvm_callbacks: &Option<Rc<RefCell<dyn LLVMCallbacks>>>,
) -> (Self, LLVMCache) {
let memory_buffer = target_machine
.write_to_memory_buffer(&module.borrow_mut(), FileType::Object)
.unwrap();
let mem_buf_slice = memory_buffer.as_slice();
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.obj_file } {
let mut file = File::create(path).unwrap();
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
if let Some(callbacks) = llvm_callbacks {
callbacks
.borrow_mut()
.obj_memory_buffer_callback(&memory_buffer);
}
let callbacks = get_callbacks();
let mut module: *mut LLVMModule = ptr::null_mut();
let mem_buf_slice = memory_buffer.as_slice();
let res = unsafe {
module_load(
mem_buf_slice.as_ptr(),

View File

@ -5,6 +5,7 @@ use crate::{
stackmap::{StackmapEntry, StackmapEntryKind, StackmapRegistry, ValueSemantic},
state::{ControlFrame, ExtraInfo, IfElseState, State},
trampolines::generate_trampolines,
LLVMBackendConfig, LLVMCallbacks,
};
use inkwell::{
builder::Builder,
@ -877,6 +878,7 @@ pub struct LLVMModuleCodeGenerator<'ctx> {
stackmaps: Rc<RefCell<StackmapRegistry>>,
track_state: bool,
target_machine: TargetMachine,
llvm_callbacks: Option<Rc<RefCell<dyn LLVMCallbacks>>>,
}
pub struct LLVMFunctionCodeGenerator<'ctx> {
@ -8513,6 +8515,7 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
stackmaps: Rc::new(RefCell::new(StackmapRegistry::default())),
track_state: false,
target_machine,
llvm_callbacks: None,
}
}
@ -8654,8 +8657,10 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
message: format!("trampolines generation error: {:?}", e),
})?;
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.pre_opt_ir } {
self.module.borrow_mut().print_to_file(path).unwrap();
if let Some(ref mut callbacks) = self.llvm_callbacks {
callbacks
.borrow_mut()
.preopt_ir_callback(&*self.module.borrow_mut());
}
let pass_manager = PassManager::create(());
@ -8695,8 +8700,10 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
pass_manager.add_early_cse_pass();
pass_manager.run_on(&*self.module.borrow_mut());
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.post_opt_ir } {
self.module.borrow_mut().print_to_file(path).unwrap();
if let Some(ref mut callbacks) = self.llvm_callbacks {
callbacks
.borrow_mut()
.postopt_ir_callback(&*self.module.borrow_mut());
}
let stackmaps = self.stackmaps.borrow();
@ -8707,12 +8714,18 @@ impl<'ctx> ModuleCodeGenerator<LLVMFunctionCodeGenerator<'ctx>, LLVMBackend, Cod
&*stackmaps,
module_info,
&self.target_machine,
&mut self.llvm_callbacks,
);
Ok((backend, Box::new(cache_gen)))
}
fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> {
self.track_state = config.track_state;
if let Some(backend_compiler_config) = &config.backend_specific_config {
if let Some(llvm_config) = backend_compiler_config.get_specific::<LLVMBackendConfig>() {
self.llvm_callbacks = llvm_config.callbacks.clone();
}
}
Ok(())
}

View File

@ -21,8 +21,6 @@ mod state;
mod structs;
mod trampolines;
use std::path::PathBuf;
pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator;
pub use code::LLVMModuleCodeGenerator as ModuleCodeGenerator;
@ -35,21 +33,15 @@ pub type LLVMCompiler = SimpleStreamingCompilerGen<
code::CodegenError,
>;
#[derive(Debug, Clone)]
/// LLVM backend flags.
pub struct LLVMOptions {
/// Emit LLVM IR before optimization pipeline.
pub pre_opt_ir: Option<PathBuf>,
pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer;
/// Emit LLVM IR after optimization pipeline.
pub post_opt_ir: Option<PathBuf>,
/// Emit LLVM generated native code object file.
pub obj_file: Option<PathBuf>,
pub trait LLVMCallbacks: std::any::Any + 'static {
fn preopt_ir_callback(&mut self, module: &InkwellModule);
fn postopt_ir_callback(&mut self, module: &InkwellModule);
fn obj_memory_buffer_callback(&mut self, memory_buffer: &InkwellMemoryBuffer);
}
pub static mut GLOBAL_OPTIONS: LLVMOptions = LLVMOptions {
pre_opt_ir: None,
post_opt_ir: None,
obj_file: None,
};
pub struct LLVMBackendConfig {
pub callbacks: Option<std::rc::Rc<std::cell::RefCell<dyn LLVMCallbacks>>>,
}

View File

@ -200,6 +200,19 @@ pub struct Features {
pub threads: bool,
}
/// Use this to point to a compiler config struct provided by the backend.
/// The backend struct must support runtime reflection with `Any`, which is any
/// struct that does not contain a non-`'static` reference.
#[derive(Debug)]
pub struct BackendCompilerConfig(pub Box<dyn Any + 'static>);
impl BackendCompilerConfig {
/// Obtain the backend-specific compiler config struct.
pub fn get_specific<T: 'static>(&self) -> Option<&T> {
self.0.downcast_ref::<T>()
}
}
/// Configuration data for the compiler
#[derive(Debug, Default)]
pub struct CompilerConfig {
@ -210,10 +223,12 @@ pub struct CompilerConfig {
pub track_state: bool,
pub features: Features,
// target info used by LLVM
// Target info. Presently only supported by LLVM.
pub triple: Option<String>,
pub cpu_name: Option<String>,
pub cpu_features: Option<String>,
pub backend_specific_config: Option<BackendCompilerConfig>,
}
pub trait Compiler {

View File

@ -9,6 +9,7 @@
)]
extern crate structopt;
use std::collections::HashMap;
use std::env;
use std::fs::{metadata, read_to_string, File};
use std::io;
@ -17,14 +18,15 @@ use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use std::collections::HashMap;
use structopt::{clap, StructOpt};
use wasmer::*;
#[cfg(feature = "backend-cranelift")]
use wasmer_clif_backend::CraneliftCompiler;
#[cfg(feature = "backend-llvm")]
use wasmer_llvm_backend::{LLVMCompiler, LLVMOptions};
use wasmer_llvm_backend::{
InkwellMemoryBuffer, InkwellModule, LLVMBackendConfig, LLVMCallbacks, LLVMCompiler,
};
use wasmer_runtime::{
cache::{Cache as BaseCache, FileSystemCache, WasmHash},
Value, VERSION,
@ -40,6 +42,11 @@ use wasmer_runtime_core::{
#[cfg(feature = "wasi")]
use wasmer_wasi;
#[cfg(feature = "backend-llvm")]
use std::{cell::RefCell, io::Write, rc::Rc};
#[cfg(feature = "backend-llvm")]
use wasmer_runtime_core::backend::BackendCompilerConfig;
#[derive(Debug, StructOpt)]
#[structopt(name = "wasmer", about = "Wasm execution runtime.", author)]
/// The options for the wasmer Command Line Interface
@ -486,6 +493,32 @@ fn execute_wasi(
Ok(())
}
#[cfg(feature = "backend-llvm")]
impl LLVMCallbacks for LLVMCLIOptions {
fn preopt_ir_callback(&mut self, module: &InkwellModule) {
if let Some(filename) = &self.pre_opt_ir {
module.print_to_file(filename).unwrap();
}
}
fn postopt_ir_callback(&mut self, module: &InkwellModule) {
if let Some(filename) = &self.post_opt_ir {
module.print_to_file(filename).unwrap();
}
}
fn obj_memory_buffer_callback(&mut self, memory_buffer: &InkwellMemoryBuffer) {
if let Some(filename) = &self.obj_file {
let mem_buf_slice = memory_buffer.as_slice();
let mut file = File::create(filename).unwrap();
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
}
}
}
/// Execute a wasm/wat file
fn execute_wasm(options: &Run) -> Result<(), String> {
let disable_cache = options.disable_cache;
@ -558,22 +591,17 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
.map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?;
}
let compiler: Box<dyn Compiler> = match get_compiler_by_backend(options.backend, options) {
Some(x) => x,
None => return Err("the requested backend is not enabled".into()),
};
let compiler: Box<dyn Compiler> = get_compiler_by_backend(options.backend, options)
.ok_or_else(|| "the requested backend is not enabled")?;
#[allow(unused_mut)]
let mut backend_specific_config = None;
#[cfg(feature = "backend-llvm")]
{
if options.backend == Backend::LLVM {
let options = options.backend_llvm_options.clone();
unsafe {
wasmer_llvm_backend::GLOBAL_OPTIONS = LLVMOptions {
pre_opt_ir: options.pre_opt_ir,
post_opt_ir: options.post_opt_ir,
obj_file: options.obj_file,
}
}
backend_specific_config = Some(BackendCompilerConfig(Box::new(LLVMBackendConfig {
callbacks: Some(Rc::new(RefCell::new(options.backend_llvm_options.clone()))),
})))
}
}
@ -598,6 +626,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
enforce_stack_check: true,
track_state,
features: options.features.into_backend_features(),
backend_specific_config,
..Default::default()
},
&*compiler,
@ -610,6 +639,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
symbol_map: em_symbol_map.clone(),
track_state,
features: options.features.into_backend_features(),
backend_specific_config,
..Default::default()
},
&*compiler,
@ -625,7 +655,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
let mut cache = unsafe {
FileSystemCache::new(wasmer_cache_dir).map_err(|e| format!("Cache error: {:?}", e))?
};
let mut load_cache_key = || -> Result<_, String> {
let load_cache_key = || -> Result<_, String> {
if let Some(ref prehashed_cache_key) = options.cache_key {
if let Ok(module) =
WasmHash::decode(prehashed_cache_key).and_then(|prehashed_key| {
@ -655,6 +685,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
symbol_map: em_symbol_map.clone(),
track_state,
features: options.features.into_backend_features(),
backend_specific_config,
..Default::default()
},
&*compiler,