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: Handle no_predicates attribute #4942

Merged
merged 16 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ pub(crate) fn optimize_into_acir(
.run_pass(Ssa::remove_bit_shifts, "After Removing Bit Shifts:")
// Run mem2reg once more with the flattened CFG to catch any remaining loads/stores
.run_pass(Ssa::mem2reg, "After Mem2Reg:")
// Run the inlining pass again as certain codegen attributes will now be disabled after flattening,
// such as treating functions marked with the `InlineType::Never` as an entry point.
.run_pass(Ssa::inline_functions, "After Inlining:")
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
.run_pass(Ssa::fold_constants, "After Constant Folding:")
.run_pass(Ssa::remove_enable_side_effects, "After EnableSideEffects removal:")
.run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:")
Expand Down
11 changes: 9 additions & 2 deletions compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@
abi_distinctness: Distinctness,
) -> Result<(Vec<GeneratedAcir>, Vec<BrilligBytecode>), RuntimeError> {
let mut acirs = Vec::new();
// TODO: can we parallelise this?

Check warning on line 287 in compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (parallelise)
let mut shared_context = SharedContext::default();
for function in self.functions.values() {
let context = Context::new(&mut shared_context);
Expand Down Expand Up @@ -390,7 +390,10 @@
panic!("ACIR function should have been inlined earlier if not marked otherwise");
}
}
InlineType::Fold | InlineType::Never => {}
InlineType::Never => {
panic!("All ACIR functions marked with #[inline(never)] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute");
}
InlineType::Fold => {}
}
// We only want to convert entry point functions. This being `main` and those marked with `InlineType::Fold`
Ok(Some(self.convert_acir_main(function, ssa, brillig)?))
Expand Down Expand Up @@ -2653,7 +2656,11 @@
basic_call_with_outputs_assert(InlineType::Fold);
call_output_as_next_call_input(InlineType::Fold);
basic_nested_call(InlineType::Fold);
}

