Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
38a5659
fix: add a `remove_unreachable_instructions` SSA pass
asterite Jun 19, 2025
2702a7d
Add a regression test
asterite Jun 19, 2025
1f59a54
Run it before removing unreachable functions
asterite Jun 19, 2025
d336b7d
Slightly improve pass name
asterite Jun 19, 2025
1d80bd8
Simplify logic
asterite Jun 19, 2025
ea2378d
If a block is unreachable, its successors are too
asterite Jun 19, 2025
36902d8
Traverse blocks in pre-order
asterite Jun 19, 2025
aaa9c0e
All successors are unreachable unless we saw them before
asterite Jun 19, 2025
d0d9ab2
Update snapshots
asterite Jun 19, 2025
cfa132a
Add all successors transitively right away
asterite Jun 19, 2025
50ab93a
One more snapshot
asterite Jun 19, 2025
da3d2c2
DominatorTree to the rescue!
asterite Jun 19, 2025
f1eeaf2
Snapshots
asterite Jun 19, 2025
c4407d0
Merge branch 'master' into ab/ssa-remove-unreachable-instructions
asterite Jun 19, 2025
5e2f39e
`simple_reachable_blocks_optimization` is fine
asterite Jun 23, 2025
4d314ac
add_successors -> add_dominated_blocks
asterite Jun 23, 2025
b2b7865
More renames and comments
asterite Jun 23, 2025
1375346
Comment
asterite Jun 23, 2025
763ce42
Rename tests
asterite Jun 23, 2025
9638edb
Add an unreachable terminator
asterite Jun 23, 2025
426f390
Produce unreachable terminators
asterite Jun 23, 2025
cd83277
Assume unreachable can't be reached
asterite Jun 23, 2025
e21e963
Add a test for constrain not equal
asterite Jun 23, 2025
5b7e58b
Snapshots
asterite Jun 23, 2025
88ffc6d
Merge branch 'master' into ab/ssa-remove-unreachable-instructions
asterite Jun 23, 2025
f06f049
Fix comment
asterite Jun 23, 2025
6118feb
Better handling of unreachable terminator
asterite Jun 23, 2025
3b0cce7
Add another test
asterite Jun 23, 2025
216941f
Remove unreachable instructions in the first SSA pass
asterite Jun 23, 2025
77e8e75
Revert "Remove unreachable instructions in the first SSA pass"
asterite Jun 23, 2025
6e4e138
Handle unreachable during inlining with a panic
asterite Jun 23, 2025
ad85129
A a regression for #8994
asterite Jun 24, 2025
496d07a
Rename `remove_unreachable` to `remove_unreachable_functions`
asterite Jun 24, 2025
2eeea5b
Merge branch 'master' into ab/ssa-remove-unreachable-instructions-wit…
asterite Jun 24, 2025
1574951
No need to mark dominated blocks as unreachable
asterite Jun 24, 2025
167f12c
Extract `always_fails`
asterite Jun 24, 2025
1da08b5
Clarify comment
asterite Jun 24, 2025
85a1c3c
Rename some tests
asterite Jun 24, 2025
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: 8 additions & 2 deletions compiler/noirc_evaluator/src/acir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,8 +915,11 @@ impl<'a> Context<'a> {
) -> usize {
let return_values = match terminator {
TerminatorInstruction::Return { return_values, .. } => return_values,
TerminatorInstruction::Unreachable { .. } => return 0,
// TODO(https://github.com/noir-lang/noir/issues/4616): Enable recursion on foldable/non-inlined ACIR functions
_ => unreachable!("ICE: Program must have a singular return"),
TerminatorInstruction::JmpIf { .. } | TerminatorInstruction::Jmp { .. } => {
unreachable!("ICE: Program must have a singular return")
}
};

return_values
Expand All @@ -935,7 +938,10 @@ impl<'a> Context<'a> {
(return_values, *call_stack)
}
// TODO(https://github.com/noir-lang/noir/issues/4616): Enable recursion on foldable/non-inlined ACIR functions
_ => unreachable!("ICE: Program must have a singular return"),
TerminatorInstruction::JmpIf { .. } | TerminatorInstruction::Jmp { .. } => {
unreachable!("ICE: Program must have a singular return")
}
TerminatorInstruction::Unreachable { .. } => return Ok((vec![], vec![])),
};

