Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4428074
wip: attempt SSA optimizations on zero-arg functions first, move per-…
michaeljklein Sep 17, 2024
76ee498
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 18, 2024
999231b
wip debugging non-idempotent passes, added pass for zero-arg functions
michaeljklein Sep 18, 2024
ca540a3
disable 0-arg constant folding for now because it's contradicting the…
michaeljklein Sep 18, 2024
3476d4e
cargo clippy/fmt
michaeljklein Sep 26, 2024
fcaef1b
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 26, 2024
143358d
disable array set optimizations when assumptions don't hold, cargo cl…
michaeljklein Sep 26, 2024
a7a6f18
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 26, 2024
4621a59
only run 0-arg SSA pass on brillig functions
michaeljklein Sep 26, 2024
5d9a4dc
re-enable reachable_blocks.len() == 1 assertion
michaeljklein Sep 26, 2024
80e59e5
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 26, 2024
24f076f
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 30, 2024
3c6d4c8
patch after merge, remove 0-arg SSA pass
michaeljklein Sep 30, 2024
e8cda19
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Sep 30, 2024
c0115a1
Merge branch 'master' into michaeljklein/zero-arg-ssa-optimizations
michaeljklein Oct 1, 2024
4ccc9a1
re-remove bubble_up_constrains, add issue for combining Ssa::unroll_l…
michaeljklein Oct 1, 2024
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
49 changes: 28 additions & 21 deletions compiler/noirc_evaluator/src/ssa/opt/array_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::ssa::{
ir::{
basic_block::BasicBlockId,
dfg::DataFlowGraph,
function::Function,
instruction::{Instruction, InstructionId, TerminatorInstruction},
types::Type::{Array, Slice},
value::ValueId,
Expand All @@ -17,32 +18,38 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn array_set_optimization(mut self) -> Self {
for func in self.functions.values_mut() {
let reachable_blocks = func.reachable_blocks();

if !func.runtime().is_entry_point() {
assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization");
}
let mut array_to_last_use = HashMap::default();
let mut instructions_to_update = HashSet::default();
let mut arrays_from_load = HashSet::default();

for block in reachable_blocks.iter() {
analyze_last_uses(
&func.dfg,
*block,
&mut array_to_last_use,
&mut instructions_to_update,
&mut arrays_from_load,
);
}
for block in reachable_blocks {
make_mutable(&mut func.dfg, block, &instructions_to_update);
}
func.array_set_optimization();
}
self
}
}

impl Function {
pub(crate) fn array_set_optimization(&mut self) {
let reachable_blocks = self.reachable_blocks();

if !self.runtime().is_entry_point() {
assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization");
}
let mut array_to_last_use = HashMap::default();
let mut instructions_to_update = HashSet::default();
let mut arrays_from_load = HashSet::default();

for block in reachable_blocks.iter() {
analyze_last_uses(
&self.dfg,
*block,
&mut array_to_last_use,
&mut instructions_to_update,
&mut arrays_from_load,
);
}
for block in reachable_blocks {
make_mutable(&mut self.dfg, block, &instructions_to_update);
}
}
}

/// Builds the set of ArraySet instructions that can be made mutable
/// because their input value is unused elsewhere afterward.
fn analyze_last_uses(
Expand Down
10 changes: 8 additions & 2 deletions compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn as_slice_optimization(mut self) -> Self {
for func in self.functions.values_mut() {
let known_slice_lengths = known_slice_lengths(func);
replace_known_slice_lengths(func, known_slice_lengths);
func.as_slice_optimization();
}
self
}
}

impl Function {
pub(crate) fn as_slice_optimization(&mut self) {
let known_slice_lengths = known_slice_lengths(self);
replace_known_slice_lengths(self, known_slice_lengths);
}
}

fn known_slice_lengths(func: &Function) -> HashMap<InstructionId, usize> {
let mut known_slice_lengths = HashMap::default();
for block_id in func.reachable_blocks() {
Expand Down
33 changes: 21 additions & 12 deletions compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,31 @@ impl Ssa {
mut self,
) -> Result<Ssa, RuntimeError> {
for function in self.functions.values_mut() {
for block in function.reachable_blocks() {
// Unfortunately we can't just use instructions.retain(...) here since
// check_instruction can also return an error
let instructions = function.dfg[block].take_instructions();
let mut filtered_instructions = Vec::with_capacity(instructions.len());
function.evaluate_static_assert_and_assert_constant()?;
}
Ok(self)
}
}

for instruction in instructions {
if check_instruction(function, instruction)? {
filtered_instructions.push(instruction);
}
}
impl Function {
pub(crate) fn evaluate_static_assert_and_assert_constant(
&mut self,
) -> Result<(), RuntimeError> {
for block in self.reachable_blocks() {
// Unfortunately we can't just use instructions.retain(...) here since
// check_instruction can also return an error
let instructions = self.dfg[block].take_instructions();
let mut filtered_instructions = Vec::with_capacity(instructions.len());

*function.dfg[block].instructions_mut() = filtered_instructions;
for instruction in instructions {
if check_instruction(self, instruction)? {
filtered_instructions.push(instruction);
}
}

*self.dfg[block].instructions_mut() = filtered_instructions;
}
Ok(self)
Ok(())
}
}

Expand Down
28 changes: 15 additions & 13 deletions compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn fold_constants(mut self) -> Ssa {
for function in self.functions.values_mut() {
constant_fold(function, false);
function.constant_fold(false);
}
self
}
Expand All @@ -57,25 +57,27 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn fold_constants_using_constraints(mut self) -> Ssa {
for function in self.functions.values_mut() {
constant_fold(function, true);
function.constant_fold(true);
}
self
}
}

/// The structure of this pass is simple:
/// Go through each block and re-insert all instructions.
fn constant_fold(function: &mut Function, use_constraint_info: bool) {
let mut context = Context { use_constraint_info, ..Default::default() };
context.block_queue.push(function.entry_block());
impl Function {
/// The structure of this pass is simple:
/// Go through each block and re-insert all instructions.
pub(crate) fn constant_fold(&mut self, use_constraint_info: bool) {
let mut context = Context { use_constraint_info, ..Default::default() };
context.block_queue.push(self.entry_block());

while let Some(block) = context.block_queue.pop() {
if context.visited_blocks.contains(&block) {
continue;
}
while let Some(block) = context.block_queue.pop() {
if context.visited_blocks.contains(&block) {
continue;
}

context.visited_blocks.insert(block);
context.fold_constants_in_block(function, block);
context.visited_blocks.insert(block);
context.fold_constants_in_block(self, block);
}
}
}

Expand Down
60 changes: 31 additions & 29 deletions compiler/noirc_evaluator/src/ssa/opt/die.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,46 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn dead_instruction_elimination(mut self) -> Ssa {
for function in self.functions.values_mut() {
dead_instruction_elimination(function, true);
function.dead_instruction_elimination(true);
}
self
}
}

/// Removes any unused instructions in the reachable blocks of the given function.
///
/// The blocks of the function are iterated in post order, such that any blocks containing
/// instructions that reference results from an instruction in another block are evaluated first.
/// If we did not iterate blocks in this order we could not safely say whether or not the results
/// of its instructions are needed elsewhere.
fn dead_instruction_elimination(function: &mut Function, insert_out_of_bounds_checks: bool) {
let mut context = Context::default();
for call_data in &function.dfg.data_bus.call_data {
context.mark_used_instruction_results(&function.dfg, call_data.array_id);
}
impl Function {
/// Removes any unused instructions in the reachable blocks of the given function.
///
/// The blocks of the function are iterated in post order, such that any blocks containing
/// instructions that reference results from an instruction in another block are evaluated first.
/// If we did not iterate blocks in this order we could not safely say whether or not the results
/// of its instructions are needed elsewhere.
pub(crate) fn dead_instruction_elimination(&mut self, insert_out_of_bounds_checks: bool) {
let mut context = Context::default();
for call_data in &self.dfg.data_bus.call_data {
context.mark_used_instruction_results(&self.dfg, call_data.array_id);
}

let mut inserted_out_of_bounds_checks = false;
let mut inserted_out_of_bounds_checks = false;

let blocks = PostOrder::with_function(function);
for block in blocks.as_slice() {
inserted_out_of_bounds_checks |= context.remove_unused_instructions_in_block(
function,
*block,
insert_out_of_bounds_checks,
);
}
let blocks = PostOrder::with_function(self);
for block in blocks.as_slice() {
inserted_out_of_bounds_checks |= context.remove_unused_instructions_in_block(
self,
*block,
insert_out_of_bounds_checks,
);
}

// If we inserted out of bounds check, let's run the pass again with those new
// instructions (we don't want to remove those checks, or instructions that are
// dependencies of those checks)
if inserted_out_of_bounds_checks {
dead_instruction_elimination(function, false);
return;
}
// If we inserted out of bounds check, let's run the pass again with those new
// instructions (we don't want to remove those checks, or instructions that are
// dependencies of those checks)
if inserted_out_of_bounds_checks {
self.dead_instruction_elimination(false);
return;
}

context.remove_rc_instructions(&mut function.dfg);
context.remove_rc_instructions(&mut self.dfg);
}
}

/// Per function context for tracking unused values and which instructions to remove.
Expand Down
14 changes: 10 additions & 4 deletions compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,22 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn mem2reg(mut self) -> Ssa {
for function in self.functions.values_mut() {
let mut context = PerFunctionContext::new(function);
context.mem2reg();
context.remove_instructions();
context.update_data_bus();
function.mem2reg();
}

self
}
}

impl Function {
pub(crate) fn mem2reg(&mut self) {
let mut context = PerFunctionContext::new(self);
context.mem2reg();
context.remove_instructions();
context.update_data_bus();
}
}

struct PerFunctionContext<'f> {
cfg: ControlFlowGraph,
post_order: PostOrder,
Expand Down
40 changes: 21 additions & 19 deletions compiler/noirc_evaluator/src/ssa/opt/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn remove_paired_rc(mut self) -> Ssa {
for function in self.functions.values_mut() {
remove_paired_rc(function);
function.remove_paired_rc();
}
self
}
Expand All @@ -44,26 +44,28 @@ pub(crate) struct RcInstruction {
pub(crate) possibly_mutated: bool,
}

/// This function is very simplistic for now. It takes advantage of the fact that dec_rc
/// instructions are currently issued only at the end of a function for parameters and will
/// only check the first and last block for inc & dec rc instructions to be removed. The rest
/// of the function is still checked for array_set instructions.
///
/// This restriction lets this function largely ignore merging intermediate results from other
/// blocks and handling loops.
fn remove_paired_rc(function: &mut Function) {
// `dec_rc` is only issued for parameters currently so we can speed things
// up a bit by skipping any functions without them.
if !contains_array_parameter(function) {
return;
}
impl Function {
/// This function is very simplistic for now. It takes advantage of the fact that dec_rc
/// instructions are currently issued only at the end of a function for parameters and will
/// only check the first and last block for inc & dec rc instructions to be removed. The rest
/// of the function is still checked for array_set instructions.
///
/// This restriction lets this function largely ignore merging intermediate results from other
/// blocks and handling loops.
pub(crate) fn remove_paired_rc(&mut self) {
// `dec_rc` is only issued for parameters currently so we can speed things
// up a bit by skipping any functions without them.
if !contains_array_parameter(self) {
return;
}

let mut context = Context::default();
let mut context = Context::default();

context.find_rcs_in_entry_block(function);
context.scan_for_array_sets(function);
let to_remove = context.find_rcs_to_remove(function);
remove_instructions(to_remove, function);
context.find_rcs_in_entry_block(self);
context.scan_for_array_sets(self);
let to_remove = context.find_rcs_to_remove(self);
remove_instructions(to_remove, self);
}
}

fn contains_array_parameter(function: &mut Function) -> bool {
Expand Down
28 changes: 17 additions & 11 deletions compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,30 @@ impl Ssa {
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn remove_bit_shifts(mut self) -> Ssa {
for function in self.functions.values_mut() {
remove_bit_shifts(function);
function.remove_bit_shifts();
}
self
}
}

/// The structure of this pass is simple:
/// Go through each block and re-insert all instructions.
fn remove_bit_shifts(function: &mut Function) {
if let RuntimeType::Brillig = function.runtime() {
return;
}
impl Function {
/// The structure of this pass is simple:
/// Go through each block and re-insert all instructions.
pub(crate) fn remove_bit_shifts(&mut self) {
if let RuntimeType::Brillig = self.runtime() {
return;
}

let block = function.entry_block();
let mut context =
Context { function, new_instructions: Vec::new(), block, call_stack: CallStack::default() };
let block = self.entry_block();
let mut context = Context {
function: self,
new_instructions: Vec::new(),
block,
call_stack: CallStack::default(),
};

context.remove_bit_shifts();
context.remove_bit_shifts();
}
}

struct Context<'f> {
Expand Down
Loading