#[test]
#[should_panic]
fn basic_calls_inline_never() {
call_output_as_next_call_input(InlineType::Never);
basic_nested_call(InlineType::Never);
basic_call_with_outputs_assert(InlineType::Never);
Expand Down Expand Up @@ -2795,7 +2802,7 @@
let (acir_functions, _) = ssa
.into_acir(&Brillig::default(), noirc_frontend::ast::Distinctness::Distinct)
.expect("Should compile manually written SSA into ACIR");
// The expected result should look very similar to the abvoe test expect that the input witnesses of the `Call`
// The expected result should look very similar to the above test expect that the input witnesses of the `Call`
// opcodes will be different. The changes can discerned from the checks below.

let main_acir = &acir_functions[0];
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ impl Function {
self.runtime = runtime;
}

pub(crate) fn is_inline_never(&self) -> bool {
match self.runtime() {
RuntimeType::Acir(inline_type) => matches!(inline_type, InlineType::Never),
RuntimeType::Brillig => false,
}
}

/// Retrieves the entry block of a function.
///
/// A function's entry block contains the instructions
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ impl Ssa {
for function in self.functions.values_mut() {
flatten_function_cfg(function);
}
// Now that flattening has been completed we can have SSA generation inline any
// calls to functions marked with `#[inline(never)]`
self.use_inline_never = false;
self
}
}
Expand Down
25 changes: 16 additions & 9 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ impl Ssa {
/// as well save the work for later instead of performing it twice.
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn inline_functions(mut self) -> Ssa {
self.functions = btree_map(get_entry_point_functions(&self), |entry_point| {
let new_function = InlineContext::new(&self, entry_point).inline_all(&self);
(entry_point, new_function)
});
self.functions =
btree_map(get_entry_point_functions(&self, self.use_inline_never), |entry_point| {
let new_function = InlineContext::new(&self, entry_point).inline_all(&self);
(entry_point, new_function)
});

self
}
Expand Down Expand Up @@ -97,10 +98,13 @@ struct PerFunctionContext<'function> {
/// should be left in the final program.
/// This is the `main` function, any Acir functions with a [fold inline type][InlineType::Fold],
/// and any brillig functions used.
fn get_entry_point_functions(ssa: &Ssa) -> BTreeSet<FunctionId> {
fn get_entry_point_functions(ssa: &Ssa, use_inline_never: bool) -> BTreeSet<FunctionId> {
let functions = ssa.functions.iter();
let mut entry_points = functions
.filter(|(_, function)| function.runtime().is_entry_point())
.filter(|(_, function)| {
let inline_never = if use_inline_never { function.is_inline_never() } else { false };
function.runtime().is_entry_point() || inline_never
})
.map(|(id, _)| *id)
.collect::<BTreeSet<_>>();

Expand Down Expand Up @@ -351,11 +355,14 @@ impl<'function> PerFunctionContext<'function> {
for id in block.instructions() {
match &self.source_function.dfg[*id] {
Instruction::Call { func, arguments } => match self.get_function(*func) {
Some(function) => {
if ssa.functions[&function].runtime().is_entry_point() {
Some(func_id) => {
let function = &ssa.functions[&func_id];
let inline_never =
if ssa.use_inline_never { function.is_inline_never() } else { false };
if function.runtime().is_entry_point() || inline_never {
self.push_instruction(*id);
} else {
self.inline_function(ssa, *id, function, arguments);
self.inline_function(ssa, *id, func_id, arguments);
}
}
None => self.push_instruction(*id),
Expand Down
5 changes: 5 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ pub(crate) struct Ssa {
/// This mapping is necessary to use the correct function pointer for an ACIR call,
/// as the final program artifact will be a list of only entry point functions.
pub(crate) entry_point_to_generated_index: BTreeMap<FunctionId, u32>,
/// A flag to indicate how we should perform codegen in between passes.
/// This is currently defaulted to `true` and should be set to false by a pass at some point
/// before ACIR generation.
pub(crate) use_inline_never: bool,
}

impl Ssa {
Expand Down Expand Up @@ -50,6 +54,7 @@ impl Ssa {
main_id,
next_id: AtomicCounter::starting_after(max_id),
entry_point_to_generated_index,
use_inline_never: true,
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/monomorphization/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ impl InlineType {
match self {
InlineType::Inline => false,
InlineType::Fold => true,
InlineType::Never => true,
InlineType::Never => false,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "inline_numeric_generic_poseidon"
type = "bin"
authors = [""]
compiler_version = ">=0.28.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
enable = [true, false]
to_hash = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2};

global NUM_HASHES = 2;
global HASH_LENGTH = 10;

#[inline(never)]
pub fn poseidon_hash<N>(inputs: [Field; N]) -> Field {
Poseidon2::hash(inputs, inputs.len())
}

fn main(
to_hash: [[Field; HASH_LENGTH]; NUM_HASHES],
enable: [bool; NUM_HASHES]
) -> pub [Field; NUM_HASHES + 1] {
let mut result = [0; NUM_HASHES + 1];
for i in 0..NUM_HASHES {
let enable = enable[i];
let to_hash = to_hash[i];
if enable {
result[i] = poseidon_hash(to_hash);
}
}

// We want to make sure that the function marked with `#[inline(never)]` with a numeric generic
// is monomorphized correctly.
let mut double_preimage = [0; 20];
for i in 0..HASH_LENGTH * 2 {
double_preimage[i] = to_hash[0][i % HASH_LENGTH];
}
result[NUM_HASHES] = poseidon_hash(double_preimage);

result
}
1 change: 1 addition & 0 deletions tooling/debugger/ignored-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ fold_call_witness_condition
fold_after_inlined_calls
fold_numeric_generic_poseidon
inline_never_basic
inline_numeric_generic_poseidon
regression_4709
Loading