let mut has_constant_return = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
});
self.brillig_context.codegen_return(&return_registers);
}
TerminatorInstruction::Unreachable { .. } => {
// If we assume this is unreachable code then there's nothing to do here
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
SsaPass::new(Ssa::brillig_array_get_and_set, "Brillig Array Get and Set Optimizations"),
// Perform another DIE pass to update the used globals after offsetting Brillig indexes.
SsaPass::new(Ssa::dead_instruction_elimination, "Dead Instruction Elimination"),
// A function can be potentially unreachable post-DIE if all calls to that function were removed.
SsaPass::new(Ssa::remove_unreachable_instructions, "Remove Unreachable Instructions"),
// A function can be potentially unreachable post-DIE if all calls to that function were removed,
// or after the removal of unreachable instructions.
SsaPass::new(Ssa::remove_unreachable_functions, "Removing Unreachable Functions"),
SsaPass::new(Ssa::checked_to_unchecked, "Checked to unchecked"),
SsaPass::new_try(
Expand All @@ -229,6 +231,7 @@ pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
pub fn secondary_passes(brillig: &Brillig) -> Vec<SsaPass> {
vec![
SsaPass::new(move |ssa| ssa.fold_constants_with_brillig(brillig), "Inlining Brillig Calls"),
SsaPass::new(Ssa::remove_unreachable_instructions, "Remove Unreachable Instructions"),
// It could happen that we inlined all calls to a given brillig function.
// In that case it's unused so we can remove it. This is what we check next.
SsaPass::new(Ssa::remove_unreachable_functions, "Removing Unreachable Functions"),
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ impl FunctionBuilder {
self.terminate_block_with(TerminatorInstruction::Return { return_values, call_stack });
}

/// Terminate the current block with an unreachable instruction
pub fn terminate_with_unreachable(&mut self) {
let call_stack = self.call_stack;
self.terminate_block_with(TerminatorInstruction::Unreachable { call_stack });
}

/// Returns a ValueId pointing to the given function or imports the function
/// into the current function if it was not already, and returns that ID.
pub fn import_function(&mut self, function: FunctionId) -> ValueId {
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_evaluator/src/ssa/interpreter/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum InterpreterError {
ToRadixFailed { field_id: ValueId, field: FieldElement, radix: u32 },
#[error("Failed to solve blackbox function {name}: {reason}")]
BlackBoxError { name: String, reason: String },
#[error("Reached the unreachable")]
ReachedTheUnreachable,
}

/// These errors can only result from interpreting malformed SSA
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@

break return_values;
}
Some(TerminatorInstruction::Unreachable { .. }) => {
return Err(InterpreterError::ReachedTheUnreachable);
}
}
};

Expand Down Expand Up @@ -1174,7 +1177,7 @@
}));
}

// Disable this instruction if it is side-effectful and side effects are disabled.

Check warning on line 1180 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (effectful)
if !side_effects_enabled {
let zero = NumericValue::zero(lhs.get_type());
return Ok(Value::Numeric(zero));
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_evaluator/src/ssa/ir/basic_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ impl BasicBlock {
Some(TerminatorInstruction::JmpIf { then_destination, else_destination, .. }) => {
vec![*then_destination, *else_destination].into_iter()
}
Some(TerminatorInstruction::Return { .. }) => vec![].into_iter(),
None => vec![].into_iter(),
Some(TerminatorInstruction::Return { .. })
| Some(TerminatorInstruction::Unreachable { .. })
| None => vec![].into_iter(),
}
}
}
15 changes: 12 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,10 @@ pub(crate) enum TerminatorInstruction {
/// as the block arguments. Then the exit block can terminate in a return
/// instruction returning these values.
Return { return_values: Vec<ValueId>, call_stack: CallStackId },

/// A terminator that will never be reached because an instruction in its block
/// will always produce an assertion failure.
Unreachable { call_stack: CallStackId },
}

impl TerminatorInstruction {
Expand All @@ -873,6 +877,7 @@ impl TerminatorInstruction {
*return_value = f(*return_value);
}
}
Unreachable { .. } => (),
}
}

Expand All @@ -893,6 +898,7 @@ impl TerminatorInstruction {
f(*return_value);
}
}
Unreachable { .. } => (),
}
}

