Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion EXTERNAL_NOIR_LIBRARIES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ libraries:
critical: false
sha256:
repo: noir-lang/sha256
timeout: 3
timeout: 10
critical: true
sha512:
repo: noir-lang/sha512
Expand Down
230 changes: 227 additions & 3 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,9 @@ impl<'function> PerFunctionContext<'function> {
// can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`,
// resulting in predicates not being applied properly.
//
// Note that this doesn't cover the case in which there exists an `Instruction::EnabledSideEffects`
// Note that this doesn't cover the case in which there exists an `Instruction::EnableSideEffectsIf`
// within the function being inlined whilst the source function has not encountered one yet.
// In practice this isn't an issue as the last `Instruction::EnabledSideEffects` in the
// In practice this isn't an issue as the last `Instruction::EnableSideEffectsIf` in the
// function being inlined will be to turn off predicates rather than to create one.
if let Some(condition) = side_effects_enabled {
self.context.builder.insert_enable_side_effects_if(condition);
Expand Down Expand Up @@ -772,7 +772,7 @@ impl<'function> PerFunctionContext<'function> {
mod test {
use crate::{
assert_ssa_snapshot,
ssa::{Ssa, opt::assert_normalized_ssa_equals},
ssa::{Ssa, ir::instruction::TerminatorInstruction, opt::assert_normalized_ssa_equals},
};

#[test]
Expand Down Expand Up @@ -1051,6 +1051,63 @@ mod test {
");
}

#[test]
fn conditional_inlining_const_from_param_and_direct_constant() {
let src = "
brillig(inline) fn foo f0 {
b0():
v1 = call f1() -> Field
v2 = call f2(u1 1) -> Field
v3 = call f2(u1 0) -> Field
return v1, v2, v3
}
brillig(inline) fn bar f1 {
b0():
jmpif u1 1 then: b1, else: b2
b1():
jmp b3(Field 1)
b2():
jmp b3(Field 2)
b3(v3: Field):
return v3
}
brillig(inline) fn baz f2 {
b0(v0: u1):
jmpif v0 then: b1, else: b2
b1():
jmp b3(Field 1)
b2():
jmp b3(Field 2)
b3(v3: Field):
return v3
}
";
let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.inline_functions(i64::MAX).unwrap();

// We expect a block from all calls to f1 and f2 to be pruned and that the constant argument to the f2 call
// is propagated to the jmpif conditional in b0.
// Field 1 to be returned from the first call to f2 and Field 2 should be returned from the second call to f2.
assert_ssa_snapshot!(ssa, @r"
brillig(inline) fn foo f0 {
b0():
jmp b1()
b1():
jmp b2(Field 1)
b2(v0: Field):
jmp b3()
b3():
jmp b4(Field 1)
b4(v1: Field):
jmp b5()
b5():
jmp b6(Field 2)
b6(v2: Field):
return v0, v1, v2
}
");
}

#[test]
fn static_assertions_to_always_be_inlined() {
let src = "
Expand Down Expand Up @@ -1150,4 +1207,171 @@ mod test {
let ssa = ssa.inline_functions(i64::MIN).unwrap();
assert_normalized_ssa_equals(ssa, no_inline_always_src);
}

#[test]
fn acir_global_arrays_are_inlined_with_new_value_ids() {
let src = "
g0 = Field 1
g1 = Field 2
g2 = make_array [Field 1, Field 2] : [Field; 2]

acir(inline) fn main f0 {
b0():
v0 = call f1() -> [Field; 2]
return v0
}
acir(inline) fn create_array f1 {
b0():
return g2
}
";

let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.inline_functions(i64::MAX).unwrap();

assert_ssa_snapshot!(ssa, @r"
g0 = Field 1
g1 = Field 2
g2 = make_array [Field 1, Field 2] : [Field; 2]

acir(inline) fn main f0 {
b0():
v3 = make_array [Field 1, Field 2] : [Field; 2]
return v3
}
");
}

#[test]
fn brillig_global_arrays_keep_same_value_ids() {
let src = "
g0 = Field 1
g1 = Field 2
g2 = make_array [Field 1, Field 2] : [Field; 2]

brillig(inline) fn main f0 {
b0():
v0 = call f1() -> [Field; 2]
// v1 = array_get g2, index u32 1 -> Field
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot to point out this comment too, sorry.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I missed that. We can just remove in some other testing PR

return v0
}
brillig(inline) fn create_array f1 {
b0():
return g2
}
";

let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.inline_functions(i64::MAX).unwrap();

assert_ssa_snapshot!(ssa, @r"
g0 = Field 1
g1 = Field 2
g2 = make_array [Field 1, Field 2] : [Field; 2]

brillig(inline) fn main f0 {
b0():
return g2
}
");
}

#[test]
fn acir_global_constants_are_inlined_with_new_value_ids() {
let src = "
g0 = Field 1

acir(inline) fn main f0 {
b0():
v0 = call f1() -> Field
return v0
}
acir(inline) fn get_constant f1 {
b0():
return g0
}
";

let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.inline_functions(i64::MAX).unwrap();

// The string output of global constants resolve to their inner values, so we need to check whether they are globals explicitly.
let main = ssa.main();
let entry_block = main.entry_block();
let terminator = main.dfg[entry_block].unwrap_terminator();
let TerminatorInstruction::Return { return_values, .. } = terminator else {
panic!("Expected return");
};
assert_eq!(return_values.len(), 1);
// TODO(https://github.com/noir-lang/noir/issues/9408)
// assert!(!main.dfg.is_global(return_values[0]));

assert_ssa_snapshot!(ssa, @r"
g0 = Field 1

acir(inline) fn main f0 {
b0():
return Field 1
}
");
}

#[test]
fn brillig_global_constants_keep_same_value_ids() {
let src = "
g0 = Field 1

brillig(inline) fn main f0 {
b0():
v0 = call f1() -> Field
return v0
}
brillig(inline) fn get_constant f1 {
b0():
return g0
}
";

let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.inline_functions(i64::MAX).unwrap();

// The string output of global constants resolve to their inner values, so we need to check whether they are globals explicitly.
let main = ssa.main();
let entry_block = main.entry_block();
let terminator = main.dfg[entry_block].unwrap_terminator();
let TerminatorInstruction::Return { return_values, .. } = terminator else {
panic!("Expected return");
};
assert_eq!(return_values.len(), 1);
assert!(main.dfg.is_global(return_values[0]));

assert_ssa_snapshot!(ssa, @r"
g0 = Field 1

brillig(inline) fn main f0 {
b0():
return Field 1
}
");
}

#[test]
#[should_panic(
expected = "Unreachable terminator instruction should not exist during inlining"
)]
fn inlining_unreachable_block() {
let src = "
acir(inline) fn foo f0 {
b0():
v1 = call f1() -> Field
return v1
}
acir(inline) fn bar f1 {
b0():
unreachable
}
";
let ssa = Ssa::from_str(src).unwrap();
let _ = ssa.inline_functions(i64::MAX).unwrap();
}
}
Loading