wasm_opt.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. use crate::config::WasmOptLevel;
  2. use crate::{Result, WasmOptConfig};
  3. use std::path::Path;
  4. #[memoize::memoize(SharedCache)]
  5. pub fn wasm_opt_available() -> bool {
  6. if cfg!(feature = "optimizations") {
  7. return true;
  8. }
  9. which::which("wasm-opt").is_ok()
  10. }
  11. /// Write these wasm bytes with a particular set of optimizations
  12. pub async fn write_wasm(bytes: &[u8], output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
  13. tokio::fs::write(output_path, bytes).await?;
  14. optimize(output_path, output_path, cfg).await?;
  15. Ok(())
  16. }
  17. #[allow(unreachable_code)]
  18. pub async fn optimize(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
  19. #[cfg(feature = "optimizations")]
  20. return run_from_lib(input_path, output_path, cfg).await;
  21. // It's okay not to run wasm-opt but we should *really* try it
  22. if which::which("wasm-opt").is_err() {
  23. tracing::warn!("wasm-opt not found and CLI is compiled without optimizations. Skipping optimization for {}", input_path.display());
  24. return Ok(());
  25. }
  26. run_locally(input_path, output_path, cfg).await?;
  27. Ok(())
  28. }
  29. async fn run_locally(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
  30. let mut args = vec![
  31. // needed by wasm-bindgen
  32. "--enable-reference-types",
  33. ];
  34. if cfg.memory_packing {
  35. // needed for our current approach to bundle splitting to work properly
  36. // todo(jon): emit the main module's data section in chunks instead of all at once
  37. args.push("--memory-packing");
  38. }
  39. if !cfg.debug {
  40. args.push("--strip-debug");
  41. } else {
  42. args.push("--debuginfo");
  43. }
  44. let level = match cfg.level {
  45. WasmOptLevel::Z => "-Oz",
  46. WasmOptLevel::S => "-Os",
  47. WasmOptLevel::Zero => "-O0",
  48. WasmOptLevel::One => "-O1",
  49. WasmOptLevel::Two => "-O2",
  50. WasmOptLevel::Three => "-O3",
  51. WasmOptLevel::Four => "-O4",
  52. };
  53. tokio::process::Command::new("wasm-opt")
  54. .arg(input_path)
  55. .arg(level)
  56. .arg("-o")
  57. .arg(output_path)
  58. .args(args)
  59. .output()
  60. .await?;
  61. Ok(())
  62. }
  63. /// Use the `wasm_opt` crate
  64. #[cfg(feature = "optimizations")]
  65. async fn run_from_lib(
  66. input_path: &Path,
  67. output_path: &Path,
  68. options: &WasmOptConfig,
  69. ) -> Result<()> {
  70. let mut level = match options.level {
  71. WasmOptLevel::Z => wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively(),
  72. WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(),
  73. WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(),
  74. WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(),
  75. WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(),
  76. WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(),
  77. WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(),
  78. };
  79. level
  80. .enable_feature(wasm_opt::Feature::ReferenceTypes)
  81. .add_pass(wasm_opt::Pass::MemoryPacking)
  82. .debug_info(options.debug)
  83. .run(input_path, output_path)
  84. .map_err(|err| crate::Error::Other(anyhow::anyhow!(err)))?;
  85. Ok(())
  86. }