From eabede72c4f37bb9b49c148af4ad547d3b63db6d Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Mon, 20 Oct 2025 16:27:12 +0100 Subject: [PATCH 1/3] Add integration test --- .../execution_failure/regression_10238/Nargo.toml | 6 ++++++ .../execution_failure/regression_10238/Prover.toml | 1 + .../execution_failure/regression_10238/src/main.nr | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 test_programs/execution_failure/regression_10238/Nargo.toml create mode 100644 test_programs/execution_failure/regression_10238/Prover.toml create mode 100644 test_programs/execution_failure/regression_10238/src/main.nr diff --git a/test_programs/execution_failure/regression_10238/Nargo.toml b/test_programs/execution_failure/regression_10238/Nargo.toml new file mode 100644 index 00000000000..6e24177dfbc --- /dev/null +++ b/test_programs/execution_failure/regression_10238/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_10238" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_failure/regression_10238/Prover.toml b/test_programs/execution_failure/regression_10238/Prover.toml new file mode 100644 index 00000000000..2ac3230b9d1 --- /dev/null +++ b/test_programs/execution_failure/regression_10238/Prover.toml @@ -0,0 +1 @@ +b = "-0x0000000000000000000000000000000000000000000000008000000000000000" diff --git a/test_programs/execution_failure/regression_10238/src/main.nr b/test_programs/execution_failure/regression_10238/src/main.nr new file mode 100644 index 00000000000..f934bbcaa24 --- /dev/null +++ b/test_programs/execution_failure/regression_10238/src/main.nr @@ -0,0 +1,7 @@ +fn main(b: i64, mut c: bool) -> pub Field { + if ((6863985126385003285_i64 - b) != 0) { + 10 + } else { + 20 + } +} From 61c271f5a81913738025cdad5a2235d12a31b400 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Mon, 20 Oct 2025 16:27:28 +0100 Subject: [PATCH 2/3] Add integer modulus if it's a sub --- .../src/ssa/interpreter/mod.rs | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs index 6727fb69d40..de0691cac28 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs @@ -7,6 +7,7 @@ use super::{ function::{Function, FunctionId, RuntimeType}, instruction::{Binary, BinaryOp, ConstrainError, Instruction, TerminatorInstruction}, types::Type, + value::Value as IrValue, value::ValueId, }, }; @@ -686,12 +687,38 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> { return Err(internal(InternalError::TruncateToZeroBits { value_id, max_bit_size })); } + let is_sub = if let IrValue::Instruction { instruction, .. } = self.dfg()[value_id] { + matches!( + self.dfg()[instruction], + Instruction::Binary(Binary { operator: BinaryOp::Sub { .. }, .. }) + ) + } else { + false + }; + + // Based on acir::Context::convert_ssa_truncate: subtractions must first have the integer modulus added to avoid underflow. + fn truncate_unfit( + mut value: FieldElement, + bit_size: u32, + max_bit_size: u32, + is_sub: bool, + ) -> FieldElement { + if is_sub { + let max_bit_size = FieldElement::from(max_bit_size); + let integer_modulus = FieldElement::from(2u128).pow(&max_bit_size); + value = value + integer_modulus; + } + truncate_field(value, bit_size) + } + // Truncate an unsigned value. fn truncate_fitted( cons: F, typ: NumericType, value: Fitted, bit_size: u32, + max_bit_size: u32, + is_sub: bool, ) -> IResult where T: TryFrom, @@ -702,7 +729,7 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> { match value { Fit(value) => Ok(cons(Fit(truncate_unsigned(value, bit_size)?))), Unfit(value) => { - let truncated = truncate_field(value, bit_size); + let truncated = truncate_unfit(value, bit_size, max_bit_size, is_sub); NumericValue::from_constant(truncated, typ) .or_else(|_| Ok(cons(Unfit(truncated)))) } @@ -711,13 +738,13 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> { // Truncate a signed value via unsigned cast and back. macro_rules! truncate_via { - ($cons:expr, $typ:expr, $value:ident, $bit_size:ident, $signed:ty, $unsigned:ty) => { + ($cons:expr, $typ:expr, $value:ident, $bit_size:ident, $max_bit_size:ident, $is_sub:ident, $signed:ty, $unsigned:ty) => { match $value { Fit(value) => { $cons(Fit(truncate_unsigned(value as $unsigned, $bit_size)? as $signed)) } Unfit(value) => { - let truncated = truncate_field(value, bit_size); + let truncated = truncate_unfit(value, bit_size, max_bit_size, is_sub); NumericValue::from_constant(truncated, typ) .unwrap_or_else(|_| $cons(Unfit(truncated))) } @@ -728,15 +755,15 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> { let truncated = match value { Field(value) => Field(truncate_field(value, bit_size)), U1(value) => U1(value), - U8(value) => truncate_fitted(U8, typ, value, bit_size)?, - U16(value) => truncate_fitted(U16, typ, value, bit_size)?, - U32(value) => truncate_fitted(U32, typ, value, bit_size)?, - U64(value) => truncate_fitted(U64, typ, value, bit_size)?, - U128(value) => truncate_fitted(U128, typ, value, bit_size)?, - I8(value) => truncate_via!(I8, typ, value, bit_size, i8, u8), - I16(value) => truncate_via!(I16, typ, value, bit_size, i16, u16), - I32(value) => truncate_via!(I32, typ, value, bit_size, i32, u32), - I64(value) => truncate_via!(I64, typ, value, bit_size, i64, u64), + U8(value) => truncate_fitted(U8, typ, value, bit_size, max_bit_size, is_sub)?, + U16(value) => truncate_fitted(U16, typ, value, bit_size, max_bit_size, is_sub)?, + U32(value) => truncate_fitted(U32, typ, value, bit_size, max_bit_size, is_sub)?, + U64(value) => truncate_fitted(U64, typ, value, bit_size, max_bit_size, is_sub)?, + U128(value) => truncate_fitted(U128, typ, value, bit_size, max_bit_size, is_sub)?, + I8(value) => truncate_via!(I8, typ, value, bit_size, max_bit_size, is_sub, i8, u8), + I16(value) => truncate_via!(I16, typ, value, bit_size, max_bit_size, is_sub, i16, u16), + I32(value) => truncate_via!(I32, typ, value, bit_size, max_bit_size, is_sub, i32, u32), + I64(value) => truncate_via!(I64, typ, value, bit_size, max_bit_size, is_sub, i64, u64), }; self.define(result, Value::Numeric(truncated)) From 4174b7d6e5b94cc842eb84aaddbd289ad513d53e Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Wed, 22 Oct 2025 09:55:39 +0100 Subject: [PATCH 3/3] Clippy --- compiler/noirc_evaluator/src/ssa/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs index de0691cac28..60468f6a403 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs @@ -706,7 +706,7 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> { if is_sub { let max_bit_size = FieldElement::from(max_bit_size); let integer_modulus = FieldElement::from(2u128).pow(&max_bit_size); - value = value + integer_modulus; + value += integer_modulus; } truncate_field(value, bit_size) }