diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index ac5a64bb2fd..feeb34bc036 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -231,6 +231,28 @@ impl Function { } }) } + + /// Asserts that the [`Function`] is well formed. + /// + /// Panics on malformed functions. + pub(crate) fn assert_valid(&self) { + let reachable_blocks = self.reachable_blocks(); + + // We assume that all functions have a single block which terminates with a `return` instruction. + let return_blocks: BTreeSet<_> = reachable_blocks + .iter() + .filter(|block| { + // All blocks must have a terminator instruction of some sort. + let terminator = self.dfg[**block].terminator().unwrap_or_else(|| { + panic!("Function {} has no terminator in block {block}", self.id()) + }); + matches!(terminator, TerminatorInstruction::Return { .. }) + }) + .collect(); + if return_blocks.len() > 1 { + panic!("Function {} has multiple return blocks {return_blocks:?}", self.id()) + } + } } impl Clone for Function { diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index a5e1147d288..1f8060f0c3b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -816,12 +816,14 @@ mod test { v2 = lt v1, u32 1 jmpif v2 then: b1, else: b2 b1(): - return u32 1 + jmp b3(u32 1) b2(): v4 = sub v1, u32 1 v5 = call f1(v4) -> u32 v6 = mul v1, v5 - return v6 + jmp b3(v6) + b3(v7: u32): + return v7 } "; let ssa = Ssa::from_str(src).unwrap(); @@ -842,7 +844,23 @@ mod test { b5(): jmp b6() b6(): - return u32 120 + jmp b7(u32 1) + b7(v0: u32): + jmp b8(v0) + b8(v1: u32): + v8 = mul u32 2, v1 + jmp b9(v8) + b9(v2: u32): + v10 = mul u32 3, v2 + jmp b10(v10) + b10(v3: u32): + v12 = mul u32 4, v3 + jmp b11(v12) + b11(v4: u32): + v14 = mul u32 5, v4 + jmp b12(v14) + b12(v5: u32): + return v5 } "); } diff --git a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 8c6fff7c561..3dbb219d6fc 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -349,7 +349,7 @@ mod test { b1(): return Field 1 b2(): - return Field 2 + jmp b1() } "; let ssa = Ssa::from_str(src).unwrap(); diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index 995f5e41386..f30e975a79f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -503,6 +503,10 @@ impl Translator { // before each print. ssa.normalize_ids(); + for function in ssa.functions.values() { + function.assert_valid(); + } + ssa } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 8f23d0b1d47..13a6d8ff1e6 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -170,7 +170,7 @@ fn test_jmpif() { b0(v0: Field): jmpif v0 then: b1, else: b2 b1(): - return + jmp b2() b2(): return } @@ -182,7 +182,7 @@ fn test_jmpif() { b0(v0: Field): jmpif v0 then: b2, else: b1 b1(): - return + jmp b2() b2(): return } @@ -197,10 +197,12 @@ fn test_multiple_jmpif() { b0(v0: Field, v1: Field): jmpif v0 then: b1, else: b2 b1(): - return + jmp b4() b2(): jmpif v1 then: b3, else: b1 b3(): + jmp b4() + b4(): return } ";