diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index fd78ec80489..a1c003b1c42 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -110,6 +110,8 @@ pub(crate) fn gen_brillig_for( #[cfg(test)] mod entry_point { + use acvm::acir::brillig::Opcode as BrilligOpcode; + use crate::{ assert_artifact_snapshot, brillig::{ @@ -287,4 +289,36 @@ mod entry_point { 60: return "); } + + #[test] + fn unreachable_terminator_keeps_jump_targets_in_range() { + let src = " + brillig(inline) fn main f0 { + b0(v0: u32): + constrain v0 == u32 7 + unreachable + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let options = BrilligOptions::default(); + let brillig = ssa.to_brillig(&options); + + let args = vec![BrilligParameter::SingleAddr(32)]; + let entry = gen_brillig_for(ssa.main(), &args, &brillig, &options).unwrap(); + + let len = entry.byte_code.len(); + for (index, opcode) in entry.byte_code.iter().enumerate() { + let target = match opcode { + BrilligOpcode::Jump { location } + | BrilligOpcode::JumpIf { location, .. } + | BrilligOpcode::Call { location } => *location, + _ => continue, + }; + assert!( + target < len, + "opcode {index} ({opcode:?}) targets {target}, \ + which is out of range for a program of {len} opcodes" + ); + } + } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index b5330b8afd6..365040dda37 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -605,7 +605,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { self.brillig_context.codegen_return(&return_registers); } TerminatorInstruction::Unreachable { .. } => { - // If we assume this is unreachable code then there's nothing to do here + self.brillig_context.codegen_trap(); } } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/tests/memory.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/tests/memory.rs index b2e28f76d47..ac8bf5c423e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/tests/memory.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/tests/memory.rs @@ -186,8 +186,8 @@ fn brillig_global_array_not_coalesced_with_block_param() { let brillig = ssa_to_brillig_artifacts(src); let main = &brillig.ssa_function_to_brillig[&Id::test_new(0)]; // Key opcodes: - // 13: sp[2] = @68 — global g0 lives in global register @68, copied into sp[2] (param v3's slot) - // 15: return — returns sp[1]; global and param use separate allocations (not coalesced) + // 19: sp[2] = @68 — global g0 lives in global register @68, copied into sp[2] (param v3's slot) + // 21: return — returns sp[1]; global and param use separate allocations (not coalesced) assert_artifact_snapshot!(main, @r" fn main 0: sp[3] = @1 @@ -201,14 +201,16 @@ fn brillig_global_array_not_coalesced_with_block_param() { 8: @0 = sp[0] 9: sp[4] = sp[8] 10: jump if sp[4] to 0 // -> 12: f0/b1 - 11: jump to 0 // -> 17: f0/b2 + 11: jump to 0 // -> 19: f0/b2 12: sp[2] = const bool 1 // f0/b1 13: sp[3] = bool eq @69, sp[2] 14: jump if sp[3] to 0 // -> 17: f0/b1/1 15: sp[4] = const u32 0 16: trap @[@1; sp[4]] - 17: sp[2] = @68 // f0/b1/1, f0/b2 - 18: jump to 0 // -> 19: f0/b3 - 19: return // f0/b3 + 17: sp[2] = const u32 0 // f0/b1/1 + 18: trap @[@1; sp[2]] + 19: sp[2] = @68 // f0/b2 + 20: jump to 0 // -> 21: f0/b3 + 21: return // f0/b3 "); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index 65931d281a7..e4dfcb36960 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -186,6 +186,14 @@ impl BrilligContext< self.enter_section(end_section); } + /// Emits an unconditional trap with empty error data (no error information). + pub(crate) fn codegen_trap(&mut self) { + let error_data = self.make_usize_constant_instruction(0_usize.into()).map(|size| { + HeapVector { pointer: ReservedRegisters::free_memory_pointer(), size: size.address } + }); + self.trap_instruction(*error_data); + } + /// Jump to a trap condition if `condition` is false. /// The trap will include the given message as error data. /// @@ -204,12 +212,7 @@ impl BrilligContext< // Special case: No error selector means completely empty error data let Some(error_selector) = error_selector else { - let error_data = - ctx.make_usize_constant_instruction(0_usize.into()).map(|size| HeapVector { - pointer: ReservedRegisters::free_memory_pointer(), - size: size.address, - }); - ctx.trap_instruction(*error_data); + ctx.codegen_trap(); return; };