Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
df28f5f
Collapse aliased optional projections after SSA
Kuhai9801 Jun 20, 2026
4c88dab
Clean SSA const propagation snapshot EOF
Kuhai9801 Jun 20, 2026
9a51b2c
Add vector unwrap_or compiler regression
Kuhai9801 Jun 20, 2026
50e7eec
Absorb redundant SSA ternaries
Kuhai9801 Jun 20, 2026
d40359f
Clean ternary absorption snapshot EOF
Kuhai9801 Jun 20, 2026
e3573d8
Limit late SSA const propagation to aliases
Kuhai9801 Jun 20, 2026
8558a55
Keep compiler SSA aggregate forwarding alias-only
Kuhai9801 Jun 20, 2026
df3379c
Restrict late SSA alias projection to optionals
Kuhai9801 Jun 20, 2026
6061de5
Format SSA ternary tracking updates
Kuhai9801 Jun 20, 2026
98580d6
Clean dead aggregates before CSE
Kuhai9801 Jun 20, 2026
28910c8
Keep CSE conservative on unsupported expressions
Kuhai9801 Jun 20, 2026
9b1af7c
Restore direct SSA const propagation
Kuhai9801 Jun 20, 2026
8786f1e
Forward all late SSA composite aliases
Kuhai9801 Jun 20, 2026
54c390d
Keep SSA aggregate forwarding alias-only
Kuhai9801 Jun 20, 2026
a8f5cd5
Keep late SSA alias forwarding narrow
Kuhai9801 Jun 20, 2026
ad25d67
Run CSE before final DCE
Kuhai9801 Jun 20, 2026
2436d8c
Avoid late SSA const-prop in compiler
Kuhai9801 Jun 20, 2026
9d2bb3c
Use default SSA const propagation in compiler
Kuhai9801 Jun 20, 2026
21b234a
Limit SSA alias field forwarding
Kuhai9801 Jun 20, 2026
f484c31
Disable implicit SSA alias field rewrites
Kuhai9801 Jun 20, 2026
0d5a835
Track optional wrapper casts in SSA const prop
Kuhai9801 Jun 20, 2026
4534cad
Match optional wrapper SSA snapshots
Kuhai9801 Jun 20, 2026
b9c780b
Forward optional wrapper aliases only
Kuhai9801 Jun 20, 2026
ede2446
Match SSA const prop import style
Kuhai9801 Jun 20, 2026
c1e4ae6
Update ternary absorption expectations
Kuhai9801 Jun 20, 2026
44b2a19
Update remaining optional execution expectations
Kuhai9801 Jun 20, 2026
6b22cbe
Update stacked execution expectations
Kuhai9801 Jun 20, 2026
967f298
Update struct roundtrip execution expectation
Kuhai9801 Jun 20, 2026
38f51ef
Update stacked optional execution expectations
Kuhai9801 Jun 20, 2026
7ec1d8c
Update stacked simple vector expectation
Kuhai9801 Jun 20, 2026
3aa06f2
Update stacked storage and view expectations
Kuhai9801 Jun 20, 2026
665b21e
Restore ternary absorption snapshot ending
Kuhai9801 Jun 20, 2026
2c29608
Update stacked ABI CLI expectation
Kuhai9801 Jun 20, 2026
46ac175
Update stacked ABI Aleo expectation
Kuhai9801 Jun 20, 2026
d68feb7
Restore stacked ABI CLI snapshot ending
Kuhai9801 Jun 20, 2026
3f13bca
Refresh stacked storage unit CLI fixture
Kuhai9801 Jun 20, 2026
2773053
Refresh generated stacked storage unit fixture
Kuhai9801 Jun 20, 2026
c0d0276
Forward SSA composite aliases generally
Kuhai9801 Jun 21, 2026
29b7078
Fold redundant generated ternaries
Kuhai9801 Jun 21, 2026
9387a0f
Refresh ternary absorption expectations
Kuhai9801 Jun 21, 2026
0f8c6f4
Match ternary tracking formatting
Kuhai9801 Jun 21, 2026
63f80c3
Remove stale counter storage log tail
Kuhai9801 Jun 21, 2026
271f8cf
Restore counter storage snapshot terminator
Kuhai9801 Jun 21, 2026
38488da
Refresh ternary execution snapshots
Kuhai9801 Jun 21, 2026
5b4a6e7
Refresh optional ternary snapshots
Kuhai9801 Jun 21, 2026
6eb2f33
Refresh remaining ternary execution snapshots
Kuhai9801 Jun 21, 2026
a827263
Refresh ternary pass snapshots
Kuhai9801 Jun 21, 2026
be0c81b
Refresh ABI CLI expectations
Kuhai9801 Jun 21, 2026
cc859e4
Preserve ABI expectation EOF format
Kuhai9801 Jun 21, 2026
6620dd6
Refresh stacked external submodule expectations
Kuhai9801 Jun 21, 2026
8b524bc
Refresh stacked storage CLI output
Kuhai9801 Jun 21, 2026
5f29d48
Refresh stacked storage Aleo output
Kuhai9801 Jun 21, 2026
9ec2094
Refresh stacked storage test Aleo output
Kuhai9801 Jun 21, 2026
f1379c8
Normalize stacked storage Aleo spacing
Kuhai9801 Jun 21, 2026
8e24a6f
Normalize SSA ternary expectation EOFs
Kuhai9801 Jun 22, 2026
2be1222
Revert "Normalize SSA ternary expectation EOFs"
Kuhai9801 Jun 22, 2026
169b815
Address CSE and SSA const propagation review
Kuhai9801 Jun 22, 2026
7032481
Add ternary peephole absorption regression
Kuhai9801 Jun 25, 2026
1ed4532
Restore SSA pass expectation EOFs
Kuhai9801 Jun 25, 2026
019402c
Constrain ternary peephole absorption operands
Kuhai9801 Jun 26, 2026
41835fb
Update ternary absorption expectations
Kuhai9801 Jun 26, 2026
6ad3d40
Tighten atom-only ternary absorption cleanup
Kuhai9801 Jun 27, 2026
5e91c38
Narrow late SSA const propagation cleanup
Kuhai9801 Jun 27, 2026
570fae9
Update generic struct forwarding expectations
Kuhai9801 Jun 27, 2026
dfae367
Update struct update nested expectation
Kuhai9801 Jun 29, 2026
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
10 changes: 9 additions & 1 deletion crates/compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,18 @@ impl Compiler {
// Flattening may produce ternary expressions not in SSA form.
self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;

self.do_pass::<SsaConstPropagation>(())?;
self.do_pass::<SsaConstPropagation>(SsaConstPropagationInput::default())?;

self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;

// The final SSA pass can introduce local aliases around atom-fielded
// composites. Run a late cleanup before CSE/DCE, leaving direct
// composite field forwarding to the earlier pass.
self.do_pass::<SsaConstPropagation>(SsaConstPropagationInput {
forward_direct_composites: false,
propagate_constants: false,
})?;

self.do_pass::<CommonSubexpressionEliminating>(())?;

self.do_pass::<DeadCodeEliminating>(())?;
Expand Down
25 changes: 12 additions & 13 deletions crates/passes/src/common_subexpression_elimination/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ impl AstReconstructor for CommonSubexpressionEliminatingVisitor<'_> {
fn reconstruct_expression(&mut self, input: Expression, _additional: &()) -> (Expression, Self::AdditionalOutput) {
// We simply forward every expression to `try_expr` rather than using the individual reconstruct
// functions from the `AstReconstructor` trait.
(self.try_expr(input, None).expect("CSE Error: Error reconstructing expression").0, Default::default())
let mut input = input;
self.try_expr(&mut input, None);
(input, Default::default())
}

fn reconstruct_block(&mut self, mut block: Block) -> (Block, Self::AdditionalOutput) {
Expand All @@ -38,23 +40,20 @@ impl AstReconstructor for CommonSubexpressionEliminatingVisitor<'_> {
fn reconstruct_definition(&mut self, mut input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
match input.place {
DefinitionPlace::Single(place) => {
let (value, definition_not_needed) = self
.try_expr(input.value, Some(place.name))
.expect("CSE Error: Error reconstructing single definition");

if definition_not_needed {
// We don't need this definition - everywhere its variable is referred to, we'll map it to some other
// Path.
(Statement::dummy(), Default::default())
if let Some(definition_not_needed) = self.try_expr(&mut input.value, Some(place.name)) {
if definition_not_needed {
// We don't need this definition - everywhere its variable is referred to, we'll map it to some other
// Path.
(Statement::dummy(), Default::default())
} else {
(input.into(), Default::default())
}
} else {
input.value = value;
(input.into(), Default::default())
}
}
DefinitionPlace::Multiple(_) => {
let (value, _) =
self.try_expr(input.value, None).expect("CSE Error: Error reconstructing multiple definitions");
input.value = value;
self.try_expr(&mut input.value, None);
(input.into(), Default::default())
}
}
Expand Down
41 changes: 25 additions & 16 deletions crates/passes/src/common_subexpression_elimination/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,15 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
///
/// - `place` If this expression is the right hand side of a definition, `place` is the left hand side,
///
/// Returns (transformed expression, place_not_needed). `place_not_needed` is true iff it has been mapped to
/// Mutates `expression` in place while probing it. `None` means the whole
/// expression is not CSE-trackable, but any rewritten atom operands must be
/// preserved so deleted aliases are not reintroduced.
///
/// Returns `place_not_needed`, which is true iff `place` has been mapped to
/// another path, and thus its definition is no longer needed.
pub fn try_expr(&mut self, mut expression: Expression, place: Option<Symbol>) -> Option<(Expression, bool)> {
pub fn try_expr(&mut self, expression: &mut Expression, place: Option<Symbol>) -> Option<bool> {
let span = expression.span();
let expr: Expr = match &mut expression {
let expr: Expr = match expression {
Expression::ArrayAccess(array_access) => {
let array = self.try_atom(&mut array_access.array)?;
let index = self.try_atom(&mut array_access.index)?;
Expand Down Expand Up @@ -202,25 +206,25 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
for arg in &mut intrinsic.arguments {
self.try_atom(arg)?;
}
return Some((expression, false));
return Some(false);
}

Expression::Call(call) => {
// Don't worry about the const expressions.
for arg in &mut call.arguments {
self.try_atom(arg)?;
}
return Some((expression, false));
return Some(false);
}

Expression::Cast(cast) => {
self.try_atom(&mut cast.expression)?;
return Some((expression, false));
return Some(false);
}

Expression::MemberAccess(member_access) => {
self.try_atom(&mut member_access.inner)?;
return Some((expression, false));
return Some(false);
}

Expression::Composite(composite_expression) => {
Expand All @@ -229,7 +233,7 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
self.try_atom(expr)?;
}
}
return Some((expression, false));
return Some(false);
}

Expression::Tuple(tuple_expression) => {
Expand All @@ -238,9 +242,12 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
tuple_expression.elements = tuple_expression
.elements
.drain(..)
.map(|expr| self.try_expr(expr, None).map(|x| x.0))
.collect::<Option<Vec<_>>>()?;
return Some((expression, false));
.map(|mut expr| {
self.try_expr(&mut expr, None);
expr
})
.collect::<Vec<_>>();
return Some(false);
}

Expression::TupleAccess(_) => panic!("Tuple access expressions should not exist in this pass."),
Expand All @@ -258,11 +265,11 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
}
DynamicOpKind::Read { .. } => {}
}
return Some((expression, false));
return Some(false);
}

Expression::Async(_) | Expression::Err(_) | Expression::Unit(_) => {
return Some((expression, false));
return Some(false);
}
};

Expand All @@ -278,9 +285,11 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
// We were defining a new variable, whose right hand side is already defined, so map
// this variable to the previous variable.
self.scopes.last_mut().unwrap().expressions.insert(Atom::Path(vec![place]).into(), name);
return Some((Path::from(identifier).to_local().into(), true));
*expression = Path::from(identifier).to_local().into();
return Some(true);
}
return Some((Path::from(identifier).to_local().into(), false));
*expression = Path::from(identifier).to_local().into();
return Some(false);
}
}

