diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7281175b..aecc26d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,6 +85,14 @@ Note that the output is truncated at 100KB. This can be adjusted for the purpose When running Zellij with the `--debug` flag, Zellij will dump a copy of all bytes received over the pty for each pane in: `/$temp_dir/zellij-/zellij-log/zellij-.log`. These might be useful when troubleshooting terminal issues. +## Testing plugins +Zellij by default uses a fast, non-optimized, compiler for WASM when running in debug mode. This can be overriden by using the `force_cranelift` feature flag, if you wish to reproduce the behavior of release mode. + +To enable the flag, run: +```sh +cargo xtask run --cranelift +``` + ## How we treat clippy lints We currently use clippy in [GitHub Actions](https://github.com/zellij-org/zellij/blob/main/.github/workflows/rust.yml) with the default settings that report only [`clippy::correctness`](https://github.com/rust-lang/rust-clippy#readme) as errors and other lints as warnings because Zellij is still unstable. This means that all warnings can be ignored depending on the situation at that time, even though they are also helpful to keep the code quality. diff --git a/Cargo.lock b/Cargo.lock index 4eecac05..58456fb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -836,6 +836,32 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2", +] + [[package]] name = "either" version = "1.6.1" @@ -3412,6 +3438,7 @@ dependencies = [ "wasmer-artifact", "wasmer-compiler", "wasmer-compiler-cranelift", + "wasmer-compiler-singlepass", "wasmer-derive", "wasmer-engine", "wasmer-engine-dylib", @@ -3473,6 +3500,25 @@ dependencies = [ "wasmer-types", ] +[[package]] +name = "wasmer-compiler-singlepass" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ca2a35204d8befa85062bc7aac259a8db8070b801b8a783770ba58231d729e" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "gimli", + "lazy_static", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-derive" version = "2.3.0" diff --git a/Cargo.toml b/Cargo.toml index af10e36c..dbe21652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,3 +73,4 @@ pkg-fmt = "tgz" default = [ "zellij-utils/plugins_from_target" ] disable_automatic_asset_installation = [ "zellij-utils/disable_automatic_asset_installation" ] unstable = [ "zellij-client/unstable", "zellij-utils/unstable" ] +force_cranelift = [ "zellij-server/force_cranelift" ] diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index c3afe2df..df785e54 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -63,6 +63,8 @@ xflags::xflags! { cmd run { /// Take plugins from here, skip building plugins. Passed to zellij verbatim optional --data-dir path: PathBuf + /// Force the use of Cranelift for compiling WASM modules + optional --cranelift /// Arguments to pass after `cargo run --` repeated args: OsString } @@ -173,6 +175,8 @@ pub struct Run { pub args: Vec, pub data_dir: Option, + + pub cranelift: bool, } #[derive(Debug)] diff --git a/xtask/src/pipelines.rs b/xtask/src/pipelines.rs index b2266a18..69737c25 100644 --- a/xtask/src/pipelines.rs +++ b/xtask/src/pipelines.rs @@ -94,6 +94,8 @@ pub fn install(sh: &Shell, flags: flags::Install) -> anyhow::Result<()> { pub fn run(sh: &Shell, flags: flags::Run) -> anyhow::Result<()> { let err_context = || format!("failed to run pipeline 'run' with args {flags:?}"); + let cranelift = flags.cranelift.then_some(["--features", "force_cranelift"]); + if let Some(ref data_dir) = flags.data_dir { let data_dir = sh.current_dir().join(data_dir); @@ -103,6 +105,7 @@ pub fn run(sh: &Shell, flags: flags::Run) -> anyhow::Result<()> { .args(["--package", "zellij"]) .arg("--no-default-features") .args(["--features", "disable_automatic_asset_installation"]) + .args(cranelift.iter().flatten()) .args(["--", "--data-dir", &format!("{}", data_dir.display())]) .args(&flags.args) .run() @@ -120,7 +123,9 @@ pub fn run(sh: &Shell, flags: flags::Run) -> anyhow::Result<()> { ) .and_then(|_| crate::cargo()) .and_then(|cargo| { - cmd!(sh, "{cargo} run --") + cmd!(sh, "{cargo} run") + .args(cranelift.iter().flatten()) + .args(["--"]) .args(&flags.args) .run() .map_err(anyhow::Error::new) @@ -134,7 +139,7 @@ pub fn run(sh: &Shell, flags: flags::Run) -> anyhow::Result<()> { /// This includes the optimized zellij executable from the [`install`] pipeline, the man page, the /// `.desktop` file and the application logo. pub fn dist(sh: &Shell, _flags: flags::Dist) -> anyhow::Result<()> { - let err_context = || format!("failed to run pipeline 'dist'"); + let err_context = || "failed to run pipeline 'dist'"; sh.change_dir(crate::project_root()); if sh.path_exists("target/dist") { diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml index 4d73b995..a9c35ffa 100644 --- a/zellij-server/Cargo.toml +++ b/zellij-server/Cargo.toml @@ -18,7 +18,7 @@ daemonize = "0.4.1" serde_json = "1.0" unicode-width = "0.1.8" url = "2.2.2" -wasmer = "2.3.0" +wasmer = { version = "2.3.0", features = ["singlepass"]} wasmer-wasi = "2.3.0" cassowary = "0.3.0" zellij-utils = { path = "../zellij-utils/", version = "0.34.5" } @@ -36,3 +36,5 @@ semver = "0.11.0" [dev-dependencies] insta = "1.6.0" +[features] +force_cranelift = [] diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index ac1b268d..9a4ddab2 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -749,7 +749,7 @@ fn init_session( Some(&to_background_jobs), None, ); - let store = Store::default(); + let store = get_store(); move || { plugin_thread_main( @@ -818,3 +818,15 @@ fn init_session( background_jobs_thread: Some(background_jobs_thread), } } + +#[cfg(any(feature = "force_cranelift", not(debug_assertions)))] +fn get_store() -> Store { + log::info!("Compiling plugins using Cranelift"); + Store::new(&wasmer::Universal::new(wasmer::Cranelift::default()).engine()) +} + +#[cfg(not(any(feature = "force_cranelift", not(debug_assertions))))] +fn get_store() -> Store { + log::info!("Compiling plugins using Singlepass"); + Store::new(&wasmer::Universal::new(wasmer::Singlepass::default()).engine()) +} diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs index 6271295e..77d979ac 100644 --- a/zellij-server/src/plugins/wasm_bridge.rs +++ b/zellij-server/src/plugins/wasm_bridge.rs @@ -30,7 +30,7 @@ use crate::{ }; use zellij_utils::{ - consts::{DEBUG_MODE, VERSION, ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR}, + consts::{VERSION, ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR}, data::{Event, EventType, PluginIds}, errors::prelude::*, input::{ @@ -293,13 +293,15 @@ impl WasmBridge { .collect(); let cached_path = ZELLIJ_CACHE_DIR.join(&hash); + let timer = std::time::Instant::now(); unsafe { match Module::deserialize_from_file(&self.store, &cached_path) { Ok(m) => { - log::debug!( - "Loaded plugin '{}' from cache folder at '{}'", + log::info!( + "Loaded plugin '{}' from cache folder at '{}' in {:?}", plugin.path.display(), ZELLIJ_CACHE_DIR.display(), + timer.elapsed(), ); m }, @@ -313,6 +315,11 @@ impl WasmBridge { }) .and_then(|m| { m.serialize_to_file(&cached_path).map_err(anyError::new)?; + log::info!( + "Compiled plugin '{}' in {:?}", + plugin.path.display(), + timer.elapsed() + ); Ok(m) }) .with_context(inner_context) @@ -442,13 +449,7 @@ impl WasmBridge { &mut self, mut updates: Vec<(Option, Option, Event)>, ) -> Result<()> { - let err_context = || { - if *DEBUG_MODE.get().unwrap_or(&true) { - format!("failed to update plugin state") - } else { - "failed to update plugin state".to_string() - } - }; + let err_context = || "failed to update plugin state".to_string(); let mut plugin_bytes = vec![]; for (pid, cid, event) in updates.drain(..) {