#![allow(dead_code, unused_imports, unused_variables)]
use std::path::PathBuf;
use std::string::ToString;
use std::sync::Arc;
use anyhow::{bail, Result};
#[cfg(feature = "sys")]
use wasmer::sys::Features;
use wasmer::*;
#[cfg(feature = "compiler")]
use wasmer_compiler::CompilerConfig;
#[cfg(feature = "compiler")]
use wasmer_compiler::Engine;
#[derive(Debug, Clone, clap::Parser, Default)]
pub struct StoreOptions {
#[cfg(feature = "compiler")]
#[clap(flatten)]
compiler: CompilerOptions,
}
#[derive(Debug, clap::Parser, Clone, Default)]
pub struct WasmFeatures {
#[clap(long = "enable-simd")]
pub simd: bool,
#[clap(long = "disable-threads")]
pub disable_threads: bool,
#[clap(long = "enable-threads")]
pub _threads: bool,
#[clap(long = "enable-reference-types")]
pub reference_types: bool,
#[clap(long = "enable-multi-value")]
pub multi_value: bool,
#[clap(long = "enable-bulk-memory")]
pub bulk_memory: bool,
#[clap(long = "enable-all")]
pub all: bool,
}
#[cfg(feature = "compiler")]
#[derive(Debug, Clone, clap::Parser, Default)]
pub struct CompilerOptions {
#[clap(long, conflicts_with_all = &["cranelift", "llvm"])]
singlepass: bool,
#[clap(long, conflicts_with_all = &["singlepass", "llvm"])]
cranelift: bool,
#[clap(long, conflicts_with_all = &["singlepass", "cranelift"])]
llvm: bool,
#[clap(long)]
enable_verifier: bool,
#[clap(long)]
llvm_debug_dir: Option<PathBuf>,
#[clap(flatten)]
features: WasmFeatures,
}
#[cfg(feature = "compiler")]
impl CompilerOptions {
fn get_compiler(&self) -> Result<CompilerType> {
if self.cranelift {
Ok(CompilerType::Cranelift)
} else if self.llvm {
Ok(CompilerType::LLVM)
} else if self.singlepass {
Ok(CompilerType::Singlepass)
} else {
cfg_if::cfg_if! {
if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] {
Ok(CompilerType::Cranelift)
}
else if #[cfg(all(feature = "singlepass", any(target_arch = "x86_64", target_arch = "aarch64")))] {
Ok(CompilerType::Singlepass)
}
else if #[cfg(feature = "llvm")] {
Ok(CompilerType::LLVM)
} else {
bail!("There are no available compilers for your architecture");
}
}
}
}
pub fn get_features(&self, mut features: Features) -> Result<Features> {
if !self.features.disable_threads || self.features.all {
features.threads(true);
}
if self.features.disable_threads && !self.features.all {
features.threads(false);
}
if self.features.multi_value || self.features.all {
features.multi_value(true);
}
if self.features.simd || self.features.all {
features.simd(true);
}
if self.features.bulk_memory || self.features.all {
features.bulk_memory(true);
}
if self.features.reference_types || self.features.all {
features.reference_types(true);
}
Ok(features)
}
pub fn get_store_for_target(&self, target: Target) -> Result<(Store, CompilerType)> {
let (compiler_config, compiler_type) = self.get_compiler_config()?;
let engine = self.get_engine(target, compiler_config)?;
let store = Store::new(engine);
Ok((store, compiler_type))
}
pub fn get_engine_for_target(&self, target: Target) -> Result<(Engine, CompilerType)> {
let (compiler_config, compiler_type) = self.get_compiler_config()?;
let engine = self.get_engine(target, compiler_config)?;
Ok((engine, compiler_type))
}
#[cfg(feature = "compiler")]
fn get_engine(
&self,
target: Target,
compiler_config: Box<dyn CompilerConfig>,
) -> Result<Engine> {
let features = self.get_features(compiler_config.default_features_for_target(&target))?;
let engine: Engine = wasmer_compiler::EngineBuilder::new(compiler_config)
.set_features(Some(features))
.set_target(Some(target))
.engine();
Ok(engine)
}
#[allow(unused_variables)]
pub(crate) fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
let compiler = self.get_compiler()?;
let compiler_config: Box<dyn CompilerConfig> = match compiler {
CompilerType::Headless => bail!("The headless engine can't be chosen"),
#[cfg(feature = "singlepass")]
CompilerType::Singlepass => {
let mut config = wasmer_compiler_singlepass::Singlepass::new();
if self.enable_verifier {
config.enable_verifier();
}
Box::new(config)
}
#[cfg(feature = "cranelift")]
CompilerType::Cranelift => {
let mut config = wasmer_compiler_cranelift::Cranelift::new();
if self.enable_verifier {
config.enable_verifier();
}
Box::new(config)
}
#[cfg(feature = "llvm")]
CompilerType::LLVM => {
use std::{fmt, fs::File, io::Write};
use wasmer_compiler_llvm::{
CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
};
use wasmer_types::entity::EntityRef;
let mut config = LLVM::new();
struct Callbacks {
debug_dir: PathBuf,
}
impl Callbacks {
fn new(debug_dir: PathBuf) -> Result<Self> {
std::fs::create_dir_all(&debug_dir)?;
Ok(Self { debug_dir })
}
}
fn types_to_signature(types: &[Type]) -> String {
types
.iter()
.map(|ty| match ty {
Type::I32 => "i".to_string(),
Type::I64 => "I".to_string(),
Type::F32 => "f".to_string(),
Type::F64 => "F".to_string(),
Type::V128 => "v".to_string(),
Type::ExternRef => "e".to_string(),
Type::FuncRef => "r".to_string(),
})
.collect::<Vec<_>>()
.join("")
}
fn function_kind_to_filename(kind: &CompiledKind) -> String {
match kind {
CompiledKind::Local(local_index) => {
format!("function_{}", local_index.index())
}
CompiledKind::FunctionCallTrampoline(func_type) => format!(
"trampoline_call_{}_{}",
types_to_signature(func_type.params()),
types_to_signature(func_type.results())
),
CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
"trampoline_dynamic_{}_{}",
types_to_signature(func_type.params()),
types_to_signature(func_type.results())
),
CompiledKind::Module => "module".into(),
}
}
impl LLVMCallbacks for Callbacks {
fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
let mut path = self.debug_dir.clone();
path.push(format!("{}.preopt.ll", function_kind_to_filename(kind)));
module
.print_to_file(&path)
.expect("Error while dumping pre optimized LLVM IR");
}
fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
let mut path = self.debug_dir.clone();
path.push(format!("{}.postopt.ll", function_kind_to_filename(kind)));
module
.print_to_file(&path)
.expect("Error while dumping post optimized LLVM IR");
}
fn obj_memory_buffer(
&self,
kind: &CompiledKind,
memory_buffer: &InkwellMemoryBuffer,
) {
let mut path = self.debug_dir.clone();
path.push(format!("{}.o", function_kind_to_filename(kind)));
let mem_buf_slice = memory_buffer.as_slice();
let mut file = File::create(path)
.expect("Error while creating debug object file from LLVM IR");
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
}
}
impl fmt::Debug for Callbacks {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LLVMCallbacks")
}
}
if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
}
if self.enable_verifier {
config.enable_verifier();
}
Box::new(config)
}
#[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
compiler => {
bail!(
"The `{}` compiler is not included in this binary.",
compiler.to_string()
)
}
};
#[allow(unreachable_code)]
Ok((compiler_config, compiler))
}
}
#[derive(Debug, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms, dead_code)]
pub enum CompilerType {
Singlepass,
Cranelift,
LLVM,
#[allow(dead_code)]
Headless,
}
impl CompilerType {
#[allow(dead_code)]
pub fn enabled() -> Vec<CompilerType> {
vec![
#[cfg(feature = "singlepass")]
Self::Singlepass,
#[cfg(feature = "cranelift")]
Self::Cranelift,
#[cfg(feature = "llvm")]
Self::LLVM,
]
}
}
impl std::fmt::Display for CompilerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Singlepass => "singlepass",
Self::Cranelift => "cranelift",
Self::LLVM => "llvm",
Self::Headless => "headless",
}
)
}
}
#[cfg(feature = "compiler")]
impl StoreOptions {
pub fn get_store(&self) -> Result<(Store, CompilerType)> {
let target = Target::default();
self.get_store_for_target(target)
}
pub fn get_store_for_target(&self, target: Target) -> Result<(Store, CompilerType)> {
let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?;
let engine = self.get_engine_with_compiler(target, compiler_config)?;
let store = Store::new(engine);
Ok((store, compiler_type))
}
#[cfg(feature = "compiler")]
fn get_engine_with_compiler(
&self,
target: Target,
compiler_config: Box<dyn CompilerConfig>,
) -> Result<Engine> {
let engine = self.compiler.get_engine(target, compiler_config)?;
Ok(engine)
}
}
#[cfg(not(any(
feature = "compiler",
feature = "jsc",
feature = "wamr",
feature = "v8",
feature = "wasmi"
)))]
impl StoreOptions {
fn get_engine_headless(&self) -> Result<wasmer_compiler::Engine> {
let engine: wasmer_compiler::Engine = wasmer_compiler::EngineBuilder::headless().engine();
Ok(engine)
}
pub fn get_store(&self) -> Result<(Store, CompilerType)> {
let engine = self.get_engine_headless()?;
let store = Store::new(engine);
Ok((store, CompilerType::Headless))
}
}
#[cfg(all(
not(feature = "compiler"),
any(feature = "jsc", feature = "wamr", feature = "wasmi", feature = "v8")
))]
impl StoreOptions {
pub fn get_store(&self) -> Result<(Store, CompilerType)> {
let store = Store::default();
Ok((store, CompilerType::Headless))
}
}