From 2b8a321d2f8e1dc2bc1998381a6f62ee7e41e68b Mon Sep 17 00:00:00 2001 From: sbillig Date: Thu, 12 Mar 2026 10:25:18 -0700 Subject: [PATCH] bump sonatina; change optimize cli flag and defaults --- Cargo.lock | 12 ++--- Cargo.toml | 8 +-- crates/codegen/src/backend.rs | 43 ++++++++++++--- crates/codegen/src/sonatina/mod.rs | 4 +- crates/codegen/src/sonatina/tests.rs | 4 +- crates/fe/src/main.rs | 76 +++++++++++++-------------- crates/fe/tests/cli_output.rs | 78 +++++++++++++++++++--------- 7 files changed, 138 insertions(+), 87 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1bc547835..dc143598cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5282,7 +5282,7 @@ dependencies = [ [[package]] name = "sonatina-codegen" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "bit-set", "cranelift-entity 0.126.2", @@ -5302,7 +5302,7 @@ dependencies = [ [[package]] name = "sonatina-ir" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "bit-set", "bitflags 2.11.0", @@ -5324,7 +5324,7 @@ dependencies = [ [[package]] name = "sonatina-macros" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "proc-macro2", "quote", @@ -5334,7 +5334,7 @@ dependencies = [ [[package]] name = "sonatina-parser" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "annotate-snippets", "bimap", @@ -5355,7 +5355,7 @@ dependencies = [ [[package]] name = "sonatina-triple" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "thiserror 2.0.18", ] @@ -5363,7 +5363,7 @@ dependencies = [ [[package]] name = "sonatina-verifier" version = "0.0.3-alpha" -source = "git+https://github.com/fe-lang/sonatina?rev=b5f600f#b5f600fc61fb9e67527c79356fc7de444c362979" +source = "git+https://github.com/fe-lang/sonatina?rev=c6c0062#c6c006250f76352e83103baadc34621a59668f73" dependencies = [ "cranelift-entity 0.126.2", "rayon", diff --git a/Cargo.toml b/Cargo.toml index 839bf00372..2f23c1066d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,10 +47,10 @@ tracing-tree = "0.4.0" wasm-bindgen-test = "0.3" semver = "1.0.26" petgraph = "0.8" -sonatina-ir = { git = "https://github.com/fe-lang/sonatina", rev = "b5f600f" } -sonatina-triple = { git = "https://github.com/fe-lang/sonatina", rev = "b5f600f" } -sonatina-codegen = { git = "https://github.com/fe-lang/sonatina", rev = "b5f600f" } -sonatina-verifier = { git = "https://github.com/fe-lang/sonatina", rev = "b5f600f" } +sonatina-ir = { git = "https://github.com/fe-lang/sonatina", rev = "c6c0062" } +sonatina-triple = { git = "https://github.com/fe-lang/sonatina", rev = "c6c0062" } +sonatina-codegen = { git = "https://github.com/fe-lang/sonatina", rev = "c6c0062" } +sonatina-verifier = { git = "https://github.com/fe-lang/sonatina", rev = "c6c0062" } [profile.dev] # Set to 0 to make the build faster and debugging more difficult. diff --git a/crates/codegen/src/backend.rs b/crates/codegen/src/backend.rs index be24468558..9db9f3c936 100644 --- a/crates/codegen/src/backend.rs +++ b/crates/codegen/src/backend.rs @@ -15,10 +15,10 @@ use std::fmt; pub enum OptLevel { /// No optimization — maximum debuggability. O0, - /// Balanced optimization (default). + /// Size-oriented optimization. + Os, + /// Speed-oriented optimization (default). #[default] - O1, - /// Aggressive optimization. O2, } @@ -28,10 +28,11 @@ impl std::str::FromStr for OptLevel { fn from_str(s: &str) -> Result { match s { "0" => Ok(OptLevel::O0), - "1" => Ok(OptLevel::O1), + "s" => Ok(OptLevel::Os), + "1" => Ok(OptLevel::O2), "2" => Ok(OptLevel::O2), _ => Err(format!( - "unknown optimization level: {s} (expected '0', '1', or '2')" + "unknown optimization level: {s} (expected '0', '1', '2', or 's')" )), } } @@ -48,7 +49,7 @@ impl fmt::Display for OptLevel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { OptLevel::O0 => write!(f, "0"), - OptLevel::O1 => write!(f, "1"), + OptLevel::Os => write!(f, "s"), OptLevel::O2 => write!(f, "2"), } } @@ -277,8 +278,8 @@ impl Backend for SonatinaBackend { // Run the optimization pipeline based on opt_level. match opt_level { OptLevel::O0 => { /* no optimization */ } - OptLevel::O1 => sonatina_codegen::optim::Pipeline::balanced().run(&mut module), - OptLevel::O2 => sonatina_codegen::optim::Pipeline::aggressive().run(&mut module), + OptLevel::Os => sonatina_codegen::optim::Pipeline::size().run(&mut module), + OptLevel::O2 => sonatina_codegen::optim::Pipeline::speed().run(&mut module), } if opt_level != OptLevel::O0 { crate::sonatina::ensure_module_sonatina_ir_valid(&module)?; @@ -360,3 +361,29 @@ impl Backend for SonatinaBackend { Ok(BackendOutput::Bytecode(runtime_section.bytes.clone())) } } + +#[cfg(test)] +mod tests { + use super::OptLevel; + use std::str::FromStr; + + #[test] + fn opt_level_parses_os() { + assert_eq!(OptLevel::from_str("s"), Ok(OptLevel::Os)); + } + + #[test] + fn opt_level_parses_o1_as_o2() { + assert_eq!(OptLevel::from_str("1"), Ok(OptLevel::O2)); + } + + #[test] + fn opt_level_default_is_o2() { + assert_eq!(OptLevel::default(), OptLevel::O2); + } + + #[test] + fn opt_level_display_uses_os() { + assert_eq!(OptLevel::Os.to_string(), "s"); + } +} diff --git a/crates/codegen/src/sonatina/mod.rs b/crates/codegen/src/sonatina/mod.rs index b3cd0ecc9d..4bd8ed4153 100644 --- a/crates/codegen/src/sonatina/mod.rs +++ b/crates/codegen/src/sonatina/mod.rs @@ -417,8 +417,8 @@ fn compile_mir_module_for_sonatina_output<'db>( match opt_level { OptLevel::O0 => {} - OptLevel::O1 => sonatina_codegen::optim::Pipeline::balanced().run(&mut module), - OptLevel::O2 => sonatina_codegen::optim::Pipeline::aggressive().run(&mut module), + OptLevel::Os => sonatina_codegen::optim::Pipeline::size().run(&mut module), + OptLevel::O2 => sonatina_codegen::optim::Pipeline::speed().run(&mut module), } if opt_level != OptLevel::O0 { ensure_module_sonatina_ir_valid(&module)?; diff --git a/crates/codegen/src/sonatina/tests.rs b/crates/codegen/src/sonatina/tests.rs index ec69fc8224..fc6bd4138e 100644 --- a/crates/codegen/src/sonatina/tests.rs +++ b/crates/codegen/src/sonatina/tests.rs @@ -164,8 +164,8 @@ pub fn emit_test_module_sonatina( fn run_sonatina_optimization_pipeline(module: &mut Module, opt_level: OptLevel) { match opt_level { OptLevel::O0 => { /* no optimization */ } - OptLevel::O1 => sonatina_codegen::optim::Pipeline::balanced().run(module), - OptLevel::O2 => sonatina_codegen::optim::Pipeline::aggressive().run(module), + OptLevel::Os => sonatina_codegen::optim::Pipeline::size().run(module), + OptLevel::O2 => sonatina_codegen::optim::Pipeline::speed().run(module), } } diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index b59d5fd2e0..6bde2961ec 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -81,23 +81,23 @@ pub enum Command { /// Code generation backend to use (yul or sonatina). #[arg(long, default_value = "sonatina")] backend: String, - /// Optimization level (0 = none, 1 = balanced, 2 = aggressive). + /// Optimization level (0 = none, s = size-oriented, 1/2 = speed-oriented). /// - /// Defaults to `1`. + /// Defaults to `2`. /// - /// Note: with `--backend yul`, opt levels `1` and `2` are currently equivalent. + /// Note: with `--backend yul`, optimize levels `s`, `1`, and `2` are currently equivalent. /// - /// - Sonatina backend: controls the optimization pipeline. - /// - Yul backend: controls whether solc optimization is enabled (0 = disabled, 1/2 = enabled). - #[arg(long, default_value = "1", value_name = "LEVEL")] - opt_level: String, - /// Enable optimization. - /// - /// Shorthand for `--opt-level 1`. + /// `1` is currently an alias for `2`. /// - /// It is an error to pass `--optimize` with `--opt-level 0`. - #[arg(long)] - optimize: bool, + /// - Sonatina backend: controls the optimization pipeline. + /// - Yul backend: controls whether solc optimization is enabled (0 = disabled, s/1/2 = enabled). + #[arg( + long = "optimize", + short = 'O', + value_name = "LEVEL", + value_parser = ["0", "1", "2", "s"] + )] + optimize: Option, /// solc binary to use (overrides FE_SOLC_PATH). /// /// Only used with `--backend yul` (ignored with a warning otherwise). @@ -234,23 +234,23 @@ pub enum Command { /// Only used with `--backend yul` (ignored with a warning otherwise). #[arg(long)] solc: Option, - /// Optimization level (0 = none, 1 = balanced, 2 = aggressive). - /// - /// Defaults to `1`. + /// Optimization level (0 = none, s = size-oriented, 1/2 = speed-oriented). /// - /// Note: with `--backend yul`, opt levels `1` and `2` are currently equivalent. + /// Defaults to `2`. /// - /// - Sonatina backend: controls the optimization pipeline. - /// - Yul backend: controls whether solc optimization is enabled (0 = disabled, 1/2 = enabled). - #[arg(long, default_value = "1", value_name = "LEVEL")] - opt_level: String, - /// Enable optimization. + /// Note: with `--backend yul`, optimize levels `s`, `1`, and `2` are currently equivalent. /// - /// Shorthand for `--opt-level 1`. + /// `1` is currently an alias for `2`. /// - /// It is an error to pass `--optimize` with `--opt-level 0`. - #[arg(long)] - optimize: bool, + /// - Sonatina backend: controls the optimization pipeline. + /// - Yul backend: controls whether solc optimization is enabled (0 = disabled, s/1/2 = enabled). + #[arg( + long = "optimize", + short = 'O', + value_name = "LEVEL", + value_parser = ["0", "1", "2", "s"] + )] + optimize: Option, /// Trace executed EVM opcodes while running tests. #[arg(long)] trace_evm: bool, @@ -423,7 +423,6 @@ pub fn run(opts: &Options) { standalone, contract, backend, - opt_level, optimize, solc, out_dir, @@ -440,7 +439,7 @@ pub fn run(opts: &Options) { std::process::exit(1); } }; - let opt_level = match effective_opt_level(backend_kind, opt_level, *optimize) { + let opt_level = match effective_opt_level(backend_kind, optimize.as_deref()) { Ok(level) => level, Err(err) => { eprintln!("Error: {err}"); @@ -527,7 +526,6 @@ pub fn run(opts: &Options) { debug: test_debug, backend, solc, - opt_level, optimize, trace_evm, trace_evm_keep, @@ -548,7 +546,7 @@ pub fn run(opts: &Options) { std::process::exit(1); } }; - let opt_level = match effective_opt_level(backend_kind, opt_level, *optimize) { + let opt_level = match effective_opt_level(backend_kind, optimize.as_deref()) { Ok(level) => level, Err(err) => { eprintln!("Error: {err}"); @@ -971,22 +969,18 @@ fn generate_lsp_doc_html(resolved_root: Option<&Utf8PathBuf>) -> String { fn effective_opt_level( backend_kind: codegen::BackendKind, - opt_level: &str, - optimize: bool, + optimize: Option<&str>, ) -> Result { - let level: codegen::OptLevel = opt_level.parse()?; + let level: codegen::OptLevel = optimize.unwrap_or("2").parse()?; - if optimize && level == codegen::OptLevel::O0 { - return Err( - "--optimize is shorthand for `--opt-level 1` and cannot be used with `--opt-level 0`" - .to_string(), + if backend_kind == codegen::BackendKind::Yul + && let Some(optimize @ ("1" | "2")) = optimize + { + eprintln!( + "Warning: --optimize {optimize} has no additional effect for --backend yul (same as s)" ); } - if backend_kind == codegen::BackendKind::Yul && level == codegen::OptLevel::O2 { - eprintln!("Warning: --opt-level 2 has no additional effect for --backend yul (same as 1)"); - } - Ok(level) } diff --git a/crates/fe/tests/cli_output.rs b/crates/fe/tests/cli_output.rs index f8286f0020..272050a597 100644 --- a/crates/fe/tests/cli_output.rs +++ b/crates/fe/tests/cli_output.rs @@ -1374,28 +1374,26 @@ fn test_cli_test_solc_flag_overrides_env() { } #[test] -fn test_cli_build_optimize_and_opt_level_0_is_error() { +fn test_cli_build_opt_level_flag_is_error() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/cli_output/build/simple_contract.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); - let (output, exit_code) = - run_fe_main(&["build", "--optimize", "--opt-level", "0", fixture_path_str]); + let (output, exit_code) = run_fe_main(&["build", "--opt-level", "0", fixture_path_str]); assert_ne!(exit_code, 0, "expected non-zero exit code:\n{output}"); assert!( - output.contains("Error: --optimize is shorthand for"), - "expected error about conflicting optimization flags, got:\n{output}" + output.contains("unexpected argument '--opt-level'"), + "expected `fe build` to reject `--opt-level`, got:\n{output}" ); } #[test] -fn test_cli_check_optimize_and_opt_level_0_is_error() { +fn test_cli_check_optimize_flag_is_error() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/cli_output/build/simple_contract.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); - let (output, exit_code) = - run_fe_main(&["check", "--optimize", "--opt-level", "0", fixture_path_str]); + let (output, exit_code) = run_fe_main(&["check", "--optimize", "0", fixture_path_str]); assert_ne!(exit_code, 0, "expected non-zero exit code:\n{output}"); assert!( output.contains("unexpected argument '--optimize'"), @@ -1404,23 +1402,22 @@ fn test_cli_check_optimize_and_opt_level_0_is_error() { } #[test] -fn test_cli_test_optimize_and_opt_level_0_is_error() { +fn test_cli_test_opt_level_flag_is_error() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/fe_test_runner/pass.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); - let (output, exit_code) = - run_fe_main(&["test", "--optimize", "--opt-level", "0", fixture_path_str]); + let (output, exit_code) = run_fe_main(&["test", "--opt-level", "0", fixture_path_str]); assert_ne!(exit_code, 0, "expected non-zero exit code:\n{output}"); assert!( - output.contains("Error: --optimize is shorthand for"), - "expected error about conflicting optimization flags, got:\n{output}" + output.contains("unexpected argument '--opt-level'"), + "expected `fe test` to reject `--opt-level`, got:\n{output}" ); } #[cfg(unix)] #[test] -fn test_cli_test_optimize_flag_is_forwarded_to_solc() { +fn test_cli_test_optimize_s_enables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/fe_test_runner/pass.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1435,6 +1432,7 @@ fn test_cli_test_optimize_flag_is_forwarded_to_solc() { "--backend", "yul", "--optimize", + "s", "--solc", fake_solc_str, fixture_path_str, @@ -1450,7 +1448,7 @@ fn test_cli_test_optimize_flag_is_forwarded_to_solc() { #[cfg(unix)] #[test] -fn test_cli_test_opt_level_enables_solc_optimizer_for_yul() { +fn test_cli_test_optimize_1_enables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/fe_test_runner/pass.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1464,7 +1462,38 @@ fn test_cli_test_opt_level_enables_solc_optimizer_for_yul() { "test", "--backend", "yul", - "--opt-level", + "-O", + "1", + "--solc", + fake_solc_str, + fixture_path_str, + ], + &[ + ("FE_SOLC_PATH", "/no/such/solc"), + ("FAKE_SOLC_CONTRACT", "test_test_pass"), + ("FAKE_SOLC_EXPECT_OPTIMIZE", "true"), + ], + ); + assert_eq!(exit_code, 0, "fe test failed:\n{output}"); +} + +#[cfg(unix)] +#[test] +fn test_cli_test_optimize_2_enables_solc_optimizer_for_yul() { + let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/fe_test_runner/pass.fe"); + let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); + + let temp = tempdir().expect("tempdir"); + let fake_solc = write_fake_solc(&temp); + let fake_solc_str = fake_solc.to_str().expect("fake solc utf8"); + + let (output, exit_code) = run_fe_main_with_env( + &[ + "test", + "--backend", + "yul", + "--optimize", "2", "--solc", fake_solc_str, @@ -1481,7 +1510,7 @@ fn test_cli_test_opt_level_enables_solc_optimizer_for_yul() { #[cfg(unix)] #[test] -fn test_cli_test_opt_level_0_disables_solc_optimizer_for_yul() { +fn test_cli_test_optimize_0_disables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/fe_test_runner/pass.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1495,7 +1524,7 @@ fn test_cli_test_opt_level_0_disables_solc_optimizer_for_yul() { "test", "--backend", "yul", - "--opt-level", + "--optimize", "0", "--solc", fake_solc_str, @@ -1512,7 +1541,7 @@ fn test_cli_test_opt_level_0_disables_solc_optimizer_for_yul() { #[cfg(unix)] #[test] -fn test_cli_build_optimize_flag_is_forwarded_to_solc() { +fn test_cli_build_optimize_s_enables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/cli_output/build/simple_contract.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1531,6 +1560,7 @@ fn test_cli_build_optimize_flag_is_forwarded_to_solc() { "--contract", "Foo", "--optimize", + "s", "--out-dir", out_dir_str.as_str(), fixture_path_str, @@ -1546,7 +1576,7 @@ fn test_cli_build_optimize_flag_is_forwarded_to_solc() { #[cfg(unix)] #[test] -fn test_cli_build_opt_level_enables_solc_optimizer_for_yul() { +fn test_cli_build_optimize_1_enables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/cli_output/build/simple_contract.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1564,8 +1594,8 @@ fn test_cli_build_opt_level_enables_solc_optimizer_for_yul() { "yul", "--contract", "Foo", - "--opt-level", - "2", + "-O", + "1", "--out-dir", out_dir_str.as_str(), fixture_path_str, @@ -1581,7 +1611,7 @@ fn test_cli_build_opt_level_enables_solc_optimizer_for_yul() { #[cfg(unix)] #[test] -fn test_cli_build_opt_level_0_disables_solc_optimizer_for_yul() { +fn test_cli_build_optimize_0_disables_solc_optimizer_for_yul() { let fixture_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/cli_output/build/simple_contract.fe"); let fixture_path_str = fixture_path.to_str().expect("fixture path utf8"); @@ -1599,7 +1629,7 @@ fn test_cli_build_opt_level_0_disables_solc_optimizer_for_yul() { "yul", "--contract", "Foo", - "--opt-level", + "--optimize", "0", "--out-dir", out_dir_str.as_str(),