Expand All @@ -913,6 +919,7 @@ impl TerminatorInstruction {
f(index, *return_value);
}
}
Unreachable { .. } => (),
}
}

Expand All @@ -927,23 +934,25 @@ impl TerminatorInstruction {
Jmp { destination, .. } => {
*destination = f(*destination);
}
Return { .. } => (),
Return { .. } | Unreachable { .. } => (),
}
}

pub(crate) fn call_stack(&self) -> CallStackId {
match self {
TerminatorInstruction::JmpIf { call_stack, .. }
| TerminatorInstruction::Jmp { call_stack, .. }
| TerminatorInstruction::Return { call_stack, .. } => *call_stack,
| TerminatorInstruction::Return { call_stack, .. }
| TerminatorInstruction::Unreachable { call_stack } => *call_stack,
}
}

pub(crate) fn set_call_stack(&mut self, new_call_stack: CallStackId) {
match self {
TerminatorInstruction::JmpIf { call_stack, .. }
| TerminatorInstruction::Jmp { call_stack, .. }
| TerminatorInstruction::Return { call_stack, .. } => *call_stack = new_call_stack,
| TerminatorInstruction::Return { call_stack, .. }
| TerminatorInstruction::Unreachable { call_stack } => *call_stack = new_call_stack,
}
}
}
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ fn display_terminator(
writeln!(f, " return {}", value_list(dfg, return_values))
}
}
Some(TerminatorInstruction::Unreachable { .. }) => {
writeln!(f, " unreachable")
}
None => writeln!(f, " (no terminator instruction)"),
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ impl Context<'_> {
let return_values = vecmap(return_values, |value| self.inserter.resolve(value));
TerminatorInstruction::Return { return_values, call_stack }
}
TerminatorInstruction::Unreachable { call_stack } => {
TerminatorInstruction::Unreachable { call_stack }
}
};
self.inserter.function.dfg.set_block_terminator(conditional.block_entry, new_terminator);
self.inserter.map_data_bus_in_place();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ impl Function {
TerminatorInstruction::Return { .. } => {
unreachable!("ICE: A return block should not be a predecessor");
}
TerminatorInstruction::Unreachable { .. } => {
unreachable!("ICE: An unreachable block should not be a predecessor");
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,10 @@ impl<'f> Context<'f> {
self.inserter.function.dfg.set_block_terminator(target, new_return);
vec![]
}
TerminatorInstruction::Unreachable { .. } => {
// Nothing to do
vec![]
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,12 @@ impl<'function> PerFunctionContext<'function> {

Some((block_id, return_values))
}
TerminatorInstruction::Unreachable { .. } => {
// Note: `unreachable` terminators are only added during the `remove_unreachable_instructions`,
// which runs near the end of the optimization pipeline, so never before inlining.
// If we ever want to run it before inlining we'll have to handle this case.
panic!("Unreachable terminator instruction should not exist during inlining.")
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
//!
//! Repeating this algorithm for each block in the function in program order should result in
//! optimizing out most known loads. However, identifying all aliases correctly has been proven
//! undecidable in general (Landi, 1992). So this pass will not always optimize out all loads

Check warning on line 71 in compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Landi)
//! that could theoretically be optimized out. This pass can be performed at any time in the
//! SSA optimization pipeline, although it will be more successful the simpler the program's CFG is.
//! This pass is currently performed several times to enable other passes - most notably being
Expand Down Expand Up @@ -619,7 +619,7 @@
self.inserter.map_terminator_in_place(block);

match self.inserter.function.dfg[block].unwrap_terminator() {
TerminatorInstruction::JmpIf { .. } => (), // Nothing to do
TerminatorInstruction::JmpIf { .. } | TerminatorInstruction::Unreachable { .. } => (), // Nothing to do
TerminatorInstruction::Jmp { destination, arguments, .. } => {
let destination_parameters = self.inserter.function.dfg[*destination].parameters();
assert_eq!(destination_parameters.len(), arguments.len());
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/opt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ mod remove_bit_shifts;
mod remove_enable_side_effects;
mod remove_if_else;
mod remove_truncate_after_range_check;
mod remove_unreachable;
mod remove_unreachable_functions;
mod remove_unreachable_instructions;
mod simple_optimization;
mod simplify_cfg;
mod unrolling;
Expand Down
Loading
Loading