Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cd6594d
feat(llvm): Use debug symbols when generating function names
Apr 7, 2025
de69b95
feat(llvm): Add new `pass_params` (g0m0) optimization
Apr 9, 2025
197c240
chore: Make linter happy
Apr 9, 2025
77bdd28
fix(llvm): Rename `FunctionKind` to `G0M0FunctionKind`, fix generatio…
Apr 22, 2025
b9a71c2
feat: Use `String` as return for `deterministic_id` and, in compilers…
Apr 10, 2025
184b8d4
feat(api): Add `with_opts` function to create new engines
Apr 10, 2025
2304461
feat(cli): Add `--profiler=perfmap` support
Apr 9, 2025
6ecdefb
chore: Make linter happy
Apr 22, 2025
f25342b
feat(wasmer_config): Add `SuggestedCompilerOptimizations` field to Ma…
Apr 22, 2025
4c5e5ec
feat(wasmer-types): Minor changes to comments for `SuggestedCompilerO…
Apr 22, 2025
0a04f39
feat(wasix): Extract compiler optimizations suggestions from commands
Apr 22, 2025
88633cb
chore: Minor changes
Apr 22, 2025
79f2135
fix(compiler): Feature-gate `register_perfmap` for non-wasm32 targets
Apr 22, 2025
f3cf1aa
chore: Make linter happy
Apr 22, 2025
7e881aa
fix: Feature-gate fields and calls related to compilers
Apr 22, 2025
8e7c316
chore: Make linter happy
Apr 22, 2025
09b1f15
fix: Rename `wasmer_types::SuggestedCompilerOptimizations` to `UserCo…
Apr 22, 2025
0ba63b9
chore: Make linter happy
Apr 22, 2025
e823456
Merge pull request #5518 from wasmerio/feat/suggested-opts
xdoardo Apr 23, 2025
25bd79b
Merge branch 'llvm-pass-params' into feat/perfmap
xdoardo Apr 23, 2025
d1c9889
Merge pull request #5504 from wasmerio/feat/perfmap
xdoardo Apr 23, 2025
f1b3b82
chore: Fix missing closing delimiter, make linter happy
Apr 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/api/src/backend/js/entities/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use wasmer_types::{target::Target, Features};
pub struct Engine;

