Skip to content

Commit 97408a6

Browse files
authored
Merge pull request #17 from arnoox/add-opt-switch
Enhance optimization handling by introducing HERKOS_OPTIMIZE flag and updating transpile options
2 parents f49d270 + d064c15 commit 97408a6

5 files changed

Lines changed: 59 additions & 20 deletions

File tree

.github/workflows/ci.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77

88
env:
99
CARGO_TERM_COLOR: always
10+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
1011

1112
jobs:
1213
check:
@@ -29,8 +30,15 @@ jobs:
2930
- name: Build
3031
run: cargo build --all-targets
3132

32-
- name: Test
33+
- name: Test (optimized)
3334
run: cargo test
35+
env:
36+
HERKOS_OPTIMIZE: "1"
37+
38+
- name: Test (unoptimized)
39+
run: cargo test
40+
env:
41+
HERKOS_OPTIMIZE: "0"
3442

3543
- name: Clippy
3644
run: cargo clippy --all-targets -- -D warnings

crates/herkos-tests/build.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@ use std::fs;
55
use std::path::{Path, PathBuf};
66

77
fn main() -> Result<()> {
8-
// Rerun build script if this file or any data files change
8+
// Rerun build script if this file, any data files, or the optimize flag changes
99
println!("cargo:rerun-if-changed=build.rs");
1010
println!("cargo:rerun-if-changed=data");
11+
println!("cargo:rerun-if-env-changed=HERKOS_OPTIMIZE");
1112

1213
// Use cargo's OUT_DIR for generated files (parallel-build safe)
1314
let out_dir = PathBuf::from(env::var("OUT_DIR").context("OUT_DIR not set")?);
15+
16+
// HERKOS_OPTIMIZE=1 enables IR optimizations; any other value (or unset) disables them.
17+
let optimize = env::var("HERKOS_OPTIMIZE").is_ok_and(|v| v == "1");
1418
eprintln!(
15-
"Generating WASM and transpiled modules to: {}",
16-
out_dir.display()
19+
"Generating WASM and transpiled modules to: {} (optimize={})",
20+
out_dir.display(),
21+
optimize
1722
);
1823

19-
let options = TranspileOptions::default();
24+
let options = TranspileOptions {
25+
optimize,
26+
..TranspileOptions::default()
27+
};
2028
let mut module_names = Vec::new();
2129

2230
// 1. WAT test cases from data/wat/*.wat

crates/herkos/src/lib.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use backend::SafeBackend;
1515
use codegen::CodeGenerator;
1616
use ir::builder::build_module_info;
1717
use ir::{lower_phis, LoweredModuleInfo};
18-
use optimizer::optimize_ir;
18+
use optimizer::{optimize_ir, optimize_lowered_ir};
1919
use parser::parse_wasm;
2020

2121
/// Configuration options for transpilation
@@ -25,13 +25,16 @@ pub struct TranspileOptions {
2525
pub mode: String,
2626
/// Maximum memory pages (used when Wasm module declares no maximum)
2727
pub max_pages: usize,
28+
/// Enable optimizations
29+
pub optimize: bool,
2830
}
2931

3032
impl Default for TranspileOptions {
3133
fn default() -> Self {
3234
Self {
3335
mode: "safe".to_string(),
3436
max_pages: 256,
37+
optimize: false,
3538
}
3639
}
3740
}
@@ -65,16 +68,17 @@ pub fn transpile(wasm_bytes: &[u8], options: &TranspileOptions) -> Result<String
6568
let module_info =
6669
build_module_info(&parsed, options).context("failed to build module metadata")?;
6770

71+
// Optimize the pure SSA IR.
72+
let module_info = optimize_ir(module_info, options.optimize)?;
73+
6874
// SSA destruction: lower phi nodes to predecessor assignments.
69-
// Must run before the optimizer so that all block-elimination passes
70-
// operate on phi-free IR.
71-
let module_info = lower_phis::lower(module_info);
75+
let lowered_module_info = lower_phis::lower(module_info);
7276

73-
// Optimize the IR
74-
let module_info = optimize_ir(module_info)?;
77+
// Optimize the lowered IR
78+
let lowered_module_info = optimize_lowered_ir(lowered_module_info, options.optimize)?;
7579

7680
// Generate Rust source code
77-
let rust_code = generate_rust_code(&module_info)?;
81+
let rust_code = generate_rust_code(&lowered_module_info)?;
7882

7983
Ok(rust_code)
8084
}

crates/herkos/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ struct Cli {
1414
/// Output Rust source file
1515
#[arg(long, short)]
1616
output: Option<PathBuf>,
17+
18+
/// Enable IR optimizations
19+
#[arg(long, short = 'O')]
20+
optimize: bool,
1721
}
1822

1923
fn main() -> Result<()> {
@@ -29,6 +33,7 @@ fn main() -> Result<()> {
2933
let options = TranspileOptions {
3034
mode: "safe".to_string(),
3135
max_pages: 256,
36+
optimize: cli.optimize,
3237
};
3338

3439
// Transpile using library function

crates/herkos/src/optimizer/mod.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,38 @@
66
//! Each optimization is a self-contained sub-module. The top-level
77
//! [`optimize_ir`] function runs all passes in order.
88
9-
use crate::ir::LoweredModuleInfo;
9+
use crate::ir::{LoweredModuleInfo, ModuleInfo};
1010
use anyhow::Result;
1111

1212
// ── Passes ───────────────────────────────────────────────────────────────────
1313
mod dead_blocks;
1414

15-
/// Optimizes the IR representation by running all passes in order.
15+
/// Optimizes the pure SSA IR before phi lowering.
1616
///
17-
/// Expects a [`LoweredModuleInfo`] — i.e. phi nodes have already been lowered
18-
/// by [`crate::ir::lower_phis::lower`] before calling this function.
19-
pub fn optimize_ir(module_info: LoweredModuleInfo) -> Result<LoweredModuleInfo> {
17+
/// Passes here operate on [`ModuleInfo`] with phi nodes still intact.
18+
/// Control-flow based passes (e.g. dead block elimination) belong here
19+
/// because reachability is identical in SSA and lowered form.
20+
pub fn optimize_ir(module_info: ModuleInfo, do_opt: bool) -> Result<ModuleInfo> {
2021
let mut module_info = module_info;
21-
for func in &mut module_info.ir_functions {
22-
dead_blocks::eliminate(func)?;
22+
if do_opt {
23+
for func in &mut module_info.ir_functions {
24+
dead_blocks::eliminate(func)?;
25+
}
2326
}
2427
Ok(module_info)
2528
}
2629

30+
/// Optimizes the lowered IR after phi nodes have been eliminated.
31+
///
32+
/// Passes here operate on [`LoweredModuleInfo`] where all `IrInstr::Phi`
33+
/// nodes have been replaced by `IrInstr::Assign` in predecessor blocks.
34+
pub fn optimize_lowered_ir(
35+
module_info: LoweredModuleInfo,
36+
_do_opt: bool,
37+
) -> Result<LoweredModuleInfo> {
38+
Ok(module_info)
39+
}
40+
2741
// ── optimize_ir integration tests ─────────────────────────────────────────────
2842

2943
#[cfg(test)]
@@ -74,7 +88,7 @@ mod tests {
7488
..Default::default()
7589
};
7690

77-
let result = super::optimize_ir(crate::ir::lower_phis::lower(module)).unwrap();
91+
let result = super::optimize_ir(module, true).unwrap();
7892
assert_eq!(
7993
result.ir_functions[0].blocks.len(),
8094
1,

0 commit comments

Comments
 (0)