From 0ce04d3ea8734b76d96f5dd0fb2a6cdd4081969e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 30 Apr 2024 21:13:15 +0100 Subject: [PATCH] feat: Handle `no_predicates` attribute (#4942) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4911 and https://github.com/noir-lang/noir/issues/4688 ## Summary\* ~~We recently included the `#[inline(never)]` attribute to enable developers to optimize codegen.~~ This has now been switched to the name `no_predicates`. The main use-case in mind is for circuits in issue #4688 where inlining a function with heavy array operations when dependent upon witnesses is not always ideal. Specifically when the function being inlined does not need to rely on the predicate for correctness. Originally I had in mind to delay inlining all the way to after ACIR gen and inline the ACIR artifacts. However, this feels overly complex now as we have all the infrastructure to inline functions as we wish during SSA, we could just need to delay the inlining of certain functions to happen after flattening. This PR does exactly what was just mentioned. For example, the new test `no_predicates_numeric_generic_poseidon` gave these results when `poseidon_hash` was not marked with `#[no_predicates_numeric_generic_poseidon]`: ExistingInlineStrategy While when `poseidon_hash` was marked with `#[no_predicates_numeric_generic_poseidon]`: InlineNeverBench ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [X] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa.rs | 3 + .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 17 +++-- .../noirc_evaluator/src/ssa/ir/function.rs | 7 ++ .../noirc_evaluator/src/ssa/opt/inlining.rs | 68 +++++++++++++++---- compiler/noirc_frontend/src/ast/function.rs | 2 +- .../src/hir/resolution/errors.rs | 12 ++-- .../src/hir/resolution/resolver.rs | 12 ++-- .../noirc_frontend/src/hir/type_check/mod.rs | 4 +- .../noirc_frontend/src/hir_def/function.rs | 5 +- compiler/noirc_frontend/src/lexer/token.rs | 19 +++--- .../src/monomorphization/ast.rs | 21 +++--- compiler/noirc_frontend/src/tests.rs | 7 +- .../Nargo.toml | 2 +- .../Prover.toml | 0 .../src/main.nr | 2 +- .../Nargo.toml | 7 ++ .../Prover.toml | 2 + .../src/main.nr | 33 +++++++++ tooling/debugger/ignored-tests.txt | 3 +- 19 files changed, 164 insertions(+), 62 deletions(-) rename test_programs/execution_success/{inline_never_basic => no_predicates_basic}/Nargo.toml (74%) rename test_programs/execution_success/{inline_never_basic => no_predicates_basic}/Prover.toml (100%) rename test_programs/execution_success/{inline_never_basic => no_predicates_basic}/src/main.nr (87%) create mode 100644 test_programs/execution_success/no_predicates_numeric_generic_poseidon/Nargo.toml create mode 100644 test_programs/execution_success/no_predicates_numeric_generic_poseidon/Prover.toml create mode 100644 test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index f9fd5f41f77..edc32fddeaf 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -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 to handle functions with `InlineType::NoPredicates`. + // Before flattening is run, we treat functions marked with the `InlineType::NoPredicates` as an entry point. + .run_pass(Ssa::inline_functions_with_no_predicates, "After Inlining:") .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:") diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 63d1c1b564f..16680c2b7eb 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -390,7 +390,10 @@ impl<'a> Context<'a> { panic!("ACIR function should have been inlined earlier if not marked otherwise"); } } - InlineType::Fold | InlineType::Never => {} + InlineType::NoPredicates => { + panic!("All ACIR functions marked with #[no_predicates] 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)?)) @@ -2653,10 +2656,14 @@ mod test { basic_call_with_outputs_assert(InlineType::Fold); call_output_as_next_call_input(InlineType::Fold); basic_nested_call(InlineType::Fold); + } - call_output_as_next_call_input(InlineType::Never); - basic_nested_call(InlineType::Never); - basic_call_with_outputs_assert(InlineType::Never); + #[test] + #[should_panic] + fn basic_calls_no_predicates() { + call_output_as_next_call_input(InlineType::NoPredicates); + basic_nested_call(InlineType::NoPredicates); + basic_call_with_outputs_assert(InlineType::NoPredicates); } #[test] @@ -2795,7 +2802,7 @@ mod test { 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]; diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 057786bf5ec..a49e02b0380 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -85,6 +85,13 @@ impl Function { self.runtime = runtime; } + pub(crate) fn is_no_predicates(&self) -> bool { + match self.runtime() { + RuntimeType::Acir(inline_type) => matches!(inline_type, InlineType::NoPredicates), + RuntimeType::Brillig => false, + } + } + /// Retrieves the entry block of a function. /// /// A function's entry block contains the instructions diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 0b78d47fbb1..bddfb25f26c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -36,13 +36,30 @@ impl Ssa { /// changes. This is because if the function's id later becomes known by a later /// pass, we would need to re-run all of inlining anyway to inline it, so we might /// as well save the work for later instead of performing it twice. + /// + /// There are some attributes that allow inlining a function at a different step of codegen. + /// Currently this is just `InlineType::NoPredicates` for which we have a flag indicating + /// whether treating that inline functions. The default is to treat these functions as entry points. #[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) - }); + pub(crate) fn inline_functions(self) -> Ssa { + Self::inline_functions_inner(self, true) + } + + // Run the inlining pass where functions marked with `InlineType::NoPredicates` as not entry points + pub(crate) fn inline_functions_with_no_predicates(self) -> Ssa { + Self::inline_functions_inner(self, false) + } + fn inline_functions_inner(mut self, no_predicates_is_entry_point: bool) -> Ssa { + self.functions = btree_map( + get_entry_point_functions(&self, no_predicates_is_entry_point), + |entry_point| { + let new_function = + InlineContext::new(&self, entry_point, no_predicates_is_entry_point) + .inline_all(&self); + (entry_point, new_function) + }, + ); self } } @@ -60,6 +77,8 @@ struct InlineContext { // The FunctionId of the entry point function we're inlining into in the old, unmodified Ssa. entry_point: FunctionId, + + no_predicates_is_entry_point: bool, } /// The per-function inlining context contains information that is only valid for one function. @@ -97,10 +116,19 @@ 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 { +fn get_entry_point_functions( + ssa: &Ssa, + no_predicates_is_entry_point: bool, +) -> BTreeSet { let functions = ssa.functions.iter(); let mut entry_points = functions - .filter(|(_, function)| function.runtime().is_entry_point()) + .filter(|(_, function)| { + // If we have not already finished the flattening pass, functions marked + // to not have predicates should be marked as entry points. + let no_predicates_is_entry_point = + no_predicates_is_entry_point && function.is_no_predicates(); + function.runtime().is_entry_point() || no_predicates_is_entry_point + }) .map(|(id, _)| *id) .collect::>(); @@ -114,11 +142,21 @@ impl InlineContext { /// The function being inlined into will always be the main function, although it is /// actually a copy that is created in case the original main is still needed from a function /// that could not be inlined calling it. - fn new(ssa: &Ssa, entry_point: FunctionId) -> InlineContext { + fn new( + ssa: &Ssa, + entry_point: FunctionId, + no_predicates_is_entry_point: bool, + ) -> InlineContext { let source = &ssa.functions[&entry_point]; let mut builder = FunctionBuilder::new(source.name().to_owned(), entry_point); builder.set_runtime(source.runtime()); - Self { builder, recursion_level: 0, entry_point, call_stack: CallStack::new() } + Self { + builder, + recursion_level: 0, + entry_point, + call_stack: CallStack::new(), + no_predicates_is_entry_point, + } } /// Start inlining the entry point function and all functions reachable from it. @@ -351,11 +389,17 @@ 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]; + // If we have not already finished the flattening pass, functions marked + // to not have predicates should be marked as entry points. + let no_predicates_is_entry_point = + self.context.no_predicates_is_entry_point + && function.is_no_predicates(); + if function.runtime().is_entry_point() || no_predicates_is_entry_point { 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), diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index 9115178671e..dc426a4642a 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -109,7 +109,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, Some(FunctionAttribute::Fold) => FunctionKind::Normal, - Some(FunctionAttribute::Inline(_)) => FunctionKind::Normal, + Some(FunctionAttribute::NoPredicates) => FunctionKind::Normal, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 1727471c34f..953edc7c7d9 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -90,8 +90,8 @@ pub enum ResolverError { MutableGlobal { span: Span }, #[error("Self-referential structs are not supported")] SelfReferentialStruct { span: Span }, - #[error("#[inline(tag)] attribute is only allowed on constrained functions")] - InlineAttributeOnUnconstrained { ident: Ident }, + #[error("#[no_predicates] attribute is only allowed on constrained functions")] + NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, } @@ -362,16 +362,16 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, - ResolverError::InlineAttributeOnUnconstrained { ident } => { + ResolverError::NoPredicatesAttributeOnUnconstrained { ident } => { let name = &ident.0.contents; let mut diag = Diagnostic::simple_error( - format!("misplaced #[inline(tag)] attribute on unconstrained function {name}. Only allowed on constrained functions"), - "misplaced #[inline(tag)] attribute".to_string(), + format!("misplaced #[no_predicates] attribute on unconstrained function {name}. Only allowed on constrained functions"), + "misplaced #[no_predicates] attribute".to_string(), ident.0.span(), ); - diag.add_note("The `#[inline(tag)]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); + diag.add_note("The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); diag } ResolverError::FoldAttributeOnUnconstrained { ident } => { diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index bef0ebdaacc..14a45a8788f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1000,11 +1000,11 @@ impl<'a> Resolver<'a> { let name_ident = HirIdent::non_trait_method(id, location); let attributes = func.attributes().clone(); - let has_inline_attribute = attributes.is_inline(); + let has_no_predicates_attribute = attributes.is_no_predicates(); let should_fold = attributes.is_foldable(); if !self.inline_attribute_allowed(func) { - if has_inline_attribute { - self.push_err(ResolverError::InlineAttributeOnUnconstrained { + if has_no_predicates_attribute { + self.push_err(ResolverError::NoPredicatesAttributeOnUnconstrained { ident: func.name_ident().clone(), }); } else if should_fold { @@ -1013,10 +1013,10 @@ impl<'a> Resolver<'a> { }); } } - // Both the #[fold] and #[inline(tag)] alter a function's inline type and code generation in similar ways. + // Both the #[fold] and #[no_predicates] alter a function's inline type and code generation in similar ways. // In certain cases such as type checking (for which the following flag will be used) both attributes // indicate we should code generate in the same way. Thus, we unify the attributes into one flag here. - let has_inline_or_fold_attribute = has_inline_attribute || should_fold; + let has_inline_attribute = has_no_predicates_attribute || should_fold; let mut generics = vecmap(&self.generics, |(_, typevar, _)| typevar.clone()); let mut parameters = vec![]; @@ -1111,7 +1111,7 @@ impl<'a> Resolver<'a> { has_body: !func.def.body.is_empty(), trait_constraints: self.resolve_trait_constraints(&func.def.where_clause), is_entry_point: self.is_entry_point_function(func), - has_inline_or_fold_attribute, + has_inline_attribute, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 6235fe3848d..ef5692abdd3 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -173,7 +173,7 @@ fn check_if_type_is_valid_for_program_input( ) { let meta = type_checker.interner.function_meta(&func_id); if (meta.is_entry_point && !param.1.is_valid_for_program_input()) - || (meta.has_inline_or_fold_attribute && !param.1.is_valid_non_inlined_function_input()) + || (meta.has_inline_attribute && !param.1.is_valid_non_inlined_function_input()) { let span = param.0.span(); errors.push(TypeCheckError::InvalidTypeForEntryPoint { span }); @@ -546,7 +546,7 @@ pub mod test { trait_constraints: Vec::new(), direct_generics: Vec::new(), is_entry_point: true, - has_inline_or_fold_attribute: false, + has_inline_attribute: false, }; interner.push_fn_meta(func_meta, func_id); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 57d3038a135..db54a40c8e7 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -126,8 +126,9 @@ pub struct FuncMeta { pub is_entry_point: bool, /// True if this function is marked with an attribute - /// that indicates it should not be inlined, such as `fold` or `inline(never)` - pub has_inline_or_fold_attribute: bool, + /// that indicates it should be inlined differently than the default (inline everything). + /// For example, such as `fold` (never inlined) or `no_predicates` (inlined after flattening) + pub has_inline_attribute: bool, } impl FuncMeta { diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 82e17ac3912..ebbc7fc9813 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -582,8 +582,8 @@ impl Attributes { self.function.as_ref().map_or(false, |func_attribute| func_attribute.is_foldable()) } - pub fn is_inline(&self) -> bool { - self.function.as_ref().map_or(false, |func_attribute| func_attribute.is_inline()) + pub fn is_no_predicates(&self) -> bool { + self.function.as_ref().map_or(false, |func_attribute| func_attribute.is_no_predicates()) } } @@ -645,10 +645,7 @@ impl Attribute { ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), ["fold"] => Attribute::Function(FunctionAttribute::Fold), - ["inline", tag] => { - validate(tag)?; - Attribute::Function(FunctionAttribute::Inline(tag.to_string())) - } + ["no_predicates"] => Attribute::Function(FunctionAttribute::NoPredicates), ["test", name] => { validate(name)?; let malformed_scope = @@ -701,7 +698,7 @@ pub enum FunctionAttribute { Test(TestScope), Recursive, Fold, - Inline(String), + NoPredicates, } impl FunctionAttribute { @@ -738,8 +735,8 @@ impl FunctionAttribute { /// Check whether we have an `inline` attribute /// Although we also do not want to inline foldable functions, /// we keep the two attributes distinct for clarity. - pub fn is_inline(&self) -> bool { - matches!(self, FunctionAttribute::Inline(_)) + pub fn is_no_predicates(&self) -> bool { + matches!(self, FunctionAttribute::NoPredicates) } } @@ -752,7 +749,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Recursive => write!(f, "#[recursive]"), FunctionAttribute::Fold => write!(f, "#[fold]"), - FunctionAttribute::Inline(ref k) => write!(f, "#[inline({k})]"), + FunctionAttribute::NoPredicates => write!(f, "#[no_predicates]"), } } } @@ -798,7 +795,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Test { .. } => "", FunctionAttribute::Recursive => "", FunctionAttribute::Fold => "", - FunctionAttribute::Inline(string) => string, + FunctionAttribute::NoPredicates => "", } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index dc43f53e038..721c7d5f0b1 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -213,10 +213,13 @@ pub enum InlineType { Inline, /// Functions marked as foldable will not be inlined and compiled separately into ACIR Fold, - /// Similar to `Fold`, these functions will not be inlined and compile separately into ACIR. - /// They are different from `Fold` though as they are expected to be inlined into the program + /// Functions marked to have no predicates will not be inlined in the default inlining pass + /// and will be separately inlined after the flattening pass. + /// They are different from `Fold` as they are expected to be inlined into the program /// entry point before being used in the backend. - Never, + /// This attribute is unsafe and can cause a function whose logic relies on predicates from + /// the flattening pass to fail. + NoPredicates, } impl From<&Attributes> for InlineType { @@ -224,13 +227,7 @@ impl From<&Attributes> for InlineType { attributes.function.as_ref().map_or(InlineType::default(), |func_attribute| { match func_attribute { FunctionAttribute::Fold => InlineType::Fold, - FunctionAttribute::Inline(tag) => { - if tag == "never" { - InlineType::Never - } else { - InlineType::default() - } - } + FunctionAttribute::NoPredicates => InlineType::NoPredicates, _ => InlineType::default(), } }) @@ -242,7 +239,7 @@ impl InlineType { match self { InlineType::Inline => false, InlineType::Fold => true, - InlineType::Never => true, + InlineType::NoPredicates => false, } } } @@ -252,7 +249,7 @@ impl std::fmt::Display for InlineType { match self { InlineType::Inline => write!(f, "inline"), InlineType::Fold => write!(f, "fold"), - InlineType::Never => write!(f, "inline(never)"), + InlineType::NoPredicates => write!(f, "no_predicates"), } } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index b2cc7eee9f8..5f99e9e347a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1378,9 +1378,10 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(get_program_errors(src).len(), 1); } + #[test] fn deny_inline_attribute_on_unconstrained() { let src = r#" - #[inline(never)] + #[no_predicates] unconstrained fn foo(x: Field, y: Field) { assert(x != y); } @@ -1389,7 +1390,9 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::InlineAttributeOnUnconstrained { .. }) + CompilationError::ResolverError( + ResolverError::NoPredicatesAttributeOnUnconstrained { .. } + ) )); } diff --git a/test_programs/execution_success/inline_never_basic/Nargo.toml b/test_programs/execution_success/no_predicates_basic/Nargo.toml similarity index 74% rename from test_programs/execution_success/inline_never_basic/Nargo.toml rename to test_programs/execution_success/no_predicates_basic/Nargo.toml index 16691770d76..bcefd550fb0 100644 --- a/test_programs/execution_success/inline_never_basic/Nargo.toml +++ b/test_programs/execution_success/no_predicates_basic/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "inline_never_basic" +name = "no_predicates_basic" type = "bin" authors = [""] compiler_version = ">=0.27.0" diff --git a/test_programs/execution_success/inline_never_basic/Prover.toml b/test_programs/execution_success/no_predicates_basic/Prover.toml similarity index 100% rename from test_programs/execution_success/inline_never_basic/Prover.toml rename to test_programs/execution_success/no_predicates_basic/Prover.toml diff --git a/test_programs/execution_success/inline_never_basic/src/main.nr b/test_programs/execution_success/no_predicates_basic/src/main.nr similarity index 87% rename from test_programs/execution_success/inline_never_basic/src/main.nr rename to test_programs/execution_success/no_predicates_basic/src/main.nr index 1922aaedb6c..d6037c2ab26 100644 --- a/test_programs/execution_success/inline_never_basic/src/main.nr +++ b/test_programs/execution_success/no_predicates_basic/src/main.nr @@ -2,7 +2,7 @@ fn main(x: Field, y: pub Field) { basic_check(x, y); } -#[inline(never)] +#[no_predicates] fn basic_check(x: Field, y: Field) { assert(x != y); } diff --git a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Nargo.toml b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Nargo.toml new file mode 100644 index 00000000000..1ce13c24287 --- /dev/null +++ b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "no_predicates_numeric_generic_poseidon" +type = "bin" +authors = [""] +compiler_version = ">=0.28.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Prover.toml b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Prover.toml new file mode 100644 index 00000000000..00e821cf89d --- /dev/null +++ b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/Prover.toml @@ -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]] diff --git a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr new file mode 100644 index 00000000000..dcb5e6bc5ca --- /dev/null +++ b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr @@ -0,0 +1,33 @@ +use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; + +global NUM_HASHES = 2; +global HASH_LENGTH = 10; + +#[no_predicates] +pub fn poseidon_hash(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 `#[no_predicates]` 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 +} diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 5e6ce18be54..f214be5e613 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -17,5 +17,6 @@ fold_basic_nested_call fold_call_witness_condition fold_after_inlined_calls fold_numeric_generic_poseidon -inline_never_basic +no_predicates_basic +no_predicates_numeric_generic_poseidon regression_4709