Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(perf): mem2reg function state for value loads to optimize across blocks #5757

Merged
merged 28 commits into from
Aug 19, 2024
Merged
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c7f70de
brillig opcode report workflow and small changes to info cmd for serde
vezenovm Aug 16, 2024
8acce99
update noir-gates-diff commit and chmod gates_report_brillig.sh
vezenovm Aug 16, 2024
d962871
increase circuit to test gates reports
vezenovm Aug 16, 2024
e54b80a
fix compile_success_empty
vezenovm Aug 16, 2024
e24932e
fix compile_success_empty
vezenovm Aug 16, 2024
e41b1d3
Merge branch 'master' into mv/brillig-opcode-report
vezenovm Aug 16, 2024
6b70236
Merge branch 'mv/brillig-opcode-report' into mv/test-brillig-opcode-r…
vezenovm Aug 16, 2024
60bbbdc
update header for brillig opcode report
vezenovm Aug 16, 2024
d65b066
use brillig_bytecode_diff for job id
vezenovm Aug 16, 2024
63b3d02
update commit
vezenovm Aug 16, 2024
b2be50e
Merge branch 'mv/brillig-opcode-report' into mv/test-brillig-opcode-r…
TomAFrench Aug 19, 2024
0ebc06e
add headers to the sticky comments
vezenovm Aug 19, 2024
85f4017
remove keccak256 test additions and add size regression from issue #4535
vezenovm Aug 19, 2024
4753d65
track the last loads across a function to cleanup blocks w/ unused st…
vezenovm Aug 19, 2024
f6342e4
update commit and pass brillig_report flag to github action
vezenovm Aug 19, 2024
7083c1e
Merge branch 'mv/test-brillig-opcode-report' into mv/mem2reg-small-fu…
vezenovm Aug 19, 2024
3a89d8e
udpate comment
vezenovm Aug 19, 2024
d978889
only insert last_load when load value is unknown
vezenovm Aug 19, 2024
5a024c4
fix mem2reg test after opt
vezenovm Aug 19, 2024
87d6f0b
update multiple_blocks test
vezenovm Aug 19, 2024
6472f23
add regression for issue 5761
vezenovm Aug 19, 2024
7d9612b
revert test added to wrong branch
vezenovm Aug 19, 2024
af35285
conflicts w/ master
vezenovm Aug 19, 2024
487879b
check we do not have a reference param
vezenovm Aug 19, 2024
cdfd93f
remvoe debug println
vezenovm Aug 19, 2024
fb49590
clippy
vezenovm Aug 19, 2024
8b56fb5
Update compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
vezenovm Aug 19, 2024
c1ef620
missing semicolon
vezenovm Aug 19, 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
48 changes: 34 additions & 14 deletions compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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 59 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 All @@ -66,6 +66,8 @@

use std::collections::{BTreeMap, BTreeSet};

use fxhash::FxHashMap as HashMap;

use crate::ssa::{
ir::{
basic_block::BasicBlockId,
Expand Down Expand Up @@ -109,8 +111,12 @@
/// Load and Store instructions that should be removed at the end of the pass.
///
/// We avoid removing individual instructions as we go since removing elements
/// from the middle of Vecs many times will be slower than a single call to `retain`.

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Vecs)
instructions_to_remove: BTreeSet<InstructionId>,

/// Track a value's last load across all blocks.
/// If a value is not used in anymore loads we can remove the last store to that value.
last_loads: HashMap<ValueId, InstructionId>,
}

impl<'f> PerFunctionContext<'f> {
Expand All @@ -124,6 +130,7 @@
inserter: FunctionInserter::new(function),
blocks: BTreeMap::new(),
instructions_to_remove: BTreeSet::new(),
last_loads: HashMap::default(),
}
}

Expand All @@ -140,6 +147,19 @@
let references = self.find_starting_references(block);
self.analyze_block(block, references);
}

// If we never load from an address within a function we can remove all stores to that address.
// This rule does not apply to reference parameters, which we must also check for before removing these stores.
for (block_id, block) in self.blocks.iter() {
let block_params = self.inserter.function.dfg.block_parameters(*block_id);
for (value, store_instruction) in block.last_stores.iter() {
let is_reference_param = block_params.contains(value)
&& self.inserter.function.dfg.value_is_reference(*value);
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
if self.last_loads.get(value).is_none() && !is_reference_param {
self.instructions_to_remove.insert(*store_instruction);
}
}
}
}

/// The value of each reference at the start of the given block is the unification
Expand Down Expand Up @@ -239,6 +259,8 @@
self.instructions_to_remove.insert(instruction);
} else {
references.mark_value_used(address, self.inserter.function);

self.last_loads.insert(address, instruction);
}
}
Instruction::Store { address, value } => {
Expand Down Expand Up @@ -594,10 +616,8 @@
// acir fn main f0 {
// b0():
// v7 = allocate
// store Field 5 at v7
// jmp b1(Field 5)
// b1(v3: Field):
// store Field 6 at v7
// return v3, Field 5, Field 6
// }
let ssa = ssa.mem2reg();
Expand All @@ -609,9 +629,9 @@
assert_eq!(count_loads(main.entry_block(), &main.dfg), 0);
assert_eq!(count_loads(b1, &main.dfg), 0);

// Neither store is removed since they are each the last in the block and there are multiple blocks
assert_eq!(count_stores(main.entry_block(), &main.dfg), 1);
assert_eq!(count_stores(b1, &main.dfg), 1);
// All stores are removed as there are no loads to the values being stored anywhere in the function.
assert_eq!(count_stores(main.entry_block(), &main.dfg), 0);
assert_eq!(count_stores(b1, &main.dfg), 0);

// The jmp to b1 should also be a constant 5 now
match main.dfg[main.entry_block()].terminator() {
Expand Down Expand Up @@ -641,8 +661,8 @@
// b1():
// store Field 1 at v3
// store Field 2 at v4
// v8 = load v3
// v9 = eq v8, Field 2
// v7 = load v3
// v8 = eq v7, Field 2
// return
// }
let main_id = Id::test_new(0);
Expand Down Expand Up @@ -681,12 +701,9 @@
// acir fn main f0 {
// b0():
// v9 = allocate
// store Field 0 at v9
// v10 = allocate
// store v9 at v10
// jmp b1()
// b1():
// store Field 2 at v9
// return
// }
let ssa = ssa.mem2reg();
Expand All @@ -698,14 +715,17 @@
assert_eq!(count_loads(main.entry_block(), &main.dfg), 0);
assert_eq!(count_loads(b1, &main.dfg), 0);

// Only the first store in b1 is removed since there is another store to the same reference
// All stores should be removed.
// The first store in b1 is removed since there is another store to the same reference
// in the same block, and the store is not needed before the later store.
assert_eq!(count_stores(main.entry_block(), &main.dfg), 2);
assert_eq!(count_stores(b1, &main.dfg), 1);
// The rest of the stores are also removed as no loads are done within any blocks
// to the stored values.
assert_eq!(count_stores(main.entry_block(), &main.dfg), 0);
assert_eq!(count_stores(b1, &main.dfg), 0);

let b1_instructions = main.dfg[b1].instructions();

// We expect the last eq to be optimized out
assert_eq!(b1_instructions.len(), 1);
assert_eq!(b1_instructions.len(), 0);
}
}
Loading