diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 457fe41de93..e123623cae9 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -82,6 +82,9 @@ impl Intrinsic { match self { Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint => true, + // These apply a constraint that the input must fit into a specified number of limbs. + Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true, + Intrinsic::Sort | Intrinsic::ArrayLen | Intrinsic::SlicePushBack @@ -91,8 +94,6 @@ impl Intrinsic { | Intrinsic::SliceInsert | Intrinsic::SliceRemove | Intrinsic::StrAsBytes - | Intrinsic::ToBits(_) - | Intrinsic::ToRadix(_) | Intrinsic::FromField | Intrinsic::AsField => false, diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 6bdf2ab1c0a..1059994b9be 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -144,9 +144,9 @@ use crate::ssa::{ dfg::{CallStack, InsertInstructionResult}, function::Function, function_inserter::FunctionInserter, - instruction::{BinaryOp, Instruction, InstructionId, TerminatorInstruction}, + instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, types::Type, - value::ValueId, + value::{Value, ValueId}, }, ssa_gen::Ssa, }; @@ -683,6 +683,28 @@ impl<'f> Context<'f> { ); Instruction::RangeCheck { value, max_bit_size, assert_message } } + Instruction::Call { func, mut arguments } => match self.inserter.function.dfg[func] + { + Value::Intrinsic(Intrinsic::ToBits(_) | Intrinsic::ToRadix(_)) => { + let field = arguments[0]; + let argument_type = self.inserter.function.dfg.type_of_value(field); + + let casted_condition = self.insert_instruction( + Instruction::Cast(condition, argument_type), + call_stack.clone(), + ); + let field = self.insert_instruction( + Instruction::binary(BinaryOp::Mul, field, casted_condition), + call_stack.clone(), + ); + + arguments[0] = field; + + Instruction::Call { func, arguments } + } + + _ => Instruction::Call { func, arguments }, + }, other => other, } } else { diff --git a/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/test_programs/compile_success_empty/intrinsic_die/src/main.nr index 88f7a3634c1..8cac707dfea 100644 --- a/test_programs/compile_success_empty/intrinsic_die/src/main.nr +++ b/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -1,8 +1,6 @@ use dep::std; // This test checks that we perform dead-instruction-elimination on intrinsic functions. fn main(x: Field) { - let bytes = x.to_be_bytes(32); - let hash = std::hash::pedersen_commitment([x]); let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); } diff --git a/test_programs/execution_success/to_le_bytes/Prover.toml b/test_programs/execution_success/to_le_bytes/Prover.toml index 07fe857ac7c..bf58776d557 100644 --- a/test_programs/execution_success/to_le_bytes/Prover.toml +++ b/test_programs/execution_success/to_le_bytes/Prover.toml @@ -1 +1,2 @@ x = "2040124" +cond = false \ No newline at end of file diff --git a/test_programs/execution_success/to_le_bytes/src/main.nr b/test_programs/execution_success/to_le_bytes/src/main.nr index 05eefc0f143..a0b48efe528 100644 --- a/test_programs/execution_success/to_le_bytes/src/main.nr +++ b/test_programs/execution_success/to_le_bytes/src/main.nr @@ -1,4 +1,4 @@ -fn main(x: Field) -> pub [u8; 31] { +fn main(x: Field, cond: bool) -> pub [u8; 31] { // The result of this byte array will be little-endian let byte_array = x.to_le_bytes(31); assert(byte_array.len() == 31); @@ -7,5 +7,12 @@ fn main(x: Field) -> pub [u8; 31] { for i in 0..31 { bytes[i] = byte_array[i]; } + + if cond { + // We've set x = "2040124" so we shouldn't be able to represent this as a single byte. + let bad_byte_array = x.to_le_bytes(1); + assert_eq(bad_byte_array.len(), 1); + } + bytes }