impl Engine {
pub(crate) fn deterministic_id(&self) -> &str {
pub(crate) fn deterministic_id(&self) -> String {
// All js engines have the same id
"js-generic"
String::from("js-generic")
}

/// Returns the WebAssembly features supported by the JS engine.
Expand Down
4 changes: 2 additions & 2 deletions lib/api/src/backend/jsc/entities/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ impl JSCEngine {
}

impl Engine {
pub(crate) fn deterministic_id(&self) -> &str {
pub(crate) fn deterministic_id(&self) -> String {
// All js engines have the same id
"javascriptcore"
String::from("javascriptcore")
}

/// Returns the WebAssembly features supported by the JSC engine for the given target.
Expand Down
4 changes: 2 additions & 2 deletions lib/api/src/backend/v8/entities/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ impl Engine {
Self::default()
}

pub(crate) fn deterministic_id(&self) -> &str {
"v8"
pub(crate) fn deterministic_id(&self) -> String {
String::from("v8")
}

/// Returns the WebAssembly features supported by the V8 engine.
Expand Down
4 changes: 2 additions & 2 deletions lib/api/src/backend/wamr/entities/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl Engine {
Self::default()
}

pub(crate) fn deterministic_id(&self) -> &str {
"wamr"
pub(crate) fn deterministic_id(&self) -> String {
String::from("wamr")
}

/// Returns the WebAssembly features supported by the WAMR engine.
Expand Down
4 changes: 2 additions & 2 deletions lib/api/src/backend/wasmi/entities/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl Engine {
Self::default()
}

pub(crate) fn deterministic_id(&self) -> &str {
"wasmi"
pub(crate) fn deterministic_id(&self) -> String {
String::from("wasmi")
}

/// Returns the WebAssembly features supported by the WASMI engine.
Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/entities/engine/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ gen_rt_ty!(Engine @derives Debug, Clone);
impl BackendEngine {
/// Returns the deterministic id of this engine.
#[inline]
pub fn deterministic_id(&self) -> &str {
pub fn deterministic_id(&self) -> String {
match_rt!(on self => s {
s.deterministic_id()
})
Expand Down
25 changes: 23 additions & 2 deletions lib/api/src/entities/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use bytes::Bytes;
use std::{path::Path, sync::Arc};
use wasmer_types::{target::Target, DeserializeError, Features};
use wasmer_types::{
target::{Target, UserCompilerOptimizations},
CompileError, DeserializeError, Features,
};

#[cfg(feature = "sys")]
use wasmer_compiler::Artifact;
Expand Down Expand Up @@ -51,7 +54,7 @@ impl Engine {
}

/// Returns the deterministic id of this engine.
pub fn deterministic_id(&self) -> &str {
pub fn deterministic_id(&self) -> String {
self.be.deterministic_id()
}

Expand Down Expand Up @@ -223,4 +226,22 @@ impl Engine {
) -> Result<Arc<Artifact>, DeserializeError> {
self.be.deserialize_from_file_unchecked(file_ref)
}

/// Add suggested optimizations to this engine.
///
/// # Note
///
/// Not every backend supports every optimization. This function may fail (i.e. not set the
/// suggested optimizations) silently if the underlying engine backend does not support one or
/// more optimizations.
pub fn with_opts(
&mut self,
suggested_opts: &UserCompilerOptimizations,
) -> Result<(), CompileError> {
match self.be {
#[cfg(feature = "sys")]
BackendEngine::Sys(ref mut e) => e.with_opts(suggested_opts),
_ => Ok(()),
}
}
}
196 changes: 189 additions & 7 deletions lib/cli/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
// module.
#![allow(dead_code, unused_imports, unused_variables)]

use std::path::PathBuf;
use std::string::ToString;
use std::sync::Arc;
use std::{path::PathBuf, str::FromStr};

use anyhow::{bail, Result};
#[cfg(feature = "sys")]
Expand Down Expand Up @@ -187,16 +187,44 @@ pub struct RuntimeOptions {
#[clap(long)]
enable_verifier: bool,

/// Enable a profiler.
///
/// Available for cranelift, LLVM and singlepass.
#[clap(long, value_enum)]
profiler: Option<Profiler>,

/// LLVM debug directory, where IR and object files will be written to.
///
/// Only available for the LLVM compiler.
#[clap(long)]
llvm_debug_dir: Option<PathBuf>,

/// Only available for the LLVM compiler. Enable the "pass-params" optimization, where the first (#0)
/// global and the first (#0) memory passed between guest functions as explicit parameters.
#[clap(long)]
enable_pass_params_opt: bool,

#[clap(flatten)]
features: WasmFeatures,
}

#[derive(Clone, Debug)]
pub enum Profiler {
/// Perfmap-based profilers.
Perfmap,
}

impl FromStr for Profiler {
type Err = anyhow::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"perfmap" => Ok(Self::Perfmap),
_ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
}
}
}

impl RuntimeOptions {
pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
// If a specific backend is explicitly requested, use it
Expand Down Expand Up @@ -267,7 +295,7 @@ impl RuntimeOptions {
let backend = backends.first().unwrap();
let backend_kind = wasmer::BackendKind::from(backend);
let required_features = wasmer::Engine::default_features_for_backend(&backend_kind, target);
backend.get_engine(target, &required_features)
backend.get_engine(target, &required_features, self)
}

pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
Expand Down Expand Up @@ -310,7 +338,7 @@ impl RuntimeOptions {
filtered_backends
.first()
.unwrap()
.get_engine(target, required_features)
.get_engine(target, required_features, self)
}

#[cfg(feature = "compiler")]
Expand Down Expand Up @@ -417,6 +445,12 @@ impl RuntimeOptions {
if self.enable_verifier {
config.enable_verifier();
}
if let Some(p) = &self.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}

Box::new(config)
}
#[cfg(feature = "cranelift")]
Expand All @@ -425,6 +459,11 @@ impl RuntimeOptions {
if self.enable_verifier {
config.enable_verifier();
}
if let Some(p) = &self.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}
Box::new(config)
}
#[cfg(feature = "llvm")]
Expand All @@ -436,6 +475,11 @@ impl RuntimeOptions {
};
use wasmer_types::entity::EntityRef;
let mut config = LLVM::new();

if self.enable_pass_params_opt {
config.enable_pass_params_opt();
}

struct Callbacks {
debug_dir: PathBuf,
}
Expand Down Expand Up @@ -528,6 +572,12 @@ impl RuntimeOptions {
if self.enable_verifier {
config.enable_verifier();
}
if let Some(p) = &self.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}

Box::new(config)
}
BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
Expand Down Expand Up @@ -592,11 +642,24 @@ impl BackendType {
}

/// Get an engine for this backend type
pub fn get_engine(&self, target: &Target, features: &Features) -> Result<Engine> {
pub fn get_engine(
&self,
target: &Target,
features: &Features,
runtime_opts: &RuntimeOptions,
) -> Result<Engine> {
match self {
#[cfg(feature = "singlepass")]
Self::Singlepass => {
let config = wasmer_compiler_singlepass::Singlepass::new();
let mut config = wasmer_compiler_singlepass::Singlepass::new();
if runtime_opts.enable_verifier {
config.enable_verifier();
}
if let Some(p) = &runtime_opts.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}
let engine = wasmer_compiler::EngineBuilder::new(config)
.set_features(Some(features.clone()))
.set_target(Some(target.clone()))
Expand All @@ -606,7 +669,15 @@ impl BackendType {
}
#[cfg(feature = "cranelift")]
Self::Cranelift => {
let config = wasmer_compiler_cranelift::Cranelift::new();
let mut config = wasmer_compiler_cranelift::Cranelift::new();
if runtime_opts.enable_verifier {
config.enable_verifier();
}
if let Some(p) = &runtime_opts.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}
let engine = wasmer_compiler::EngineBuilder::new(config)
.set_features(Some(features.clone()))
.set_target(Some(target.clone()))
Expand All @@ -616,7 +687,118 @@ impl BackendType {
}
#[cfg(feature = "llvm")]
Self::LLVM => {
let config = wasmer_compiler_llvm::LLVM::new();
use std::{fmt, fs::File, io::Write};

use wasmer_compiler_llvm::{
CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
};
use wasmer_types::entity::EntityRef;

let mut config = wasmer_compiler_llvm::LLVM::new();

struct Callbacks {
debug_dir: PathBuf,
}
impl Callbacks {
fn new(debug_dir: PathBuf) -> Result<Self> {
// Create the debug dir in case it doesn't exist
std::fs::create_dir_all(&debug_dir)?;
Ok(Self { debug_dir })
}
}
// Converts a kind into a filename, that we will use to dump
// the contents of the IR object file to.
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(),
Type::ExceptionRef => "x".to_string(),
})
.collect::<Vec<_>>()
.join("")
}
// Converts a kind into a filename, that we will use to dump
// the contents of the IR object file to.
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) = runtime_opts.llvm_debug_dir {
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
}
if runtime_opts.enable_verifier {
config.enable_verifier();
}

if runtime_opts.enable_pass_params_opt {
config.enable_pass_params_opt();
}

if let Some(p) = &runtime_opts.profiler {
match p {
Profiler::Perfmap => config.enable_perfmap(),
}
}

let engine = wasmer_compiler::EngineBuilder::new(config)
.set_features(Some(features.clone()))
.set_target(Some(target.clone()))
Expand Down
Loading
Loading