Expand All @@ -289,6 +298,6 @@ impl CommonSubexpressionEliminatingVisitor<'_> {
self.scopes.last_mut().unwrap().expressions.insert(expr, place);
}

Some((expression, false))
Some(false)
}
}
124 changes: 116 additions & 8 deletions crates/passes/src/peephole_optimization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//! - **Identity operation folding**: eliminates `add x 0`, `mul x 1`, `or x false`, etc.
//! - **Trivial assert elimination**: removes `assert.eq <lit> <lit>` when both sides are equal,
//! preserving the dummy assert required for empty closures/finalizes.
//! - **Ternary absorption**: collapses redundant nested selects over the same condition.
//! - **Dead register elimination**: removes pure instructions whose destination register is never read.
//! - **Consecutive cast folding**: merges adjacent casts through the same intermediate register.
//! - **Register renumbering**: compacts register indices after instruction removal.
Expand All @@ -44,6 +45,7 @@ impl Pass for PeepholeOptimizing {
input.for_each_statement_list(|stmts, num_inputs| {
fold_identity_operations(stmts);
eliminate_trivial_asserts(stmts);
fold_redundant_ternaries(stmts);
eliminate_dead_registers(stmts);
fold_consecutive_casts(stmts);
renumber_registers(stmts, num_inputs);
Expand Down Expand Up @@ -94,16 +96,37 @@ fn is_true(expr: &AleoExpr) -> bool {
matches!(expr, AleoExpr::Bool(true))
}

/// Returns true if the expression is a literal (not a register or composite expression).
/// Returns true if the expression is a generated-Aleo literal.
fn is_literal(expr: &AleoExpr) -> bool {
!matches!(
expr,
match expr {
AleoExpr::Address(_)
| AleoExpr::Identifier(_)
| AleoExpr::Bool(_)
| AleoExpr::Field(_)
| AleoExpr::Group(_)
| AleoExpr::Signature(_)
| AleoExpr::Scalar(_)
| AleoExpr::String(_)
| AleoExpr::U8(_)
| AleoExpr::U16(_)
| AleoExpr::U32(_)
| AleoExpr::U64(_)
| AleoExpr::U128(_)
| AleoExpr::I8(_)
| AleoExpr::I16(_)
| AleoExpr::I32(_)
| AleoExpr::I64(_)
| AleoExpr::I128(_) => true,
AleoExpr::Reg(_)
| AleoExpr::Tuple(_)
| AleoExpr::ArrayAccess(_, _)
| AleoExpr::MemberAccess(_, _)
| AleoExpr::RawName(_)
)
| AleoExpr::Tuple(_)
| AleoExpr::ArrayAccess(_, _)
| AleoExpr::MemberAccess(_, _)
| AleoExpr::RawName(_) => false,
}
}

fn is_ternary_absorption_operand(expr: &AleoExpr) -> bool {
matches!(expr, AleoExpr::Reg(_)) || is_literal(expr)
}

// Identity operation folding
Expand Down Expand Up @@ -431,6 +454,39 @@ fn eliminate_trivial_asserts(stmts: &mut Vec<AleoStmt>) {
}
}

// Ternary absorption

fn fold_redundant_ternaries(stmts: &mut [AleoStmt]) {
let mut ternaries: HashMap<AleoReg, (AleoExpr, AleoExpr, AleoExpr)> = HashMap::new();

for stmt in stmts {
if let AleoStmt::Ternary(condition, if_true, if_false, dest) = stmt {
if let AleoExpr::Reg(inner) = if_true.clone()
&& let Some((inner_condition, inner_true, inner_false)) = ternaries.get(&inner)
&& condition == inner_condition
&& if_false == inner_false
{
*if_true = inner_true.clone();
}

if let AleoExpr::Reg(inner) = if_false.clone()
&& let Some((inner_condition, inner_true, inner_false)) = ternaries.get(&inner)
&& condition == inner_condition
&& if_true == inner_true
{
*if_false = inner_false.clone();
}

if is_ternary_absorption_operand(condition)
&& is_ternary_absorption_operand(if_true)
&& is_ternary_absorption_operand(if_false)
{
ternaries.insert(dest.clone(), (condition.clone(), if_true.clone(), if_false.clone()));
}
}
}
}

// Dead register elimination

/// Collect all registers that are read (used as operands) in a statement.
Expand Down Expand Up @@ -1092,3 +1148,55 @@ fn renumber_registers(stmts: &mut [AleoStmt], num_inputs: usize) {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

fn reg(index: u64) -> AleoExpr {
AleoExpr::Reg(AleoReg::R(index))
}

fn reg_dest(index: u64) -> AleoReg {
AleoReg::R(index)
}

#[test]
fn redundant_ternary_absorption_uses_atom_operands() {
let mut stmts = vec![
AleoStmt::Ternary(reg(0), reg(1), reg(2), reg_dest(3)),
AleoStmt::Ternary(reg(0), reg(3), reg(2), reg_dest(4)),
];

fold_redundant_ternaries(&mut stmts);

assert_eq!(stmts[1], AleoStmt::Ternary(reg(0), reg(1), reg(2), reg_dest(4)));
}

#[test]
fn redundant_ternary_absorption_uses_literal_operands() {
let one = AleoExpr::U8(1);
let two = AleoExpr::U8(2);
let mut stmts = vec![
AleoStmt::Ternary(reg(0), one.clone(), two.clone(), reg_dest(3)),
AleoStmt::Ternary(reg(0), reg(3), two.clone(), reg_dest(4)),
];

fold_redundant_ternaries(&mut stmts);

assert_eq!(stmts[1], AleoStmt::Ternary(reg(0), one, two, reg_dest(4)));
}

#[test]
fn redundant_ternary_absorption_ignores_non_atom_operands() {
let member = AleoExpr::MemberAccess(Box::new(reg(1)), "field".to_string());
let mut stmts = vec![
AleoStmt::Ternary(reg(0), member, reg(2), reg_dest(3)),
AleoStmt::Ternary(reg(0), reg(3), reg(2), reg_dest(4)),
];

fold_redundant_ternaries(&mut stmts);

assert_eq!(stmts[1], AleoStmt::Ternary(reg(0), reg(3), reg(2), reg_dest(4)));
}
}
Loading