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
55 changes: 0 additions & 55 deletions compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,59 +110,4 @@ mod test {
}
");
}

/// TODO(https://github.com/noir-lang/noir/issues/9416): This test should be prevented during SSA validation
/// Once type checking of intrinsic function calls is supported this test will have to be run against non-validated SSA.
#[test]
#[should_panic(expected = "AsSlice called with non-array [Field]")]
fn as_slice_length_on_slice_type() {
let src = "
acir(inline) fn main f0 {
b0():
v3 = make_array [Field 1, Field 2, Field 3] : [Field]
v4 = call f1(v3) -> u32
return v4
}

acir(inline) fn foo f1 {
b0(v0: [Field]):
v2, v3 = call as_slice(v0) -> (u32, [Field])
return v2
}
";
let ssa = Ssa::from_str(src).unwrap();
let _ = ssa.as_slice_optimization();
}

/// TODO(https://github.com/noir-lang/noir/issues/9416): This test should be prevented during SSA validation
/// Once type checking of intrinsic function calls is supported this test will have to be run against non-validated SSA.
#[test]
#[should_panic(expected = "AsSlice called with non-array Field")]
fn as_slice_length_on_numeric_type() {
let src = "
acir(inline) fn main f0 {
b0(v0: Field):
v2, v3 = call as_slice(v0) -> (u32, [Field])
return v2
}
";
let ssa = Ssa::from_str(src).unwrap();
let _ = ssa.as_slice_optimization();
}

/// TODO(https://github.com/noir-lang/noir/issues/9416): This test should be prevented during SSA validation
/// Once type checking of intrinsic function calls is supported this test will have to be run against non-validated SSA.
#[test]
#[should_panic(expected = "AsSlice should always have one argument")]
fn as_slice_wrong_number_of_arguments() {
let src = "
acir(inline) fn main f0 {
b0():
v1, v2 = call as_slice() -> (u32, [Field])
return v1
}
";
let ssa = Ssa::from_str(src).unwrap();
let _ = ssa.as_slice_optimization();
}
}
4 changes: 2 additions & 2 deletions compiler/noirc_evaluator/src/ssa/opt/constant_folding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ struct Context {
/// For example, this allows simplifying the instructions below to determine that `v2 == Field 3` without
/// laying down constraints for the addition:
///
/// ```
/// ```ssa
/// constrain v1 == Field 0
/// v2 = add v1, Field 2
/// ```
Expand Down Expand Up @@ -497,7 +497,7 @@ enum CanBeDeduplicated {
/// An example is `EnableSideEffects` where a "duplicate" of this instruction has an important effect on later instructions
/// which is not implied by the existence of the original `EnableSideEffects` instruction. For example:
///
/// ```
/// ```ssa
/// enable_side_effects u1 1
/// enable_side_effects u1 0
/// enable_side_effects u1 1 <-- deduplicating this instruction results in side effects being disabled rather than enabled.
Expand Down
62 changes: 59 additions & 3 deletions compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2151,9 +2151,6 @@ mod test {
#[test_case(1, TestCall::Function(Some(Purity::Impure)), false; "impure function")]
#[test_case(1, TestCall::Function(None), false; "purity unknown")]
#[test_case(1, TestCall::ForeignFunction, false; "foreign functions always impure")]
#[test_case(0, TestCall::Intrinsic(Intrinsic::AsWitness), false; "empty loop; predicate pure intrinsic")]
#[test_case(1, TestCall::Intrinsic(Intrinsic::AsWitness), true; "non-empty loop; predicate pure intrinsic")]
#[test_case(1, TestCall::Intrinsic(Intrinsic::ArrayRefCount), false; "non-empty loop, impure intrinsic")]
#[test_case(0, TestCall::Intrinsic(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::Keccakf1600)), true; "empty loop, pure intrinsic")]
#[test_case(1, TestCall::Intrinsic(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::Keccakf1600)), true; "non-empty loop, pure intrinsic")]
fn hoist_from_loop_call_with_purity(upper: u32, test_call: TestCall, should_hoist: bool) {
Expand Down Expand Up @@ -2202,6 +2199,65 @@ mod test {
}
}

#[test_case(0, false; "empty loop; predicate pure intrinsic")]
#[test_case(1, true; "non-empty loop; predicate pure intrinsic")]
fn hoist_as_witness_from_loop_call_with_purity(upper: u32, should_hoist: bool) {
let src = format!(
r#"
acir(inline) fn main f0 {{
b0(v0: Field):
jmp b1(u32 0)
b1(v1: u32):
v2 = lt v1, u32 {upper}
jmpif v2 then: b2, else: b3
b2():
call as_witness(v0)
v4 = unchecked_add v1, u32 1
jmp b1(v4)
b3():
return
}}
"#,
);

let ssa = Ssa::from_str(&src).unwrap();
let ssa = ssa.loop_invariant_code_motion();

// The pre-header of the loop b1 is b0
let pre_header = &ssa.main().dfg[BasicBlockId::new(0)];
if !should_hoist {
assert!(pre_header.instructions().is_empty(), "should not hoist");
} else {
assert_eq!(pre_header.instructions().len(), 1, "should hoist");
}
}

#[test]
fn does_not_hoist_array_refcount_from_loop_call_with_purity() {
let src = "
acir(inline) fn main f0 {
b0(v0: [Field; 3]):
jmp b1(u32 0)
b1(v1: u32):
v2 = lt v1, u32 1
jmpif v2 then: b2, else: b3
b2():
v3 = call array_refcount(v0) -> u32
v4 = unchecked_add v1, u32 1
jmp b1(v4)
b3():
return
}
";

let ssa = Ssa::from_str(src).unwrap();
let ssa = ssa.loop_invariant_code_motion();

// The pre-header of the loop b1 is b0
let pre_header = &ssa.main().dfg[BasicBlockId::new(0)];
assert!(pre_header.instructions().is_empty(), "should not hoist");
}

/// Test cases where `i < const` or `const < i` should or shouldn't be simplified.
#[test_case(10, 20, true, 25, Some(true))]
#[test_case(10, 20, true, 20, Some(true))]
Expand Down
Loading
Loading