diff --git a/.github/benchmark_projects.yml b/.github/benchmark_projects.yml index 71bbf522b78..50ead64337e 100644 --- a/.github/benchmark_projects.yml +++ b/.github/benchmark_projects.yml @@ -73,7 +73,7 @@ projects: path: noir-projects/noir-protocol-circuits/crates/rollup-block-root num_runs: 1 timeout: 60 - compilation-timeout: 200 + compilation-timeout: 250 execution-timeout: 40 compilation-memory-limit: 10000 execution-memory-limit: 1900 diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index 646ba5e1f86..8b9cc36d3df 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -1044,6 +1044,18 @@ impl<'a> Context<'a> { let lhs = self.convert_numeric_value(binary.lhs, dfg)?; let rhs = self.convert_numeric_value(binary.rhs, dfg)?; let binary_type = self.type_of_binary_operation(binary, dfg); + + if binary_type.is_signed() + && matches!( + binary.operator, + BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false } + ) + { + panic!("Checked signed operations should all be removed before ACIRgen") + } + let binary_type = AcirType::from(binary_type); let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); 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 42296ad604e..4c47494a430 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1761,6 +1761,16 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { let bit_size = left.bit_size; if bit_size == FieldElement::max_num_bits() || is_signed { + if is_signed + && matches!( + binary.operator, + BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false } + ) + { + panic!("Checked signed operations should all be removed before brilliggen") + } return; } diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/errors.rs b/compiler/noirc_evaluator/src/ssa/interpreter/errors.rs index e2b19b8a744..381c647bc7f 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/errors.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/errors.rs @@ -55,7 +55,7 @@ pub enum InterpreterError { )] IncRcRevive { value_id: ValueId, value: String }, #[error("An overflow occurred while evaluating {instruction}")] - Overflow { instruction: String }, + Overflow { operator: BinaryOp, instruction: String }, #[error( "if-else instruction with then condition `{then_condition_id}` and else condition `{else_condition_id}` has both branches as true. This should be impossible except for malformed SSA code" )] diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs index 4218e9e799d..f56e583c51b 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs @@ -1096,7 +1096,7 @@ macro_rules! apply_int_binop_opt { let operator = binary.operator; let overflow = || { - if matches!(binary.operator, BinaryOp::Div | BinaryOp::Mod) { + if matches!(operator, BinaryOp::Div | BinaryOp::Mod) { let lhs_id = binary.lhs; let rhs_id = binary.rhs; let lhs = lhs.to_string(); @@ -1105,7 +1105,7 @@ macro_rules! apply_int_binop_opt { } else { let instruction = format!("`{}` ({operator} {lhs}, {rhs})", display_binary(binary, $dfg)); - InterpreterError::Overflow { instruction } + InterpreterError::Overflow { operator, instruction } } }; @@ -1186,7 +1186,7 @@ impl Interpreter<'_, W> { })); } - // Disable this instruction if it is side-effectful and side effects are disabled. + // Disable this instruction if it is side-effectual and side effects are disabled. if !side_effects_enabled { let zero = NumericValue::zero(lhs.get_type()); return Ok(Value::Numeric(zero)); @@ -1203,32 +1203,20 @@ impl Interpreter<'_, W> { let dfg = self.dfg(); let result = match binary.operator { BinaryOp::Add { unchecked: false } => { - if lhs.get_type().is_unsigned() { - apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedAdd::checked_add) - } else { - apply_int_binop!(lhs, rhs, binary, num_traits::WrappingAdd::wrapping_add) - } + apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedAdd::checked_add) } BinaryOp::Add { unchecked: true } => { apply_int_binop!(lhs, rhs, binary, num_traits::WrappingAdd::wrapping_add) } BinaryOp::Sub { unchecked: false } => { - if lhs.get_type().is_unsigned() { - apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedSub::checked_sub) - } else { - apply_int_binop!(lhs, rhs, binary, num_traits::WrappingSub::wrapping_sub) - } + apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedSub::checked_sub) } BinaryOp::Sub { unchecked: true } => { apply_int_binop!(lhs, rhs, binary, num_traits::WrappingSub::wrapping_sub) } BinaryOp::Mul { unchecked: false } => { // Only unsigned multiplication has side effects - if lhs.get_type().is_unsigned() { - apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedMul::checked_mul) - } else { - apply_int_binop!(lhs, rhs, binary, num_traits::WrappingMul::wrapping_mul) - } + apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedMul::checked_mul) } BinaryOp::Mul { unchecked: true } => { apply_int_binop!(lhs, rhs, binary, num_traits::WrappingMul::wrapping_mul) @@ -1364,7 +1352,8 @@ impl Interpreter<'_, W> { fn interpret_u1_binary_op(&mut self, lhs: bool, rhs: bool, binary: &Binary) -> IResult { let overflow = || { let instruction = format!("`{}` ({lhs} << {rhs})", display_binary(binary, self.dfg())); - InterpreterError::Overflow { instruction } + let operator = binary.operator; + InterpreterError::Overflow { operator, instruction } }; let lhs_id = binary.lhs; diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs b/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs index 4b1578c474b..0bae25b0a5d 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs @@ -66,7 +66,7 @@ fn add_overflow_unsigned() { #[test] fn add_overflow_signed() { - let value = expect_value( + let error = expect_error( " acir(inline) fn main f0 { b0(): @@ -76,7 +76,7 @@ fn add_overflow_signed() { } ", ); - assert_eq!(value, Value::Numeric(NumericValue::I8(-127))); + assert!(matches!(error, InterpreterError::Overflow { .. })); } #[test] @@ -152,7 +152,7 @@ fn sub_underflow_unsigned() { #[test] fn sub_underflow_signed() { - let value = expect_value( + let error = expect_error( " acir(inline) fn main f0 { b0(): @@ -162,10 +162,7 @@ fn sub_underflow_signed() { } ", ); - // Expected wrapping sub: - // i8 can only be -128 to 127 - // -120 - 10 = -130 = 126 - assert!(matches!(value, Value::Numeric(NumericValue::I8(126)))); + assert!(matches!(error, InterpreterError::Overflow { .. })); } #[test] @@ -243,22 +240,16 @@ fn mul_overflow_unsigned() { #[test] fn mul_overflow_signed() { - // We return v0 as we simply want the output from the mul operation in this test. - // However, the valid SSA signed overflow patterns requires that the appropriate - // casts and truncates follow a signed mul. - let value = expect_value( + let error = expect_error( " acir(inline) fn main f0 { b0(): v0 = mul i8 127, i8 2 - v1 = cast v0 as u16 - v2 = truncate v1 to 8 bits, max_bit_size: 16 - v3 = cast v2 as i8 return v0 } ", ); - assert_eq!(value, Value::Numeric(NumericValue::I8(-2))); + assert!(matches!(error, InterpreterError::Overflow { .. })); } #[test] diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 3a6976039a4..a4b6463991a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -155,6 +155,11 @@ impl Type { matches!(self, Type::Numeric(NumericType::Unsigned { .. })) } + /// Returns whether the `Type` represents an signed numeric type. + pub fn is_signed(&self) -> bool { + matches!(self, Type::Numeric(NumericType::Signed { .. })) + } + /// Create a new signed integer type with the given amount of bits. pub fn signed(bit_size: u32) -> Type { Type::Numeric(NumericType::Signed { bit_size }) diff --git a/compiler/noirc_evaluator/src/ssa/mod.rs b/compiler/noirc_evaluator/src/ssa/mod.rs index 2f52d010309..198ca56c8b0 100644 --- a/compiler/noirc_evaluator/src/ssa/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/mod.rs @@ -107,6 +107,7 @@ pub struct ArtifactsAndWarnings(pub Artifacts, pub Vec); /// something we take can advantage of in the [secondary_passes]. pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec { vec![ + SsaPass::new(Ssa::expand_signed_checks, "expand signed checks"), SsaPass::new(Ssa::remove_unreachable_functions, "Removing Unreachable Functions"), SsaPass::new(Ssa::defunctionalize, "Defunctionalization"), SsaPass::new(Ssa::inline_simple_functions, "Inlining simple functions") @@ -219,6 +220,8 @@ pub fn secondary_passes(brillig: &Brillig) -> Vec { /// In the future, we can potentially execute the actual initial version using the SSA interpreter. pub fn minimal_passes() -> Vec> { vec![ + // Signed integer operations need to be expanded in order to have the appropriate overflow checks applied. + SsaPass::new(Ssa::expand_signed_checks, "expand signed checks"), // We need to get rid of function pointer parameters, otherwise they cause panic in Brillig generation. SsaPass::new(Ssa::defunctionalize, "Defunctionalization"), // Even the initial SSA generation can result in optimizations that leave a function diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index d787b1521b3..e2dff51f5dd 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -1454,7 +1454,7 @@ mod test { brillig(inline) fn one f1 { b0(v0: i32, v1: i32): - v2 = add v0, v1 + v2 = unchecked_add v0, v1 v3 = truncate v2 to 32 bits, max_bit_size: 33 return v3 } diff --git a/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs b/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs new file mode 100644 index 00000000000..56166770899 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs @@ -0,0 +1,636 @@ +use acvm::{FieldElement, acir::AcirField}; + +use crate::ssa::{ + ir::{ + function::Function, + instruction::{Binary, BinaryOp, ConstrainError, Instruction}, + types::{NumericType, Type}, + value::ValueId, + }, + ssa_gen::Ssa, +}; + +use super::simple_optimization::SimpleOptimizationContext; + +impl Ssa { + /// Expands signed arithmetic operations to include explicit overflow checks. + /// + /// See [`expand_signed_checks`][self] module for more information. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn expand_signed_checks(mut self) -> Ssa { + for function in self.functions.values_mut() { + function.expand_signed_checks(); + } + self + } +} + +impl Function { + /// The structure of this pass is simple: + /// Go through each block and re-insert all instructions, decomposing any checked signed arithmetic to have explicit + /// overflow checks. + pub(crate) fn expand_signed_checks(&mut self) { + // TODO: consider whether we can implement this more efficiently in brillig. + + self.simple_reachable_blocks_optimization(|context| { + let instruction_id = context.instruction_id; + let instruction = context.instruction(); + + // We only care about binary instructions that are signed arithmetic operations... + let Instruction::Binary(Binary { + lhs, + rhs, + operator: + operator @ (BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false }), + }) = instruction + else { + return; + }; + + // ... and it must be a signed integer operation. + if !context.dfg.type_of_value(*lhs).is_signed() { + return; + } + + let lhs = *lhs; + let rhs = *rhs; + let operator = *operator; + + // We remove the current instruction, as we will need to replace it with multiple new instructions. + context.remove_current_instruction(); + + let old_result = *context.dfg.instruction_results(instruction_id).first().unwrap(); + + let mut expansion_context = Context { context }; + let new_result = match operator { + BinaryOp::Add { .. } => expansion_context.insert_add(lhs, rhs), + BinaryOp::Sub { .. } => expansion_context.insert_sub(lhs, rhs), + BinaryOp::Mul { .. } => expansion_context.insert_mul(lhs, rhs), + _ => unreachable!("ICE: expand_signed_checks called on non-add/sub/mul"), + }; + + context.replace_value(old_result, new_result); + }); + + #[cfg(debug_assertions)] + expand_signed_checks_post_check(self); + } +} + +struct Context<'m, 'dfg, 'mapping> { + context: &'m mut SimpleOptimizationContext<'dfg, 'mapping>, +} + +impl Context<'_, '_, '_> { + fn insert_add(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let bit_size = self.context.dfg.type_of_value(lhs).bit_size(); + let unchecked_result = self.insert_binary(lhs, BinaryOp::Add { unchecked: true }, rhs); + let truncated = self.insert_truncate(unchecked_result, bit_size, bit_size + 1); + let truncated = self.insert_safe_cast(truncated, NumericType::unsigned(bit_size)); + + self.check_signed_overflow( + truncated, + lhs, + rhs, + BinaryOp::Add { unchecked: false }, + bit_size, + ); + self.insert_cast(truncated, self.context.dfg.type_of_value(lhs).unwrap_numeric()) + } + + fn insert_sub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let bit_size = self.context.dfg.type_of_value(lhs).bit_size(); + let unchecked_result = self.insert_binary(lhs, BinaryOp::Sub { unchecked: true }, rhs); + let truncated = self.insert_truncate(unchecked_result, bit_size, bit_size + 1); + let truncated = self.insert_safe_cast(truncated, NumericType::unsigned(bit_size)); + + self.check_signed_overflow( + truncated, + lhs, + rhs, + BinaryOp::Sub { unchecked: false }, + bit_size, + ); + self.insert_cast(truncated, self.context.dfg.type_of_value(lhs).unwrap_numeric()) + } + + fn insert_mul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let bit_size = self.context.dfg.type_of_value(lhs).bit_size(); + let unchecked_result = self.insert_binary(lhs, BinaryOp::Mul { unchecked: true }, rhs); + let unchecked_result_field = + self.insert_cast(unchecked_result, NumericType::unsigned(2 * bit_size)); + let truncated = self.insert_truncate(unchecked_result_field, bit_size, 2 * bit_size); + + self.check_signed_overflow( + truncated, + lhs, + rhs, + BinaryOp::Mul { unchecked: false }, + bit_size, + ); + self.insert_cast(truncated, self.context.dfg.type_of_value(lhs).unwrap_numeric()) + } + + /// Insert constraints ensuring that the operation does not overflow the bit size of the result + /// We assume that: + /// lhs and rhs are signed integers of bit size bit_size + /// result is the result of the operation, casted into an unsigned integer and not reduced + /// + /// overflow check for signed integer is less straightforward than for unsigned integers. + /// We first compute the sign of the operands, and then we use the following rules: + /// addition: positive operands => result must be positive (i.e less than half the bit size) + /// negative operands => result must be negative (i.e not positive) + /// different sign => no overflow + /// multiplication: we check that the product of the operands' absolute values does not overflow the bit size + /// then we check that the result has the proper sign, using the rule of signs + fn check_signed_overflow( + &mut self, + result: ValueId, + lhs: ValueId, + rhs: ValueId, + operator: BinaryOp, + bit_size: u32, + ) { + let is_sub = matches!(operator, BinaryOp::Sub { .. }); + let half_width = self.numeric_constant( + FieldElement::from(2_i128.pow(bit_size - 1)), + NumericType::unsigned(bit_size), + ); + // We compute the sign of the operands. The overflow checks for signed integers depends on these signs + let lhs_as_unsigned = self.insert_safe_cast(lhs, NumericType::unsigned(bit_size)); + let rhs_as_unsigned = self.insert_safe_cast(rhs, NumericType::unsigned(bit_size)); + let lhs_sign = self.insert_binary(lhs_as_unsigned, BinaryOp::Lt, half_width); + let mut rhs_sign = self.insert_binary(rhs_as_unsigned, BinaryOp::Lt, half_width); + let message = if is_sub { + // lhs - rhs = lhs + (-rhs) + rhs_sign = self.insert_not(rhs_sign); + "attempt to subtract with overflow".to_string() + } else { + "attempt to add with overflow".to_string() + }; + // same_sign is true if both operands have the same sign + let same_sign = self.insert_binary(lhs_sign, BinaryOp::Eq, rhs_sign); + match operator { + BinaryOp::Add { .. } | BinaryOp::Sub { .. } => { + //Check the result has the same sign as its inputs + let result_sign = self.insert_binary(result, BinaryOp::Lt, half_width); + let sign_diff = self.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); + // Unchecked multiplication because boolean inputs + let sign_diff_with_predicate = + self.insert_binary(sign_diff, BinaryOp::Mul { unchecked: true }, same_sign); + let overflow_check = Instruction::Constrain( + sign_diff_with_predicate, + same_sign, + Some(message.into()), + ); + self.context.insert_instruction(overflow_check, None); + } + BinaryOp::Mul { .. } => { + // Overflow check for the multiplication: + // First we compute the absolute value of operands, and their product + let lhs_abs = self.absolute_value_helper(lhs, lhs_sign, bit_size); + let rhs_abs = self.absolute_value_helper(rhs, rhs_sign, bit_size); + // Unchecked mul because these are fields + let product_field = + self.insert_binary(lhs_abs, BinaryOp::Mul { unchecked: true }, rhs_abs); + // It must not already overflow the bit_size + self.insert_range_check( + product_field, + bit_size, + Some("attempt to multiply with overflow".to_string()), + ); + let product = self.insert_safe_cast(product_field, NumericType::unsigned(bit_size)); + + // Then we check the signed product fits in a signed integer of bit_size-bits + let not_same = self.insert_not(same_sign); + let not_same_sign_field = + self.insert_safe_cast(not_same, NumericType::unsigned(bit_size)); + // Unchecked add because adding 1 to half_width can't overflow + let positive_maximum_with_offset = self.insert_binary( + half_width, + BinaryOp::Add { unchecked: true }, + not_same_sign_field, + ); + let product_overflow_check = + self.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); + + let one = self.numeric_constant(FieldElement::one(), NumericType::bool()); + self.insert_constrain(product_overflow_check, one, Some(message.into())); + } + _ => unreachable!("operator {} should not overflow", operator), + } + } + + /// helper function which add instructions to the block computing the absolute value of the + /// given signed integer input. When the input is negative, we return its two complement, and itself when it is positive. + fn absolute_value_helper(&mut self, input: ValueId, sign: ValueId, bit_size: u32) -> ValueId { + assert_eq!(self.context.dfg.type_of_value(sign), Type::bool()); + + // We compute the absolute value of lhs + let bit_width = FieldElement::from(2_i128.pow(bit_size)); + let bit_width = self.numeric_constant(bit_width, NumericType::NativeField); + let sign_not = self.insert_not(sign); + + // We use unsafe casts here, this is fine as we're casting to a `field` type. + let as_field = self.insert_safe_cast(input, NumericType::NativeField); + let sign_field = self.insert_safe_cast(sign, NumericType::NativeField); + + // All of these operations are unchecked because they deal with fields + let positive_predicate = + self.insert_binary(sign_field, BinaryOp::Mul { unchecked: true }, as_field); + let two_complement = + self.insert_binary(bit_width, BinaryOp::Sub { unchecked: true }, as_field); + let sign_not_field = self.insert_safe_cast(sign_not, NumericType::NativeField); + let negative_predicate = + self.insert_binary(sign_not_field, BinaryOp::Mul { unchecked: true }, two_complement); + // Unchecked addition because either `positive_predicate` or `negative_predicate` will be 0 + self.insert_binary( + positive_predicate, + BinaryOp::Add { unchecked: true }, + negative_predicate, + ) + } + + /// Inserts a cast instruction at the end of the current block and returns the results + /// of the cast. + /// + /// Compared to `self.insert_cast`, this version will automatically truncate `value` to be a valid `typ`. + pub(super) fn insert_safe_cast(&mut self, mut value: ValueId, typ: NumericType) -> ValueId { + let incoming_type = self.context.dfg.type_of_value(value); + + let result = match (&incoming_type, typ) { + // Casting to field is safe + (_, NumericType::NativeField) => value, + ( + Type::Numeric(NumericType::Signed { bit_size: incoming_type_size }), + NumericType::Signed { bit_size: target_type_size }, + ) => { + match target_type_size.cmp(incoming_type_size) { + std::cmp::Ordering::Less => { + // If target size is smaller, we do a truncation + self.insert_truncate(value, target_type_size, *incoming_type_size) + } + std::cmp::Ordering::Equal => value, + std::cmp::Ordering::Greater => { + // If target size is bigger, we do a sign extension: + // When the value is negative, it is represented in 2-complement form; `2^s-v`, where `s` is the incoming bit size and `v` is the absolute value + // Sign extension in this case will give `2^t-v`, where `t` is the target bit size + // So we simply convert `2^s-v` into `2^t-v` by adding `2^t-2^s` to the value when the value is negative. + // Casting s-bits signed v0 to t-bits will add the following instructions: + // v1 = cast v0 to 's-bits unsigned' + // v2 = lt v1, 2**(s-1) + // v3 = not(v1) + // v4 = cast v3 to 't-bits unsigned' + // v5 = v3 * (2**t - 2**s) + // v6 = cast v1 to 't-bits unsigned' + // return v6 + v5 + let value_as_unsigned = self + .insert_safe_cast(value, NumericType::unsigned(*incoming_type_size)); + let half_width = self.numeric_constant( + FieldElement::from(2_u128.pow(incoming_type_size - 1)), + NumericType::unsigned(*incoming_type_size), + ); + // value_sign is 1 if the value is positive, 0 otherwise + let value_sign = + self.insert_binary(value_as_unsigned, BinaryOp::Lt, half_width); + let max_for_incoming_type_size = if *incoming_type_size == 128 { + u128::MAX + } else { + 2_u128.pow(*incoming_type_size) - 1 + }; + let max_for_target_type_size = if target_type_size == 128 { + u128::MAX + } else { + 2_u128.pow(target_type_size) - 1 + }; + let patch = self.numeric_constant( + FieldElement::from( + max_for_target_type_size - max_for_incoming_type_size, + ), + NumericType::unsigned(target_type_size), + ); + let mut is_negative_predicate = self.insert_not(value_sign); + is_negative_predicate = self.insert_safe_cast( + is_negative_predicate, + NumericType::unsigned(target_type_size), + ); + // multiplication by a boolean cannot overflow + let patch_with_sign_predicate = self.insert_binary( + patch, + BinaryOp::Mul { unchecked: true }, + is_negative_predicate, + ); + let value_as_unsigned = self.insert_cast( + value_as_unsigned, + NumericType::unsigned(target_type_size), + ); + // Patch the bit sign, which gives a `target_type_size` bit size value, so it does not overflow. + self.insert_binary( + patch_with_sign_predicate, + BinaryOp::Add { unchecked: true }, + value_as_unsigned, + ) + } + } + } + ( + Type::Numeric(NumericType::Unsigned { bit_size: incoming_type_size }), + NumericType::Unsigned { bit_size: target_type_size }, + ) => { + // If target size is smaller, we do a truncation + if target_type_size < *incoming_type_size { + value = self.insert_truncate(value, target_type_size, *incoming_type_size); + } + value + } + // When casting a signed value to u1 we can truncate then cast + ( + Type::Numeric(NumericType::Signed { bit_size: incoming_type_size }), + NumericType::Unsigned { bit_size: 1 }, + ) => self.insert_truncate(value, 1, *incoming_type_size), + // For mixed sign to unsigned or unsigned to sign; + // 1. we cast to the required type using the same signedness + // 2. then we switch the signedness + ( + Type::Numeric(NumericType::Signed { bit_size: incoming_type_size }), + NumericType::Unsigned { bit_size: target_type_size }, + ) => { + if *incoming_type_size != target_type_size { + value = self.insert_safe_cast(value, NumericType::signed(target_type_size)); + } + value + } + ( + Type::Numeric(NumericType::Unsigned { bit_size: incoming_type_size }), + NumericType::Signed { bit_size: target_type_size }, + ) => { + if *incoming_type_size != target_type_size { + value = self.insert_safe_cast(value, NumericType::unsigned(target_type_size)); + } + value + } + ( + Type::Numeric(NumericType::NativeField), + NumericType::Unsigned { bit_size: target_type_size }, + ) + | ( + Type::Numeric(NumericType::NativeField), + NumericType::Signed { bit_size: target_type_size }, + ) => self.insert_truncate(value, target_type_size, FieldElement::max_num_bits()), + _ => unreachable!("Invalid cast from {} to {}", incoming_type, typ), + }; + self.insert_cast(result, typ) + } + + /// Insert a numeric constant into the current function + fn numeric_constant(&mut self, value: impl Into, typ: NumericType) -> ValueId { + self.context.dfg.make_constant(value.into(), typ) + } + + /// Insert a binary instruction at the end of the current block. + /// Returns the result of the binary instruction. + fn insert_binary(&mut self, lhs: ValueId, operator: BinaryOp, rhs: ValueId) -> ValueId { + let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); + self.context.insert_instruction(instruction, None).first() + } + + /// Insert a not instruction at the end of the current block. + /// Returns the result of the instruction. + fn insert_not(&mut self, rhs: ValueId) -> ValueId { + self.context.insert_instruction(Instruction::Not(rhs), None).first() + } + + /// Insert a truncate instruction at the end of the current block. + /// Returns the result of the truncate instruction. + fn insert_truncate(&mut self, value: ValueId, bit_size: u32, max_bit_size: u32) -> ValueId { + self.context + .insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }, None) + .first() + } + + /// Insert a cast instruction at the end of the current block. + /// Returns the result of the cast instruction. + fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { + self.context.insert_instruction(Instruction::Cast(value, typ), None).first() + } + + /// Insert a [`Instruction::RangeCheck`] instruction at the end of the current block. + fn insert_range_check( + &mut self, + value: ValueId, + max_bit_size: u32, + assert_message: Option, + ) { + self.context.insert_instruction( + Instruction::RangeCheck { value, max_bit_size, assert_message }, + None, + ); + } + + /// Insert a constrain instruction at the end of the current block. + fn insert_constrain( + &mut self, + lhs: ValueId, + rhs: ValueId, + assert_message: Option, + ) { + self.context.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); + } +} + +/// Post-check condition for [Function::expand_signed_checks]. +/// +/// Succeeds if: +/// - `func` does not contain any checked signed ops +/// +/// Otherwise panics. +#[cfg(debug_assertions)] +fn expand_signed_checks_post_check(func: &Function) { + for block_id in func.reachable_blocks() { + let instruction_ids = func.dfg[block_id].instructions(); + for instruction_id in instruction_ids { + if let Instruction::Binary(binary) = &func.dfg[*instruction_id] { + if func.dfg.type_of_value(binary.lhs).is_signed() { + match binary.operator { + BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false } => { + panic!("Checked signed binary operation has not been removed") + } + _ => (), + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + assert_ssa_snapshot, + ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}, + }; + + #[test] + fn expands_checked_add_instruction() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = add v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_ssa_snapshot!(ssa, @r#" + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_add v0, v1 + v3 = truncate v2 to 32 bits, max_bit_size: 33 + v4 = cast v3 as u32 + v5 = cast v0 as u32 + v6 = cast v1 as u32 + v8 = lt v5, u32 2147483648 + v9 = lt v6, u32 2147483648 + v10 = eq v8, v9 + v11 = lt v4, u32 2147483648 + v12 = eq v11, v8 + v13 = unchecked_mul v12, v10 + constrain v13 == v10, "attempt to add with overflow" + v14 = cast v3 as i32 + return v14 + } + "#); + } + + #[test] + fn expands_checked_sub_instruction() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = sub v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_ssa_snapshot!(ssa, @r#" + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_sub v0, v1 + v3 = truncate v2 to 32 bits, max_bit_size: 33 + v4 = cast v3 as u32 + v5 = cast v0 as u32 + v6 = cast v1 as u32 + v8 = lt v5, u32 2147483648 + v9 = lt v6, u32 2147483648 + v10 = not v9 + v11 = eq v8, v10 + v12 = lt v4, u32 2147483648 + v13 = eq v12, v8 + v14 = unchecked_mul v13, v11 + constrain v14 == v11, "attempt to subtract with overflow" + v15 = cast v3 as i32 + return v15 + } + "#); + } + + #[test] + fn expands_checked_mul_instruction() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = mul v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_ssa_snapshot!(ssa, @r#" + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_mul v0, v1 + v3 = cast v2 as u64 + v4 = truncate v3 to 32 bits, max_bit_size: 64 + v5 = cast v0 as u32 + v6 = cast v1 as u32 + v8 = lt v5, u32 2147483648 + v9 = lt v6, u32 2147483648 + v10 = eq v8, v9 + v11 = not v8 + v12 = cast v0 as Field + v13 = cast v8 as Field + v14 = mul v13, v12 + v16 = sub Field 4294967296, v12 + v17 = cast v11 as Field + v18 = mul v17, v16 + v19 = add v14, v18 + v20 = not v9 + v21 = cast v1 as Field + v22 = cast v9 as Field + v23 = mul v22, v21 + v24 = sub Field 4294967296, v21 + v25 = cast v20 as Field + v26 = mul v25, v24 + v27 = add v23, v26 + v28 = mul v19, v27 + range_check v28 to 32 bits, "attempt to multiply with overflow" + v29 = truncate v28 to 32 bits, max_bit_size: 254 + v30 = cast v29 as u32 + v31 = not v10 + v32 = cast v31 as u32 + v33 = unchecked_add u32 2147483648, v32 + v34 = lt v30, v33 + constrain v34 == u1 1, "attempt to add with overflow" + v36 = cast v4 as i32 + return v36 + } + "#); + } + + #[test] + fn ignores_unchecked_add() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_add v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn ignores_unchecked_sub() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_sub v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn ignores_unchecked_mul() { + let src = " + acir(inline) fn main f0 { + b0(v0: i32, v1: i32): + v2 = unchecked_mul v0, v1 + return v2 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.expand_signed_checks(); + assert_normalized_ssa_equals(ssa, src); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 7521be09d1c..cfccfc6e640 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -15,6 +15,7 @@ mod checked_to_unchecked; mod constant_folding; mod defunctionalize; mod die; +mod expand_signed_checks; pub(crate) mod flatten_cfg; mod hint; mod inline_simple_functions; diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 005a944acd3..0701e15c174 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, sync::Arc}; use acvm::{FieldElement, acir::AcirField}; -use noirc_errors::call_stack::CallStackId; use crate::ssa::{ ir::{ @@ -60,7 +59,6 @@ impl Function { context.remove_current_instruction(); - let call_stack = context.dfg.get_instruction_call_stack_id(instruction_id); let old_result = *context.dfg.instruction_results(instruction_id).first().unwrap(); let bit_size = match context.dfg.type_of_value(lhs) { @@ -69,12 +67,11 @@ impl Function { _ => unreachable!("ICE: right-shift attempted on non-integer"), }; + let mut bitshift_context = Context { context }; let new_result = if operator == BinaryOp::Shl { - let mut context = Context { context, call_stack }; - context.insert_wrapping_shift_left(lhs, rhs, bit_size) + bitshift_context.insert_wrapping_shift_left(lhs, rhs, bit_size) } else { - let mut context = Context { context, call_stack }; - context.insert_shift_right(lhs, rhs, bit_size) + bitshift_context.insert_shift_right(lhs, rhs, bit_size) }; context.replace_value(old_result, new_result); @@ -87,7 +84,6 @@ impl Function { struct Context<'m, 'dfg, 'mapping> { context: &'m mut SimpleOptimizationContext<'dfg, 'mapping>, - call_stack: CallStackId, } impl Context<'_, '_, '_> { @@ -322,13 +318,13 @@ impl Context<'_, '_, '_> { rhs: ValueId, ) -> ValueId { let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); - self.insert_instruction(instruction, None).first() + self.context.insert_instruction(instruction, None).first() } /// Insert a not instruction at the end of the current block. /// Returns the result of the instruction. pub(crate) fn insert_not(&mut self, rhs: ValueId) -> ValueId { - self.insert_instruction(Instruction::Not(rhs), None).first() + self.context.insert_instruction(Instruction::Not(rhs), None).first() } /// Insert a truncate instruction at the end of the current block. @@ -339,14 +335,15 @@ impl Context<'_, '_, '_> { bit_size: u32, max_bit_size: u32, ) -> ValueId { - self.insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }, None) + self.context + .insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }, None) .first() } /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { - self.insert_instruction(Instruction::Cast(value, typ), None).first() + self.context.insert_instruction(Instruction::Cast(value, typ), None).first() } /// Insert a call instruction at the end of the current block and return @@ -357,7 +354,9 @@ impl Context<'_, '_, '_> { arguments: Vec, result_types: Vec, ) -> Cow<[ValueId]> { - self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results() + self.context + .insert_instruction(Instruction::Call { func, arguments }, Some(result_types)) + .results() } /// Insert an instruction to extract an element from an array @@ -370,20 +369,7 @@ impl Context<'_, '_, '_> { let element_type = Some(vec![element_type]); let offset = ArrayOffset::None; let instruction = Instruction::ArrayGet { array, index, offset }; - self.insert_instruction(instruction, element_type).first() - } - - pub(crate) fn insert_instruction( - &mut self, - instruction: Instruction, - ctrl_typevars: Option>, - ) -> InsertInstructionResult { - self.context.dfg.insert_instruction_and_results( - instruction, - self.context.block_id, - ctrl_typevars, - self.call_stack, - ) + self.context.insert_instruction(instruction, element_type).first() } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 921c0528649..75ac707f3eb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -93,7 +93,7 @@ impl Function { if let Some(enable_side_effects_instruction_id) = last_side_effects_enabled_instruction.take() { - context.insert_instruction(enable_side_effects_instruction_id); + context.insert_instruction_by_id(enable_side_effects_instruction_id); } } }); diff --git a/compiler/noirc_evaluator/src/ssa/opt/simple_optimization.rs b/compiler/noirc_evaluator/src/ssa/opt/simple_optimization.rs index 1e0405b0e34..a0c1d0d141d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simple_optimization.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simple_optimization.rs @@ -1,13 +1,16 @@ +use noirc_errors::call_stack::CallStackId; + use acvm::FieldElement; use crate::{ errors::RtResult, ssa::ir::{ basic_block::BasicBlockId, - dfg::DataFlowGraph, + dfg::{DataFlowGraph, InsertInstructionResult}, function::Function, instruction::{Instruction, InstructionId}, types::NumericType, + types::Type, value::{ValueId, ValueMapping}, }, }; @@ -73,9 +76,11 @@ impl Function { if let Instruction::EnableSideEffectsIf { condition } = instruction { enable_side_effects = *condition; } + let call_stack_id = self.dfg.get_instruction_call_stack_id(instruction_id); let mut context = SimpleOptimizationContext { block_id, instruction_id, + call_stack_id, dfg: &mut self.dfg, values_to_replace: &mut values_to_replace, insert_current_instruction_at_callback_end: true, @@ -100,6 +105,7 @@ pub(crate) struct SimpleOptimizationContext<'dfg, 'mapping> { #[allow(unused)] pub(crate) block_id: BasicBlockId, pub(crate) instruction_id: InstructionId, + pub(crate) call_stack_id: CallStackId, pub(crate) dfg: &'dfg mut DataFlowGraph, pub(crate) enable_side_effects: ValueId, values_to_replace: &'mapping mut ValueMapping, @@ -131,7 +137,21 @@ impl SimpleOptimizationContext<'_, '_> { } /// Inserts an instruction in the current block right away. - pub(crate) fn insert_instruction(&mut self, instruction_id: InstructionId) { + pub(crate) fn insert_instruction( + &mut self, + instruction: Instruction, + ctrl_typevars: Option>, + ) -> InsertInstructionResult { + self.dfg.insert_instruction_and_results( + instruction, + self.block_id, + ctrl_typevars, + self.call_stack_id, + ) + } + + /// Inserts an instruction by id in the current block right away. + pub(crate) fn insert_instruction_by_id(&mut self, instruction_id: InstructionId) { self.dfg[self.block_id].insert_instruction(instruction_id); } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index e8b9cdcfbe3..4a5f53529d1 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -16,7 +16,6 @@ use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::basic_block::BasicBlockId; use crate::ssa::ir::function::FunctionId as IrFunctionId; use crate::ssa::ir::function::{Function, RuntimeType}; -use crate::ssa::ir::instruction::Instruction; use crate::ssa::ir::instruction::{ArrayOffset, BinaryOp}; use crate::ssa::ir::map::AtomicCounter; use crate::ssa::ir::types::{NumericType, Type}; @@ -313,51 +312,12 @@ impl<'a> FunctionContext<'a> { Ok(self.builder.numeric_constant(value, numeric_type)) } - /// helper function which add instructions to the block computing the absolute value of the - /// given signed integer input. When the input is negative, we return its two complement, and itself when it is positive. - fn absolute_value_helper(&mut self, input: ValueId, sign: ValueId, bit_size: u32) -> ValueId { - assert_eq!(self.builder.type_of_value(sign), Type::bool()); - - // We compute the absolute value of lhs - let bit_width = FieldElement::from(2_i128.pow(bit_size)); - let bit_width = self.builder.numeric_constant(bit_width, NumericType::NativeField); - let sign_not = self.builder.insert_not(sign); - - // We use unsafe casts here, this is fine as we're casting to a `field` type. - let as_field = self.builder.insert_cast(input, NumericType::NativeField); - let sign_field = self.builder.insert_cast(sign, NumericType::NativeField); - - // All of these operations are unchecked because they deal with fields - let positive_predicate = - self.builder.insert_binary(sign_field, BinaryOp::Mul { unchecked: true }, as_field); - let two_complement = - self.builder.insert_binary(bit_width, BinaryOp::Sub { unchecked: true }, as_field); - let sign_not_field = self.builder.insert_cast(sign_not, NumericType::NativeField); - let negative_predicate = self.builder.insert_binary( - sign_not_field, - BinaryOp::Mul { unchecked: true }, - two_complement, - ); - // Unchecked addition because either `positive_predicate` or `negative_predicate` will be 0 - self.builder.insert_binary( - positive_predicate, - BinaryOp::Add { unchecked: true }, - negative_predicate, - ) - } - /// Insert constraints ensuring that the operation does not overflow the bit size of the result /// /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for /// bit-shifts, because we will convert them to field multiplication /// - /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to - /// an unsigned value representing the signed integer. - /// We need to use a bigger bit size depending on the operation, in case the operation does overflow, - /// Then, we delegate the overflow checks to check_signed_overflow() and cast the result back to its type. - /// Note that we do NOT want to check for overflows here, only check_signed_overflow() is allowed to do so. - /// This is because an overflow might be valid. For instance if 'a' is a signed integer, then 'a - a', as an unsigned result will always - /// overflow the bit size, however the operation is still valid (i.e it is not a signed overflow) + /// If the result is signed, overflow will be checked during SSA optimization. fn check_overflow( &mut self, result: ValueId, @@ -370,26 +330,9 @@ impl<'a> FunctionContext<'a> { match result_type { NumericType::Signed { bit_size } => { match operator { - BinaryOpKind::Add | BinaryOpKind::Subtract => { - // Result is computed modulo the bit size - let result = self.builder.insert_truncate(result, bit_size, bit_size + 1); - let result = self.insert_safe_cast( - result, - NumericType::unsigned(bit_size), - location, - ); - - self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); - self.insert_safe_cast(result, result_type, location) - } - BinaryOpKind::Multiply => { - // Result is computed modulo the bit size - let mut result = - self.builder.insert_cast(result, NumericType::unsigned(2 * bit_size)); - result = self.builder.insert_truncate(result, bit_size, 2 * bit_size); - - self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); - self.insert_safe_cast(result, result_type, location) + BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply => { + // Overflow check is deferred to ssa pass + result } BinaryOpKind::ShiftLeft | BinaryOpKind::ShiftRight => { self.check_shift_overflow(result, rhs, bit_size, location) @@ -404,7 +347,7 @@ impl<'a> FunctionContext<'a> { match operator { BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply => { // Overflow check is deferred to acir-gen - return result; + result } BinaryOpKind::ShiftLeft => { if let Some(rhs_const) = dfg.get_numeric_constant(rhs) { @@ -418,12 +361,11 @@ impl<'a> FunctionContext<'a> { } self.check_shift_overflow(result, rhs, bit_size, location); + result } _ => unreachable!("operator {} should not overflow", operator), } - - result } NumericType::NativeField => result, } @@ -454,105 +396,6 @@ impl<'a> FunctionContext<'a> { self.builder.insert_truncate(result, bit_size, bit_size + 1) } - /// Insert constraints ensuring that the operation does not overflow the bit size of the result - /// We assume that: - /// lhs and rhs are signed integers of bit size bit_size - /// result is the result of the operation, casted into an unsigned integer and not reduced - /// - /// overflow check for signed integer is less straightforward than for unsigned integers. - /// We first compute the sign of the operands, and then we use the following rules: - /// addition: positive operands => result must be positive (i.e less than half the bit size) - /// negative operands => result must be negative (i.e not positive) - /// different sign => no overflow - /// multiplication: we check that the product of the operands' absolute values does not overflow the bit size - /// then we check that the result has the proper sign, using the rule of signs - fn check_signed_overflow( - &mut self, - result: ValueId, - lhs: ValueId, - rhs: ValueId, - operator: BinaryOpKind, - bit_size: u32, - location: Location, - ) { - let is_sub = operator == BinaryOpKind::Subtract; - let half_width = self.builder.numeric_constant( - FieldElement::from(2_i128.pow(bit_size - 1)), - NumericType::unsigned(bit_size), - ); - // We compute the sign of the operands. The overflow checks for signed integers depends on these signs - let lhs_as_unsigned = self.insert_safe_cast(lhs, NumericType::unsigned(bit_size), location); - let rhs_as_unsigned = self.insert_safe_cast(rhs, NumericType::unsigned(bit_size), location); - let lhs_sign = self.builder.insert_binary(lhs_as_unsigned, BinaryOp::Lt, half_width); - let mut rhs_sign = self.builder.insert_binary(rhs_as_unsigned, BinaryOp::Lt, half_width); - let message = if is_sub { - // lhs - rhs = lhs + (-rhs) - rhs_sign = self.builder.insert_not(rhs_sign); - "attempt to subtract with overflow".to_string() - } else { - "attempt to add with overflow".to_string() - }; - // same_sign is true if both operands have the same sign - let same_sign = self.builder.insert_binary(lhs_sign, BinaryOp::Eq, rhs_sign); - match operator { - BinaryOpKind::Add | BinaryOpKind::Subtract => { - //Check the result has the same sign as its inputs - let result_sign = self.builder.insert_binary(result, BinaryOp::Lt, half_width); - let sign_diff = self.builder.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); - // Unchecked multiplication because boolean inputs - let sign_diff_with_predicate = self.builder.insert_binary( - sign_diff, - BinaryOp::Mul { unchecked: true }, - same_sign, - ); - let overflow_check = Instruction::Constrain( - sign_diff_with_predicate, - same_sign, - Some(message.into()), - ); - self.builder.set_location(location).insert_instruction(overflow_check, None); - } - BinaryOpKind::Multiply => { - // Overflow check for the multiplication: - // First we compute the absolute value of operands, and their product - let lhs_abs = self.absolute_value_helper(lhs, lhs_sign, bit_size); - let rhs_abs = self.absolute_value_helper(rhs, rhs_sign, bit_size); - // Unchecked mul because these are fields - let product_field = - self.builder.insert_binary(lhs_abs, BinaryOp::Mul { unchecked: true }, rhs_abs); - // It must not already overflow the bit_size - self.builder.set_location(location).insert_range_check( - product_field, - bit_size, - Some("attempt to multiply with overflow".to_string()), - ); - let product = - self.builder.insert_cast(product_field, NumericType::unsigned(bit_size)); - - // Then we check the signed product fits in a signed integer of bit_size-bits - let not_same = self.builder.insert_not(same_sign); - let not_same_sign_field = - self.insert_safe_cast(not_same, NumericType::unsigned(bit_size), location); - // Unchecked add because adding 1 to half_width can't overflow - let positive_maximum_with_offset = self.builder.insert_binary( - half_width, - BinaryOp::Add { unchecked: true }, - not_same_sign_field, - ); - let product_overflow_check = - self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); - - let one = self.builder.numeric_constant(FieldElement::one(), NumericType::bool()); - self.builder.set_location(location).insert_constrain( - product_overflow_check, - one, - Some(message.into()), - ); - } - _ => unreachable!("operator {} should not overflow", operator), - } - } - /// Insert a binary instruction at the end of the current block. /// Converts the form of the binary instruction as necessary /// (e.g. swapping arguments, inserting a not) to represent it in the IR. diff --git a/compiler/noirc_evaluator/src/ssa/validation/mod.rs b/compiler/noirc_evaluator/src/ssa/validation/mod.rs index 4478b6ccc37..b96dea3a32f 100644 --- a/compiler/noirc_evaluator/src/ssa/validation/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/validation/mod.rs @@ -31,9 +31,6 @@ use super::ir::{ struct Validator<'f> { function: &'f Function, ssa: &'f Ssa, - // State for truncate-after-signed-sub validation - // Stores: Option<(bit_size, result)> - signed_binary_op: Option, // State for valid Field to integer casts // Range checks are laid down in isolation and can make for safe casts @@ -42,110 +39,9 @@ struct Validator<'f> { range_checks: HashMap, } -#[derive(Debug)] -enum PendingSignedOverflowOp { - AddOrSub { bit_size: u32, result: ValueId }, - Mul { bit_size: u32, mul_result: ValueId, cast_result: Option }, -} - impl<'f> Validator<'f> { fn new(function: &'f Function, ssa: &'f Ssa) -> Self { - Self { function, ssa, signed_binary_op: None, range_checks: HashMap::default() } - } - - /// Validates that any checked signed add/sub/mul are followed by the appropriate instructions. - /// Signed overflow is many instructions but we validate up to the initial truncate. - /// - /// Expects the following SSA form for signed checked operations: - /// Add/Sub -> Truncate - /// Mul -> Cast -> Truncate - fn validate_signed_op_overflow_pattern(&mut self, instruction: InstructionId) { - let dfg = &self.function.dfg; - match &dfg[instruction] { - Instruction::Binary(binary) => { - // Only reset if we are starting a new tracked op. - // We do not reset on unrelated ops. If we already an op pending, we have an ill formed signed op. - if self.signed_binary_op.is_some() { - panic!("Signed binary operation does not follow overflow pattern"); - } - - // Assumes rhs_type is the same as lhs_type - let lhs_type = dfg.type_of_value(binary.lhs); - let Type::Numeric(NumericType::Signed { bit_size }) = lhs_type else { - return; - }; - - let result = dfg.instruction_results(instruction)[0]; - match binary.operator { - BinaryOp::Mul { unchecked: false } => { - self.signed_binary_op = Some(PendingSignedOverflowOp::Mul { - bit_size, - mul_result: result, - cast_result: None, - }); - } - BinaryOp::Add { unchecked: false } | BinaryOp::Sub { unchecked: false } => { - self.signed_binary_op = - Some(PendingSignedOverflowOp::AddOrSub { bit_size, result }); - } - _ => {} - } - } - Instruction::Truncate { value, bit_size, max_bit_size } => { - // Only a truncate can reset the signed binary op state - match self.signed_binary_op.take() { - Some(PendingSignedOverflowOp::AddOrSub { - bit_size: expected_bit_size, - result, - }) => { - assert_eq!(*bit_size, expected_bit_size); - assert_eq!(*max_bit_size, expected_bit_size + 1); - assert_eq!(*value, result); - } - Some(PendingSignedOverflowOp::Mul { - bit_size: expected_bit_size, - cast_result: Some(cast), - .. - }) => { - assert_eq!(*bit_size, expected_bit_size); - assert_eq!(*max_bit_size, 2 * expected_bit_size); - assert_eq!(*value, cast); - } - Some(PendingSignedOverflowOp::Mul { cast_result: None, .. }) => { - panic!("Truncate not matched to signed overflow pattern"); - } - None => { - // Do nothing as there is no overflow op pending - } - } - } - Instruction::Cast(value, typ) => { - match &mut self.signed_binary_op { - Some(PendingSignedOverflowOp::AddOrSub { .. }) => { - panic!( - "Invalid cast inserted after signed checked Add/Sub. It must be followed immediately by truncate" - ); - } - Some(PendingSignedOverflowOp::Mul { - bit_size: expected_bit_size, - mul_result, - cast_result, - }) => { - assert_eq!(typ.bit_size(), 2 * *expected_bit_size); - assert_eq!(*value, *mul_result); - *cast_result = Some(dfg.instruction_results(instruction)[0]); - } - None => { - // Do nothing as there is no overflow op pending - } - } - } - _ => { - if self.signed_binary_op.is_some() { - panic!("Signed binary operation does not follow overflow pattern"); - } - } - } + Self { function, ssa, range_checks: HashMap::default() } } /// Enforces that every cast from Field -> unsigned/signed integer must obey the following invariants: @@ -383,15 +279,10 @@ impl<'f> Validator<'f> { for block in self.function.reachable_blocks() { for instruction in self.function.dfg[block].instructions() { - self.validate_signed_op_overflow_pattern(*instruction); self.validate_field_to_integer_cast_invariant(*instruction); self.type_check_instruction(*instruction); } } - - if self.signed_binary_op.is_some() { - panic!("Signed binary operation does not follow overflow pattern"); - } } } @@ -407,291 +298,6 @@ pub(crate) fn validate_function(function: &Function, ssa: &Ssa) { mod tests { use crate::ssa::ssa_gen::Ssa; - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn lone_signed_sub_acir() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - return v2 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn lone_signed_sub_brillig() { - // This matches the test above we just want to make sure it holds in the Brillig runtime as well as ACIR - let src = r" - brillig(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - return v2 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn lone_signed_add_acir() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = add v0, v1 - return v2 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn lone_signed_add_brillig() { - let src = r" - brillig(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = add v0, v1 - return v2 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_sub_bad_truncate_bit_size() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - v3 = truncate v2 to 32 bits, max_bit_size: 33 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_sub_bad_truncate_max_bit_size() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 18 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - fn truncate_follows_signed_sub_acir() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 17 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - fn truncate_follows_signed_sub_brillig() { - let src = r" - brillig(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = sub v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 17 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - fn truncate_follows_signed_add_acir() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = add v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 17 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - fn truncate_follows_signed_add_brillig() { - let src = r" - brillig(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = add v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 17 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic( - expected = "Invalid cast inserted after signed checked Add/Sub. It must be followed immediately by truncate" - )] - fn cast_and_truncate_follows_signed_add() { - let src = r" - brillig(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = add v0, v1 - v3 = cast v2 as i32 - v4 = truncate v2 to 16 bits, max_bit_size: 17 - return v4 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn signed_mul_followed_by_binary() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: Field): - v1 = truncate v0 to 16 bits, max_bit_size: 254 - v2 = cast v1 as i16 - v3 = mul v2, v2 - v4 = div v3, v2 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - fn signed_mul_followed_by_cast_and_truncate() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: i16): - v1 = mul v0, v0 - v2 = cast v1 as u32 - v3 = truncate v2 to 16 bits, max_bit_size: 32 - v4 = cast v3 as i16 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_mul_followed_by_bad_cast() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: i16): - v1 = mul v0, v0 - v2 = cast v0 as u16 - v3 = truncate v2 to 16 bits, max_bit_size: 32 - v4 = cast v3 as i16 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_mul_followed_by_bad_cast_bit_size() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: i16): - v1 = mul v0, v0 - v2 = cast v1 as u16 - v3 = truncate v2 to 16 bits, max_bit_size: 32 - v4 = cast v3 as i16 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_mul_followed_by_bad_truncate_bit_size() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: i16): - v1 = mul v0, v0 - v2 = cast v1 as u32 - v3 = truncate v2 to 32 bits, max_bit_size: 32 - v4 = cast v3 as i16 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "assertion `left == right` failed")] - fn signed_mul_followed_by_bad_truncate_max_bit_size() { - let src = " - acir(inline) predicate_pure fn main f0 { - b0(v0: i16): - v1 = mul v0, v0 - v2 = cast v1 as u32 - v3 = truncate v2 to 16 bits, max_bit_size: 33 - v4 = cast v3 as i16 - return v4 - } - "; - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Signed binary operation does not follow overflow pattern")] - fn lone_signed_mul() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = mul v0, v1 - return v2 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - - #[test] - #[should_panic(expected = "Truncate not matched to signed overflow pattern")] - fn signed_mul_followed_by_truncate_but_no_cast() { - let src = r" - acir(inline) pure fn main f0 { - b0(v0: i16, v1: i16): - v2 = mul v0, v1 - v3 = truncate v2 to 16 bits, max_bit_size: 33 - return v3 - } - "; - - let _ = Ssa::from_str(src).unwrap(); - } - #[test] fn lone_truncate() { let src = r" diff --git a/test_programs/execution_success/regression_9047/Nargo.toml b/test_programs/execution_success/regression_9047/Nargo.toml new file mode 100644 index 00000000000..8b7dc3a97be --- /dev/null +++ b/test_programs/execution_success/regression_9047/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_9047" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/regression_9047/Prover.toml b/test_programs/execution_success/regression_9047/Prover.toml new file mode 100644 index 00000000000..6e30d344d8a --- /dev/null +++ b/test_programs/execution_success/regression_9047/Prover.toml @@ -0,0 +1 @@ +return = "ok" diff --git a/test_programs/execution_success/regression_9047/src/main.nr b/test_programs/execution_success/regression_9047/src/main.nr new file mode 100644 index 00000000000..240f12b4b75 --- /dev/null +++ b/test_programs/execution_success/regression_9047/src/main.nr @@ -0,0 +1,11 @@ +fn main() -> pub str<2> { + let mut b = 0_i8; + if unsafe { func_1_proxy() } { + b = 112; + b = ((-b) * b); + }; + "ok" +} +unconstrained fn func_1_proxy() -> bool { + false +} diff --git a/tooling/ast_fuzzer/src/compare/interpreted.rs b/tooling/ast_fuzzer/src/compare/interpreted.rs index 6727222220a..2d2b292c44f 100644 --- a/tooling/ast_fuzzer/src/compare/interpreted.rs +++ b/tooling/ast_fuzzer/src/compare/interpreted.rs @@ -10,7 +10,7 @@ use noirc_abi::{Abi, AbiType, InputMap, Sign, input_parser::InputValue}; use noirc_evaluator::ssa::{ self, interpreter::{InterpreterOptions, value::Value}, - ir::types::NumericType, + ir::{instruction::BinaryOp, types::NumericType}, ssa_gen::Ssa, }; use noirc_frontend::{Shared, monomorphization::ast::Program}; @@ -173,7 +173,7 @@ impl Comparable for ssa::interpreter::errors::InterpreterError { // They mean the interpreter got something unexpected that we need to fix. false } - (Overflow { instruction: i1 }, Overflow { instruction: i2 }) => { + (Overflow { instruction: i1, .. }, Overflow { instruction: i2, .. }) => { // Overflows can occur or instructions with different IDs, but in a parentheses it contains the values that caused it. fn details(s: &str) -> Option<&str> { let start = s.find("(")?; @@ -185,6 +185,22 @@ impl Comparable for ssa::interpreter::errors::InterpreterError { } details_or_sanitize(i1) == details_or_sanitize(i2) } + + // We expand checked operations on signed types into multiple instructions during the `expand_signed_checks` + // pass. This results in the error changing from an `Overflow` into a different error type so we match + // on the attached error message. + (Overflow { operator, .. }, ConstrainEqFailed { msg: Some(msg), .. }) => match operator + { + BinaryOp::Add { unchecked: false } => msg == "attempt to add with overflow", + BinaryOp::Sub { unchecked: false } => msg == "attempt to subtract with overflow", + + _ => false, + }, + ( + Overflow { operator: BinaryOp::Mul { unchecked: false }, .. }, + RangeCheckFailed { msg: Some(msg), .. }, + ) => msg == "attempt to multiply with overflow", + ( ConstrainEqFailed { msg: msg1, .. } | ConstrainNeFailed { msg: msg1, .. }, ConstrainEqFailed { msg: msg2, .. } | ConstrainNeFailed { msg: msg2, .. }, diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/higher_order_functions/execute__tests__force_brillig_true_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/higher_order_functions/execute__tests__force_brillig_true_inliner_0.snap index 5c0d5abb9ad..49da860585d 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/higher_order_functions/execute__tests__force_brillig_true_inliner_0.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/higher_order_functions/execute__tests__force_brillig_true_inliner_0.snap @@ -48,9 +48,9 @@ expression: artifact "return value indices : [_1]", "BRILLIG CALL func 0: inputs: [Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })], outputs: [Simple(Witness(1))]", "unconstrained func 0", - "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32845 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32843), size_address: Relative(2), offset_address: Relative(3) }, Mov { destination: Relative(1), source: Direct(32843) }, Call { location: 13 }, Call { location: 22 }, Mov { destination: Direct(32844), source: Relative(1) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32844 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 1 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32837), bit_size: Integer(U1), value: 1 }, Const { destination: Direct(32838), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32839), bit_size: Integer(U32), value: 3 }, Const { destination: Direct(32840), bit_size: Field, value: 17 }, Const { destination: Direct(32841), bit_size: Field, value: 33 }, Const { destination: Direct(32842), bit_size: Integer(U32), value: 2147483648 }, Return, Call { location: 32 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 3 }, Mov { destination: Relative(3), source: Direct(0) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(2) }, Call { location: 38 }, Mov { destination: Direct(0), source: Relative(0) }, Const { destination: Relative(2), bit_size: Field, value: 5 }, BinaryFieldOp { destination: Relative(3), op: Add, lhs: Relative(1), rhs: Relative(2) }, Mov { destination: Relative(1), source: Relative(3) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 37 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 32 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 3 }, Mov { destination: Relative(5), source: Direct(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(6) }, IndirectConst { destination_pointer: Relative(5), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Mov { destination: Relative(7), source: Relative(6) }, Store { destination_pointer: Relative(7), source: Relative(2) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(3) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(4) }, Load { destination: Relative(4), source_pointer: Relative(5) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(4) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 59 }, Call { location: 510 }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(4) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 8 }, Mov { destination: Relative(8), source: Direct(0) }, Mov { destination: Relative(9), source: Relative(5) }, Mov { destination: Relative(10), source: Relative(3) }, Mov { destination: Relative(11), source: Direct(32840) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 513 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(4), source: Relative(9) }, Load { destination: Relative(7), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(7) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 76 }, Call { location: 510 }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(7) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32838) }, Load { destination: Relative(7), source_pointer: Relative(9) }, Const { destination: Relative(12), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(12) }, BinaryIntOp { destination: Relative(11), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(12) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Relative(11) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 2 }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(4), rhs: Relative(10) }, Load { destination: Relative(11), source_pointer: Relative(12) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(14) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(11), rhs: Relative(14) }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(12), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32839) }, Load { destination: Relative(12), source_pointer: Relative(13) }, Const { destination: Relative(16), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(14), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(16) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(12), rhs: Relative(16) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(15) }, JumpIf { condition: Relative(9), location: 100 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, JumpIf { condition: Relative(10), location: 103 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, JumpIf { condition: Relative(13), location: 106 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(2) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 112 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32837) }, Load { destination: Relative(10), source_pointer: Relative(4) }, Const { destination: Relative(13), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(14), op: Equals, bit_size: U32, lhs: Relative(13), rhs: Relative(10) }, Not { destination: Relative(14), source: Relative(14), bit_size: U1 }, JumpIf { condition: Relative(14), location: 123 }, Call { location: 510 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(10) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 127 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 496 }, Jump { location: 130 }, Load { destination: Relative(6), source_pointer: Relative(2) }, JumpIf { condition: Relative(6), location: 134 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 140 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Load { destination: Relative(8), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 151 }, Call { location: 510 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(8) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 474 }, Jump { location: 158 }, Load { destination: Relative(6), source_pointer: Relative(2) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 12 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, JumpIf { condition: Relative(8), location: 164 }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(9) } }, Load { destination: Relative(6), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 170 }, Call { location: 510 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(6) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Direct(32835) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(13), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(13), source: Relative(13), bit_size: U1 }, JumpIf { condition: Relative(13), location: 181 }, Call { location: 510 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 185 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(8), location: 443 }, Jump { location: 188 }, Load { destination: Relative(8), source_pointer: Relative(6) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 18 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, JumpIf { condition: Relative(9), location: 194 }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(10) } }, Load { destination: Relative(6), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 200 }, Call { location: 510 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(6) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Relative(7) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(13), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(13), source: Relative(13), bit_size: U1 }, JumpIf { condition: Relative(13), location: 211 }, Call { location: 510 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Mov { destination: Relative(1), source: Direct(32838) }, Jump { location: 215 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(8), location: 421 }, Jump { location: 218 }, Load { destination: Relative(8), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(2) }, JumpIf { condition: Relative(6), location: 223 }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(9) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 229 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(7) }, Load { destination: Relative(8), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 240 }, Call { location: 510 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(8) }, Mov { destination: Relative(1), source: Direct(32838) }, Jump { location: 244 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 390 }, Jump { location: 247 }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 16 }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(6), location: 253 }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(8) } }, Load { destination: Relative(1), source_pointer: Relative(4) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(2), rhs: Relative(1) }, Not { destination: Relative(6), source: Relative(6), bit_size: U1 }, JumpIf { condition: Relative(6), location: 259 }, Call { location: 510 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(7) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(7) }, JumpIf { condition: Relative(6), location: 267 }, Jump { location: 269 }, Mov { destination: Relative(8), source: Relative(16) }, Jump { location: 271 }, Mov { destination: Relative(8), source: Relative(7) }, Jump { location: 271 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(9), location: 277 }, Jump { location: 279 }, Mov { destination: Relative(10), source: Relative(16) }, Jump { location: 281 }, Mov { destination: Relative(10), source: Relative(3) }, Jump { location: 281 }, BinaryIntOp { destination: Relative(1), op: Div, bit_size: U32, lhs: Relative(8), rhs: Relative(10) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(6), rhs: Relative(9) }, JumpIf { condition: Relative(13), location: 285 }, Jump { location: 287 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(1), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(1) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(7), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(11) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(11) }, JumpIf { condition: Relative(7), location: 293 }, Jump { location: 295 }, Mov { destination: Relative(8), source: Relative(16) }, Jump { location: 297 }, Mov { destination: Relative(8), source: Relative(11) }, Jump { location: 297 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(9), location: 303 }, Jump { location: 305 }, Mov { destination: Relative(10), source: Relative(16) }, Jump { location: 307 }, Mov { destination: Relative(10), source: Relative(3) }, Jump { location: 307 }, BinaryIntOp { destination: Relative(6), op: Div, bit_size: U32, lhs: Relative(8), rhs: Relative(10) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(7), rhs: Relative(9) }, JumpIf { condition: Relative(13), location: 311 }, Jump { location: 313 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(6), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(6) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(12) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(12) }, JumpIf { condition: Relative(8), location: 319 }, Jump { location: 321 }, Mov { destination: Relative(9), source: Relative(16) }, Jump { location: 323 }, Mov { destination: Relative(9), source: Relative(12) }, Jump { location: 323 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(10), location: 329 }, Jump { location: 331 }, Mov { destination: Relative(11), source: Relative(16) }, Jump { location: 333 }, Mov { destination: Relative(11), source: Relative(3) }, Jump { location: 333 }, BinaryIntOp { destination: Relative(7), op: Div, bit_size: U32, lhs: Relative(9), rhs: Relative(11) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(8), rhs: Relative(10) }, JumpIf { condition: Relative(13), location: 337 }, Jump { location: 339 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(7) }, Mov { destination: Relative(8), source: Direct(1) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(9) }, IndirectConst { destination_pointer: Relative(8), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Mov { destination: Relative(10), source: Relative(9) }, Store { destination_pointer: Relative(10), source: Relative(1) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(10), source: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(10), source: Relative(7) }, Load { destination: Relative(1), source_pointer: Relative(5) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(1) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 356 }, Call { location: 510 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(1) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(8) }, Mov { destination: Relative(11), source: Relative(5) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 675 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(1), source: Relative(10) }, JumpIf { condition: Relative(1), location: 369 }, Const { destination: Relative(7), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(7) } }, Const { destination: Relative(7), bit_size: Integer(U32), value: 8 }, Mov { destination: Relative(8), source: Direct(0) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(3) }, Mov { destination: Relative(11), source: Direct(32841) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 513 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(1), source: Relative(9) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(5) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(4) }, Call { location: 675 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(3), source: Relative(8) }, JumpIf { condition: Relative(3), location: 389 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(1) } }, Return, Load { destination: Relative(6), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(10) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(6), rhs: Relative(8) }, Mov { destination: Relative(10), source: Relative(9) }, Cast { destination: Relative(9), source: Relative(10), bit_size: Integer(U32) }, Cast { destination: Relative(13), source: Relative(6), bit_size: Integer(U32) }, Cast { destination: Relative(6), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(13), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(13), rhs: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Mul, bit_size: U1, lhs: Relative(9), rhs: Relative(6) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(6) }, JumpIf { condition: Relative(9), location: 408 }, Call { location: 707 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(10), rhs: Relative(3) }, Mov { destination: Relative(8), source: Relative(6) }, Cast { destination: Relative(6), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(13) }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(6), rhs: Relative(13) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(13) }, JumpIf { condition: Relative(6), location: 417 }, Call { location: 707 }, Store { destination_pointer: Relative(2), source: Relative(8) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 244 }, Load { destination: Relative(8), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(10), rhs: Relative(1) }, Load { destination: Relative(9), source_pointer: Relative(13) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(9) }, Mov { destination: Relative(13), source: Relative(10) }, Cast { destination: Relative(10), source: Relative(13), bit_size: Integer(U32) }, Cast { destination: Relative(14), source: Relative(8), bit_size: Integer(U32) }, Cast { destination: Relative(8), source: Relative(9), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(14), rhs: Relative(9) }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(10), rhs: Relative(8) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(8) }, JumpIf { condition: Relative(10), location: 439 }, Call { location: 707 }, Store { destination_pointer: Relative(6), source: Relative(13) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(8) }, Jump { location: 215 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(10) }, Load { destination: Relative(9), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Mov { destination: Relative(13), source: Relative(10) }, Cast { destination: Relative(10), source: Relative(13), bit_size: Integer(U32) }, Cast { destination: Relative(14), source: Relative(9), bit_size: Integer(U32) }, Cast { destination: Relative(9), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(14), rhs: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Mul, bit_size: U1, lhs: Relative(10), rhs: Relative(9) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(9) }, JumpIf { condition: Relative(10), location: 461 }, Call { location: 707 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(13), rhs: Relative(3) }, Mov { destination: Relative(9), source: Relative(8) }, Cast { destination: Relative(8), source: Relative(9), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(10), rhs: Relative(14) }, BinaryIntOp { destination: Relative(10), op: Mul, bit_size: U1, lhs: Relative(8), rhs: Relative(14) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(10), rhs: Relative(14) }, JumpIf { condition: Relative(8), location: 470 }, Call { location: 707 }, Store { destination_pointer: Relative(6), source: Relative(9) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(8) }, Jump { location: 185 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(1) }, Load { destination: Relative(6), source_pointer: Relative(9) }, Load { destination: Relative(8), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Mov { destination: Relative(10), source: Relative(9) }, Cast { destination: Relative(9), source: Relative(10), bit_size: Integer(U32) }, Cast { destination: Relative(13), source: Relative(8), bit_size: Integer(U32) }, Cast { destination: Relative(8), source: Relative(6), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(13), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(6), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(13), rhs: Relative(6) }, BinaryIntOp { destination: Relative(6), op: Mul, bit_size: U1, lhs: Relative(9), rhs: Relative(8) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(6), rhs: Relative(8) }, JumpIf { condition: Relative(9), location: 492 }, Call { location: 707 }, Store { destination_pointer: Relative(2), source: Relative(10) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(1) }, Load { destination: Relative(6), source_pointer: Relative(9) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(6), rhs: Relative(14) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(3), rhs: Relative(14) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Relative(13) }, Not { destination: Relative(6), source: Relative(9), bit_size: U1 }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(8), rhs: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(9) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 127 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 32 }, Mov { destination: Relative(5), source: Direct(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(6) }, IndirectConst { destination_pointer: Relative(5), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Mov { destination: Relative(7), source: Relative(6) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Relative(5) }, Load { destination: Relative(5), source_pointer: Relative(1) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(7), rhs: Relative(5) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 534 }, Call { location: 510 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(5) }, BinaryFieldOp { destination: Relative(5), op: Equals, lhs: Relative(3), rhs: Direct(32840) }, Cast { destination: Relative(8), source: Relative(2), bit_size: Field }, Const { destination: Relative(9), bit_size: Field, value: 4294967296 }, BinaryFieldOp { destination: Relative(10), op: Sub, lhs: Relative(9), rhs: Relative(8) }, Const { destination: Relative(11), bit_size: Field, value: 23 }, BinaryFieldOp { destination: Relative(12), op: Equals, lhs: Relative(3), rhs: Relative(11) }, Const { destination: Relative(11), bit_size: Field, value: 27 }, BinaryFieldOp { destination: Relative(13), op: Equals, lhs: Relative(3), rhs: Relative(11) }, Mov { destination: Relative(4), source: Direct(32836) }, Jump { location: 546 }, BinaryIntOp { destination: Relative(7), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32839) }, JumpIf { condition: Relative(7), location: 551 }, Jump { location: 549 }, Load { destination: Relative(1), source_pointer: Relative(6) }, Return, BinaryIntOp { destination: Relative(14), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(14), rhs: Relative(4) }, Load { destination: Relative(11), source_pointer: Relative(15) }, Cast { destination: Relative(14), source: Relative(2), bit_size: Integer(U32) }, Cast { destination: Relative(15), source: Relative(11), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(16), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(18), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(19), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, JumpIf { condition: Relative(5), location: 626 }, Jump { location: 562 }, JumpIf { condition: Relative(12), location: 612 }, Jump { location: 564 }, JumpIf { condition: Relative(13), location: 598 }, Jump { location: 566 }, BinaryFieldOp { destination: Relative(16), op: Equals, lhs: Relative(3), rhs: Direct(32841) }, JumpIf { condition: Relative(16), location: 570 }, Const { destination: Relative(17), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(17) } }, Const { destination: Relative(22), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(22), rhs: Relative(11) }, Const { destination: Relative(23), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(24), op: Sub, bit_size: U32, lhs: Relative(23), rhs: Relative(11) }, JumpIf { condition: Relative(17), location: 576 }, Jump { location: 578 }, Mov { destination: Relative(18), source: Relative(24) }, Jump { location: 580 }, Mov { destination: Relative(18), source: Relative(11) }, Jump { location: 580 }, Const { destination: Relative(22), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(19), op: LessThan, bit_size: U32, lhs: Relative(22), rhs: Relative(2) }, Const { destination: Relative(23), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(24), op: Sub, bit_size: U32, lhs: Relative(23), rhs: Relative(2) }, JumpIf { condition: Relative(19), location: 586 }, Jump { location: 588 }, Mov { destination: Relative(20), source: Relative(24) }, Jump { location: 590 }, Mov { destination: Relative(20), source: Relative(2) }, Jump { location: 590 }, BinaryIntOp { destination: Relative(16), op: Div, bit_size: U32, lhs: Relative(18), rhs: Relative(20) }, BinaryIntOp { destination: Relative(21), op: Xor, bit_size: U1, lhs: Relative(17), rhs: Relative(19) }, JumpIf { condition: Relative(21), location: 594 }, Jump { location: 596 }, Const { destination: Relative(22), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(22), rhs: Relative(16) }, Mov { destination: Relative(15), source: Relative(16) }, Jump { location: 610 }, BinaryIntOp { destination: Relative(16), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(11) }, Mov { destination: Relative(11), source: Relative(16) }, Cast { destination: Relative(16), source: Relative(11), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(17), op: Equals, bit_size: U1, lhs: Relative(18), rhs: Relative(19) }, BinaryIntOp { destination: Relative(19), op: LessThan, bit_size: U32, lhs: Relative(16), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(16), op: Equals, bit_size: U1, lhs: Relative(19), rhs: Relative(18) }, BinaryIntOp { destination: Relative(18), op: Mul, bit_size: U1, lhs: Relative(16), rhs: Relative(17) }, BinaryIntOp { destination: Relative(16), op: Equals, bit_size: U1, lhs: Relative(18), rhs: Relative(17) }, JumpIf { condition: Relative(16), location: 608 }, Call { location: 707 }, Mov { destination: Relative(15), source: Relative(11) }, Jump { location: 610 }, Mov { destination: Relative(14), source: Relative(15) }, Jump { location: 624 }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(11) }, Mov { destination: Relative(11), source: Relative(15) }, Cast { destination: Relative(15), source: Relative(11), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(18), op: Equals, bit_size: U1, lhs: Relative(16), rhs: Relative(17) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(15), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(16) }, BinaryIntOp { destination: Relative(16), op: Mul, bit_size: U1, lhs: Relative(15), rhs: Relative(18) }, BinaryIntOp { destination: Relative(15), op: Equals, bit_size: U1, lhs: Relative(16), rhs: Relative(18) }, JumpIf { condition: Relative(15), location: 622 }, Call { location: 707 }, Mov { destination: Relative(14), source: Relative(11) }, Jump { location: 624 }, Mov { destination: Relative(7), source: Relative(14) }, Jump { location: 663 }, BinaryIntOp { destination: Relative(16), op: Mul, bit_size: U32, lhs: Relative(11), rhs: Relative(2) }, Cast { destination: Relative(17), source: Relative(16), bit_size: Integer(U64) }, Cast { destination: Relative(18), source: Relative(17), bit_size: Integer(U32) }, Cast { destination: Relative(16), source: Relative(18), bit_size: Integer(U64) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(15), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(15) }, Not { destination: Relative(18), source: Relative(17), bit_size: U1 }, Cast { destination: Relative(19), source: Relative(11), bit_size: Field }, Cast { destination: Relative(11), source: Relative(17), bit_size: Field }, BinaryFieldOp { destination: Relative(17), op: Mul, lhs: Relative(11), rhs: Relative(19) }, BinaryFieldOp { destination: Relative(11), op: Sub, lhs: Relative(9), rhs: Relative(19) }, Cast { destination: Relative(19), source: Relative(18), bit_size: Field }, BinaryFieldOp { destination: Relative(18), op: Mul, lhs: Relative(19), rhs: Relative(11) }, BinaryFieldOp { destination: Relative(11), op: Add, lhs: Relative(17), rhs: Relative(18) }, Not { destination: Relative(17), source: Relative(15), bit_size: U1 }, Cast { destination: Relative(18), source: Relative(15), bit_size: Field }, BinaryFieldOp { destination: Relative(15), op: Mul, lhs: Relative(18), rhs: Relative(8) }, Cast { destination: Relative(18), source: Relative(17), bit_size: Field }, BinaryFieldOp { destination: Relative(17), op: Mul, lhs: Relative(18), rhs: Relative(10) }, BinaryFieldOp { destination: Relative(18), op: Add, lhs: Relative(15), rhs: Relative(17) }, BinaryFieldOp { destination: Relative(15), op: Mul, lhs: Relative(11), rhs: Relative(18) }, Cast { destination: Relative(11), source: Relative(15), bit_size: Field }, Const { destination: Relative(17), bit_size: Field, value: 4294967295 }, BinaryFieldOp { destination: Relative(18), op: LessThanEquals, lhs: Relative(11), rhs: Relative(17) }, JumpIf { condition: Relative(18), location: 653 }, Call { location: 710 }, Cast { destination: Relative(11), source: Relative(15), bit_size: Integer(U32) }, Not { destination: Relative(15), source: Relative(14), bit_size: U1 }, Cast { destination: Relative(14), source: Relative(15), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Direct(32842), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(11), rhs: Relative(15) }, JumpIf { condition: Relative(14), location: 660 }, Call { location: 707 }, Cast { destination: Relative(11), source: Relative(16), bit_size: Integer(U32) }, Mov { destination: Relative(7), source: Relative(11) }, Jump { location: 663 }, Load { destination: Relative(11), source_pointer: Relative(6) }, Mov { destination: Direct(32771), source: Relative(11) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 4 }, Call { location: 713 }, Mov { destination: Relative(14), source: Direct(32773) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(14), rhs: Direct(2) }, BinaryIntOp { destination: Relative(16), op: Add, bit_size: U32, lhs: Relative(15), rhs: Relative(4) }, Store { destination_pointer: Relative(16), source: Relative(7) }, Store { destination_pointer: Relative(6), source: Relative(14) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32838) }, Mov { destination: Relative(4), source: Relative(7) }, Jump { location: 546 }, Call { location: 32 }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32837) }, Load { destination: Relative(5), source_pointer: Relative(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 685 }, Call { location: 510 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(5) }, Mov { destination: Relative(3), source: Direct(32836) }, Jump { location: 689 }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(3), rhs: Direct(32839) }, JumpIf { condition: Relative(5), location: 694 }, Jump { location: 692 }, Load { destination: Relative(1), source_pointer: Relative(4) }, Return, Load { destination: Relative(5), source_pointer: Relative(4) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(3) }, Load { destination: Relative(6), source_pointer: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, Load { destination: Relative(7), source_pointer: Relative(9) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(7) }, BinaryIntOp { destination: Relative(6), op: Mul, bit_size: U1, lhs: Relative(5), rhs: Relative(8) }, Store { destination_pointer: Relative(4), source: Relative(6) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(3), rhs: Direct(32838) }, Mov { destination: Relative(3), source: Relative(5) }, Jump { location: 689 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 7233212735005103307 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 717 }, Jump { location: 719 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 734 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 731 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 724 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 734 }, Return]" + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32845 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32843), size_address: Relative(2), offset_address: Relative(3) }, Mov { destination: Relative(1), source: Direct(32843) }, Call { location: 13 }, Call { location: 22 }, Mov { destination: Direct(32844), source: Relative(1) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32844 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 1 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Const { destination: Direct(32835), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32836), bit_size: Integer(U32), value: 0 }, Const { destination: Direct(32837), bit_size: Integer(U1), value: 1 }, Const { destination: Direct(32838), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(32839), bit_size: Integer(U32), value: 3 }, Const { destination: Direct(32840), bit_size: Field, value: 17 }, Const { destination: Direct(32841), bit_size: Field, value: 33 }, Const { destination: Direct(32842), bit_size: Integer(U32), value: 2147483648 }, Return, Call { location: 32 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 3 }, Mov { destination: Relative(3), source: Direct(0) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(2) }, Call { location: 38 }, Mov { destination: Direct(0), source: Relative(0) }, Const { destination: Relative(2), bit_size: Field, value: 5 }, BinaryFieldOp { destination: Relative(3), op: Add, lhs: Relative(1), rhs: Relative(2) }, Mov { destination: Relative(1), source: Relative(3) }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 37 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 32 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 1 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 3 }, Mov { destination: Relative(5), source: Direct(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(6) }, IndirectConst { destination_pointer: Relative(5), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Mov { destination: Relative(7), source: Relative(6) }, Store { destination_pointer: Relative(7), source: Relative(2) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(3) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Relative(4) }, Load { destination: Relative(4), source_pointer: Relative(5) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(4) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 59 }, Call { location: 510 }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(4) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 8 }, Mov { destination: Relative(8), source: Direct(0) }, Mov { destination: Relative(9), source: Relative(5) }, Mov { destination: Relative(10), source: Relative(3) }, Mov { destination: Relative(11), source: Direct(32840) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 513 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(4), source: Relative(9) }, Load { destination: Relative(7), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(7) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 76 }, Call { location: 510 }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(7) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32838) }, Load { destination: Relative(7), source_pointer: Relative(9) }, Const { destination: Relative(12), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(12) }, BinaryIntOp { destination: Relative(11), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(12) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Relative(11) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 2 }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(4), rhs: Relative(10) }, Load { destination: Relative(11), source_pointer: Relative(12) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(12), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(14) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(11), rhs: Relative(14) }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(12), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32839) }, Load { destination: Relative(12), source_pointer: Relative(13) }, Const { destination: Relative(16), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(14), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(16) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(12), rhs: Relative(16) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(15) }, JumpIf { condition: Relative(9), location: 100 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, JumpIf { condition: Relative(10), location: 103 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, JumpIf { condition: Relative(13), location: 106 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(2) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 112 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32837) }, Load { destination: Relative(10), source_pointer: Relative(4) }, Const { destination: Relative(13), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(14), op: Equals, bit_size: U32, lhs: Relative(13), rhs: Relative(10) }, Not { destination: Relative(14), source: Relative(14), bit_size: U1 }, JumpIf { condition: Relative(14), location: 123 }, Call { location: 510 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(10) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 127 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 496 }, Jump { location: 130 }, Load { destination: Relative(6), source_pointer: Relative(2) }, JumpIf { condition: Relative(6), location: 134 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(2) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 140 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Direct(32835) }, Load { destination: Relative(8), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 151 }, Call { location: 510 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(8) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 474 }, Jump { location: 158 }, Load { destination: Relative(6), source_pointer: Relative(2) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 12 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, JumpIf { condition: Relative(8), location: 164 }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(9) } }, Load { destination: Relative(6), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 170 }, Call { location: 510 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(6) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Direct(32835) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(13), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(13), source: Relative(13), bit_size: U1 }, JumpIf { condition: Relative(13), location: 181 }, Call { location: 510 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Mov { destination: Relative(1), source: Direct(32836) }, Jump { location: 185 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(8), location: 443 }, Jump { location: 188 }, Load { destination: Relative(8), source_pointer: Relative(6) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 18 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, JumpIf { condition: Relative(9), location: 194 }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(10) } }, Load { destination: Relative(6), source_pointer: Relative(4) }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Not { destination: Relative(9), source: Relative(9), bit_size: U1 }, JumpIf { condition: Relative(9), location: 200 }, Call { location: 510 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(6), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(6) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Relative(7) }, Load { destination: Relative(9), source_pointer: Relative(4) }, Const { destination: Relative(10), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(13), op: Equals, bit_size: U32, lhs: Relative(10), rhs: Relative(9) }, Not { destination: Relative(13), source: Relative(13), bit_size: U1 }, JumpIf { condition: Relative(13), location: 211 }, Call { location: 510 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(9), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(9) }, Mov { destination: Relative(1), source: Direct(32838) }, Jump { location: 215 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(8), location: 421 }, Jump { location: 218 }, Load { destination: Relative(8), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(8), rhs: Relative(2) }, JumpIf { condition: Relative(6), location: 223 }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(9) } }, Load { destination: Relative(2), source_pointer: Relative(4) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(2) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 229 }, Call { location: 510 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(2) }, Mov { destination: Relative(2), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(2), source: Relative(7) }, Load { destination: Relative(8), source_pointer: Relative(4) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Not { destination: Relative(10), source: Relative(10), bit_size: U1 }, JumpIf { condition: Relative(10), location: 240 }, Call { location: 510 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(8) }, Mov { destination: Relative(1), source: Direct(32838) }, Jump { location: 244 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(1), rhs: Direct(32839) }, JumpIf { condition: Relative(6), location: 390 }, Jump { location: 247 }, Load { destination: Relative(1), source_pointer: Relative(2) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 16 }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, JumpIf { condition: Relative(6), location: 253 }, Const { destination: Relative(8), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(8) } }, Load { destination: Relative(1), source_pointer: Relative(4) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U32, lhs: Relative(2), rhs: Relative(1) }, Not { destination: Relative(6), source: Relative(6), bit_size: U1 }, JumpIf { condition: Relative(6), location: 259 }, Call { location: 510 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Relative(1) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(7) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(7) }, JumpIf { condition: Relative(6), location: 267 }, Jump { location: 269 }, Mov { destination: Relative(8), source: Relative(16) }, Jump { location: 271 }, Mov { destination: Relative(8), source: Relative(7) }, Jump { location: 271 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(9), location: 277 }, Jump { location: 279 }, Mov { destination: Relative(10), source: Relative(16) }, Jump { location: 281 }, Mov { destination: Relative(10), source: Relative(3) }, Jump { location: 281 }, BinaryIntOp { destination: Relative(1), op: Div, bit_size: U32, lhs: Relative(8), rhs: Relative(10) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(6), rhs: Relative(9) }, JumpIf { condition: Relative(13), location: 285 }, Jump { location: 287 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(1), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(1) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(7), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(11) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(11) }, JumpIf { condition: Relative(7), location: 293 }, Jump { location: 295 }, Mov { destination: Relative(8), source: Relative(16) }, Jump { location: 297 }, Mov { destination: Relative(8), source: Relative(11) }, Jump { location: 297 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(9), location: 303 }, Jump { location: 305 }, Mov { destination: Relative(10), source: Relative(16) }, Jump { location: 307 }, Mov { destination: Relative(10), source: Relative(3) }, Jump { location: 307 }, BinaryIntOp { destination: Relative(6), op: Div, bit_size: U32, lhs: Relative(8), rhs: Relative(10) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(7), rhs: Relative(9) }, JumpIf { condition: Relative(13), location: 311 }, Jump { location: 313 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(6), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(6) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(12) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(12) }, JumpIf { condition: Relative(8), location: 319 }, Jump { location: 321 }, Mov { destination: Relative(9), source: Relative(16) }, Jump { location: 323 }, Mov { destination: Relative(9), source: Relative(12) }, Jump { location: 323 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Relative(3) }, Const { destination: Relative(15), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(15), rhs: Relative(3) }, JumpIf { condition: Relative(10), location: 329 }, Jump { location: 331 }, Mov { destination: Relative(11), source: Relative(16) }, Jump { location: 333 }, Mov { destination: Relative(11), source: Relative(3) }, Jump { location: 333 }, BinaryIntOp { destination: Relative(7), op: Div, bit_size: U32, lhs: Relative(9), rhs: Relative(11) }, BinaryIntOp { destination: Relative(13), op: Xor, bit_size: U1, lhs: Relative(8), rhs: Relative(10) }, JumpIf { condition: Relative(13), location: 337 }, Jump { location: 339 }, Const { destination: Relative(14), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Sub, bit_size: U32, lhs: Relative(14), rhs: Relative(7) }, Mov { destination: Relative(8), source: Direct(1) }, Const { destination: Relative(9), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(9) }, IndirectConst { destination_pointer: Relative(8), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Direct(2) }, Mov { destination: Relative(10), source: Relative(9) }, Store { destination_pointer: Relative(10), source: Relative(1) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(10), source: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(10), rhs: Direct(2) }, Store { destination_pointer: Relative(10), source: Relative(7) }, Load { destination: Relative(1), source_pointer: Relative(5) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(1) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 356 }, Call { location: 510 }, BinaryIntOp { destination: Relative(1), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(1) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 9 }, Mov { destination: Relative(9), source: Direct(0) }, Mov { destination: Relative(10), source: Relative(8) }, Mov { destination: Relative(11), source: Relative(5) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 674 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(1), source: Relative(10) }, JumpIf { condition: Relative(1), location: 369 }, Const { destination: Relative(7), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(7) } }, Const { destination: Relative(7), bit_size: Integer(U32), value: 8 }, Mov { destination: Relative(8), source: Direct(0) }, Mov { destination: Relative(9), source: Relative(4) }, Mov { destination: Relative(10), source: Relative(3) }, Mov { destination: Relative(11), source: Direct(32841) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(7) }, Call { location: 513 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(1), source: Relative(9) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 7 }, Mov { destination: Relative(7), source: Direct(0) }, Mov { destination: Relative(8), source: Relative(1) }, Mov { destination: Relative(9), source: Relative(5) }, BinaryIntOp { destination: Direct(0), op: Add, bit_size: U32, lhs: Direct(0), rhs: Relative(4) }, Call { location: 674 }, Mov { destination: Direct(0), source: Relative(0) }, Mov { destination: Relative(3), source: Relative(8) }, JumpIf { condition: Relative(3), location: 389 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(1) } }, Return, Load { destination: Relative(6), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(10) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(6), rhs: Relative(8) }, Mov { destination: Relative(10), source: Relative(9) }, Cast { destination: Relative(9), source: Relative(10), bit_size: Integer(U32) }, Cast { destination: Relative(13), source: Relative(6), bit_size: Integer(U32) }, Cast { destination: Relative(6), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(13), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(13), rhs: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Mul, bit_size: U1, lhs: Relative(9), rhs: Relative(6) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(6) }, JumpIf { condition: Relative(9), location: 408 }, Call { location: 706 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(10), rhs: Relative(3) }, Mov { destination: Relative(8), source: Relative(6) }, Cast { destination: Relative(6), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(6), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(13) }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(6), rhs: Relative(13) }, BinaryIntOp { destination: Relative(6), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(13) }, JumpIf { condition: Relative(6), location: 417 }, Call { location: 706 }, Store { destination_pointer: Relative(2), source: Relative(8) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 244 }, Load { destination: Relative(8), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(10), rhs: Relative(1) }, Load { destination: Relative(9), source_pointer: Relative(13) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(9) }, Mov { destination: Relative(13), source: Relative(10) }, Cast { destination: Relative(10), source: Relative(13), bit_size: Integer(U32) }, Cast { destination: Relative(14), source: Relative(8), bit_size: Integer(U32) }, Cast { destination: Relative(8), source: Relative(9), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(14), rhs: Relative(9) }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(10), rhs: Relative(8) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(9), rhs: Relative(8) }, JumpIf { condition: Relative(10), location: 439 }, Call { location: 706 }, Store { destination_pointer: Relative(6), source: Relative(13) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(8) }, Jump { location: 215 }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(1) }, Load { destination: Relative(8), source_pointer: Relative(10) }, Load { destination: Relative(9), source_pointer: Relative(6) }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(9), rhs: Relative(8) }, Mov { destination: Relative(13), source: Relative(10) }, Cast { destination: Relative(10), source: Relative(13), bit_size: Integer(U32) }, Cast { destination: Relative(14), source: Relative(9), bit_size: Integer(U32) }, Cast { destination: Relative(9), source: Relative(8), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(8), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(14), rhs: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Mul, bit_size: U1, lhs: Relative(10), rhs: Relative(9) }, BinaryIntOp { destination: Relative(10), op: Equals, bit_size: U1, lhs: Relative(8), rhs: Relative(9) }, JumpIf { condition: Relative(10), location: 461 }, Call { location: 706 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(13), rhs: Relative(3) }, Mov { destination: Relative(9), source: Relative(8) }, Cast { destination: Relative(8), source: Relative(9), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(10), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(10), rhs: Relative(14) }, BinaryIntOp { destination: Relative(10), op: Mul, bit_size: U1, lhs: Relative(8), rhs: Relative(14) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(10), rhs: Relative(14) }, JumpIf { condition: Relative(8), location: 470 }, Call { location: 706 }, Store { destination_pointer: Relative(6), source: Relative(9) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(8) }, Jump { location: 185 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(1) }, Load { destination: Relative(6), source_pointer: Relative(9) }, Load { destination: Relative(8), source_pointer: Relative(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(6) }, Mov { destination: Relative(10), source: Relative(9) }, Cast { destination: Relative(9), source: Relative(10), bit_size: Integer(U32) }, Cast { destination: Relative(13), source: Relative(8), bit_size: Integer(U32) }, Cast { destination: Relative(8), source: Relative(6), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(6), op: LessThan, bit_size: U32, lhs: Relative(13), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(8), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U1, lhs: Relative(6), rhs: Relative(13) }, BinaryIntOp { destination: Relative(13), op: LessThan, bit_size: U32, lhs: Relative(9), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(13), rhs: Relative(6) }, BinaryIntOp { destination: Relative(6), op: Mul, bit_size: U1, lhs: Relative(9), rhs: Relative(8) }, BinaryIntOp { destination: Relative(9), op: Equals, bit_size: U1, lhs: Relative(6), rhs: Relative(8) }, JumpIf { condition: Relative(9), location: 492 }, Call { location: 706 }, Store { destination_pointer: Relative(2), source: Relative(10) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 155 }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(1) }, Load { destination: Relative(6), source_pointer: Relative(9) }, Load { destination: Relative(8), source_pointer: Relative(2) }, Const { destination: Relative(14), bit_size: Integer(U32), value: 2147483648 }, BinaryIntOp { destination: Relative(10), op: Add, bit_size: U32, lhs: Relative(6), rhs: Relative(14) }, BinaryIntOp { destination: Relative(13), op: Add, bit_size: U32, lhs: Relative(3), rhs: Relative(14) }, BinaryIntOp { destination: Relative(9), op: LessThan, bit_size: U32, lhs: Relative(10), rhs: Relative(13) }, Not { destination: Relative(6), source: Relative(9), bit_size: U1 }, BinaryIntOp { destination: Relative(9), op: Mul, bit_size: U1, lhs: Relative(8), rhs: Relative(6) }, Store { destination_pointer: Relative(2), source: Relative(9) }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(32838) }, Mov { destination: Relative(1), source: Relative(6) }, Jump { location: 127 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 12049594436772143978 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Call { location: 32 }, Mov { destination: Relative(5), source: Direct(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 4 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(6) }, IndirectConst { destination_pointer: Relative(5), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(6), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Mov { destination: Relative(7), source: Relative(6) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(7), rhs: Direct(2) }, Store { destination_pointer: Relative(7), source: Direct(32835) }, Mov { destination: Relative(6), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(6), source: Relative(5) }, Load { destination: Relative(5), source_pointer: Relative(1) }, Const { destination: Relative(7), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(7), rhs: Relative(5) }, Not { destination: Relative(8), source: Relative(8), bit_size: U1 }, JumpIf { condition: Relative(8), location: 534 }, Call { location: 510 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(5) }, BinaryFieldOp { destination: Relative(5), op: Equals, lhs: Relative(3), rhs: Direct(32840) }, Cast { destination: Relative(8), source: Relative(2), bit_size: Field }, Const { destination: Relative(9), bit_size: Field, value: 4294967296 }, BinaryFieldOp { destination: Relative(10), op: Sub, lhs: Relative(9), rhs: Relative(8) }, Const { destination: Relative(11), bit_size: Field, value: 23 }, BinaryFieldOp { destination: Relative(12), op: Equals, lhs: Relative(3), rhs: Relative(11) }, Const { destination: Relative(11), bit_size: Field, value: 27 }, BinaryFieldOp { destination: Relative(13), op: Equals, lhs: Relative(3), rhs: Relative(11) }, Mov { destination: Relative(4), source: Direct(32836) }, Jump { location: 546 }, BinaryIntOp { destination: Relative(7), op: LessThan, bit_size: U32, lhs: Relative(4), rhs: Direct(32839) }, JumpIf { condition: Relative(7), location: 551 }, Jump { location: 549 }, Load { destination: Relative(1), source_pointer: Relative(6) }, Return, BinaryIntOp { destination: Relative(14), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(14), rhs: Relative(4) }, Load { destination: Relative(11), source_pointer: Relative(15) }, Cast { destination: Relative(14), source: Relative(2), bit_size: Integer(U32) }, Cast { destination: Relative(15), source: Relative(11), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(16), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(18), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(19), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, JumpIf { condition: Relative(5), location: 625 }, Jump { location: 562 }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(2), rhs: Relative(11) }, Mov { destination: Relative(20), source: Relative(15) }, JumpIf { condition: Relative(12), location: 612 }, Jump { location: 566 }, JumpIf { condition: Relative(13), location: 600 }, Jump { location: 568 }, BinaryFieldOp { destination: Relative(16), op: Equals, lhs: Relative(3), rhs: Direct(32841) }, JumpIf { condition: Relative(16), location: 572 }, Const { destination: Relative(17), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(17) } }, Const { destination: Relative(22), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(22), rhs: Relative(11) }, Const { destination: Relative(23), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(24), op: Sub, bit_size: U32, lhs: Relative(23), rhs: Relative(11) }, JumpIf { condition: Relative(17), location: 578 }, Jump { location: 580 }, Mov { destination: Relative(18), source: Relative(24) }, Jump { location: 582 }, Mov { destination: Relative(18), source: Relative(11) }, Jump { location: 582 }, Const { destination: Relative(22), bit_size: Integer(U32), value: 2147483647 }, BinaryIntOp { destination: Relative(19), op: LessThan, bit_size: U32, lhs: Relative(22), rhs: Relative(2) }, Const { destination: Relative(23), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(24), op: Sub, bit_size: U32, lhs: Relative(23), rhs: Relative(2) }, JumpIf { condition: Relative(19), location: 588 }, Jump { location: 590 }, Mov { destination: Relative(20), source: Relative(24) }, Jump { location: 592 }, Mov { destination: Relative(20), source: Relative(2) }, Jump { location: 592 }, BinaryIntOp { destination: Relative(16), op: Div, bit_size: U32, lhs: Relative(18), rhs: Relative(20) }, BinaryIntOp { destination: Relative(21), op: Xor, bit_size: U1, lhs: Relative(17), rhs: Relative(19) }, JumpIf { condition: Relative(21), location: 596 }, Jump { location: 598 }, Const { destination: Relative(22), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(16), op: Sub, bit_size: U32, lhs: Relative(22), rhs: Relative(16) }, Mov { destination: Relative(15), source: Relative(16) }, Jump { location: 610 }, Cast { destination: Relative(11), source: Relative(20), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(16), op: Equals, bit_size: U1, lhs: Relative(18), rhs: Relative(19) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(11), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(18) }, BinaryIntOp { destination: Relative(17), op: Mul, bit_size: U1, lhs: Relative(11), rhs: Relative(16) }, BinaryIntOp { destination: Relative(11), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(16) }, JumpIf { condition: Relative(11), location: 608 }, Call { location: 706 }, Mov { destination: Relative(15), source: Relative(20) }, Jump { location: 610 }, Mov { destination: Relative(14), source: Relative(15) }, Jump { location: 623 }, Mov { destination: Relative(11), source: Relative(15) }, Cast { destination: Relative(15), source: Relative(11), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(18), op: Equals, bit_size: U1, lhs: Relative(16), rhs: Relative(17) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(15), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(16) }, BinaryIntOp { destination: Relative(16), op: Mul, bit_size: U1, lhs: Relative(15), rhs: Relative(18) }, BinaryIntOp { destination: Relative(15), op: Equals, bit_size: U1, lhs: Relative(16), rhs: Relative(18) }, JumpIf { condition: Relative(15), location: 621 }, Call { location: 706 }, Mov { destination: Relative(14), source: Relative(11) }, Jump { location: 623 }, Mov { destination: Relative(7), source: Relative(14) }, Jump { location: 662 }, BinaryIntOp { destination: Relative(16), op: Mul, bit_size: U32, lhs: Relative(11), rhs: Relative(2) }, Cast { destination: Relative(17), source: Relative(16), bit_size: Integer(U64) }, Cast { destination: Relative(18), source: Relative(17), bit_size: Integer(U32) }, Cast { destination: Relative(16), source: Relative(18), bit_size: Integer(U64) }, BinaryIntOp { destination: Relative(17), op: LessThan, bit_size: U32, lhs: Relative(15), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(15), op: LessThan, bit_size: U32, lhs: Relative(14), rhs: Direct(32842) }, BinaryIntOp { destination: Relative(14), op: Equals, bit_size: U1, lhs: Relative(17), rhs: Relative(15) }, Not { destination: Relative(18), source: Relative(17), bit_size: U1 }, Cast { destination: Relative(19), source: Relative(11), bit_size: Field }, Cast { destination: Relative(11), source: Relative(17), bit_size: Field }, BinaryFieldOp { destination: Relative(17), op: Mul, lhs: Relative(11), rhs: Relative(19) }, BinaryFieldOp { destination: Relative(11), op: Sub, lhs: Relative(9), rhs: Relative(19) }, Cast { destination: Relative(19), source: Relative(18), bit_size: Field }, BinaryFieldOp { destination: Relative(18), op: Mul, lhs: Relative(19), rhs: Relative(11) }, BinaryFieldOp { destination: Relative(11), op: Add, lhs: Relative(17), rhs: Relative(18) }, Not { destination: Relative(17), source: Relative(15), bit_size: U1 }, Cast { destination: Relative(18), source: Relative(15), bit_size: Field }, BinaryFieldOp { destination: Relative(15), op: Mul, lhs: Relative(18), rhs: Relative(8) }, Cast { destination: Relative(18), source: Relative(17), bit_size: Field }, BinaryFieldOp { destination: Relative(17), op: Mul, lhs: Relative(18), rhs: Relative(10) }, BinaryFieldOp { destination: Relative(18), op: Add, lhs: Relative(15), rhs: Relative(17) }, BinaryFieldOp { destination: Relative(15), op: Mul, lhs: Relative(11), rhs: Relative(18) }, Cast { destination: Relative(11), source: Relative(15), bit_size: Field }, Const { destination: Relative(17), bit_size: Field, value: 4294967295 }, BinaryFieldOp { destination: Relative(18), op: LessThanEquals, lhs: Relative(11), rhs: Relative(17) }, JumpIf { condition: Relative(18), location: 652 }, Call { location: 709 }, Cast { destination: Relative(11), source: Relative(15), bit_size: Integer(U32) }, Not { destination: Relative(15), source: Relative(14), bit_size: U1 }, Cast { destination: Relative(14), source: Relative(15), bit_size: Integer(U32) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Direct(32842), rhs: Relative(14) }, BinaryIntOp { destination: Relative(14), op: LessThan, bit_size: U32, lhs: Relative(11), rhs: Relative(15) }, JumpIf { condition: Relative(14), location: 659 }, Call { location: 706 }, Cast { destination: Relative(11), source: Relative(16), bit_size: Integer(U32) }, Mov { destination: Relative(7), source: Relative(11) }, Jump { location: 662 }, Load { destination: Relative(11), source_pointer: Relative(6) }, Mov { destination: Direct(32771), source: Relative(11) }, Const { destination: Direct(32772), bit_size: Integer(U32), value: 4 }, Call { location: 712 }, Mov { destination: Relative(14), source: Direct(32773) }, BinaryIntOp { destination: Relative(15), op: Add, bit_size: U32, lhs: Relative(14), rhs: Direct(2) }, BinaryIntOp { destination: Relative(16), op: Add, bit_size: U32, lhs: Relative(15), rhs: Relative(4) }, Store { destination_pointer: Relative(16), source: Relative(7) }, Store { destination_pointer: Relative(6), source: Relative(14) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(4), rhs: Direct(32838) }, Mov { destination: Relative(4), source: Relative(7) }, Jump { location: 546 }, Call { location: 32 }, Mov { destination: Relative(4), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(2) }, Store { destination_pointer: Relative(4), source: Direct(32837) }, Load { destination: Relative(5), source_pointer: Relative(1) }, Const { destination: Relative(6), bit_size: Integer(U32), value: 0 }, BinaryIntOp { destination: Relative(7), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(5) }, Not { destination: Relative(7), source: Relative(7), bit_size: U1 }, JumpIf { condition: Relative(7), location: 684 }, Call { location: 510 }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(1), source: Relative(5) }, Mov { destination: Relative(3), source: Direct(32836) }, Jump { location: 688 }, BinaryIntOp { destination: Relative(5), op: LessThan, bit_size: U32, lhs: Relative(3), rhs: Direct(32839) }, JumpIf { condition: Relative(5), location: 693 }, Jump { location: 691 }, Load { destination: Relative(1), source_pointer: Relative(4) }, Return, Load { destination: Relative(5), source_pointer: Relative(4) }, BinaryIntOp { destination: Relative(7), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(7), rhs: Relative(3) }, Load { destination: Relative(6), source_pointer: Relative(8) }, BinaryIntOp { destination: Relative(8), op: Add, bit_size: U32, lhs: Relative(2), rhs: Direct(2) }, BinaryIntOp { destination: Relative(9), op: Add, bit_size: U32, lhs: Relative(8), rhs: Relative(3) }, Load { destination: Relative(7), source_pointer: Relative(9) }, BinaryIntOp { destination: Relative(8), op: Equals, bit_size: U32, lhs: Relative(6), rhs: Relative(7) }, BinaryIntOp { destination: Relative(6), op: Mul, bit_size: U1, lhs: Relative(5), rhs: Relative(8) }, Store { destination_pointer: Relative(4), source: Relative(6) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(3), rhs: Direct(32838) }, Mov { destination: Relative(3), source: Relative(5) }, Jump { location: 688 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 5019202896831570965 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 7233212735005103307 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return, Load { destination: Direct(32774), source_pointer: Direct(32771) }, BinaryIntOp { destination: Direct(32775), op: Equals, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, JumpIf { condition: Direct(32775), location: 716 }, Jump { location: 718 }, Mov { destination: Direct(32773), source: Direct(32771) }, Jump { location: 733 }, Mov { destination: Direct(32773), source: Direct(1) }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Direct(32772) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32772) }, Mov { destination: Direct(32778), source: Direct(32771) }, Mov { destination: Direct(32779), source: Direct(32773) }, BinaryIntOp { destination: Direct(32780), op: Equals, bit_size: U32, lhs: Direct(32778), rhs: Direct(32777) }, JumpIf { condition: Direct(32780), location: 730 }, Load { destination: Direct(32776), source_pointer: Direct(32778) }, Store { destination_pointer: Direct(32779), source: Direct(32776) }, BinaryIntOp { destination: Direct(32778), op: Add, bit_size: U32, lhs: Direct(32778), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32779), op: Add, bit_size: U32, lhs: Direct(32779), rhs: Direct(2) }, Jump { location: 723 }, IndirectConst { destination_pointer: Direct(32773), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Direct(32774), op: Sub, bit_size: U32, lhs: Direct(32774), rhs: Direct(2) }, Jump { location: 733 }, Return]" ], - "debug_symbols": "pdrfbtu4EoDxd/F1LkTO8M/0VRZFkbbpIkCQFtnkAAdF3v1wOPrsHGCl9co35S9N/MGmSVlS8vv0/eHr259fHp9//Pzr9OmP36evL49PT49/fnn6+e3+9fHn8/jf36fF/8ly+pTuTlljKDHUGFoM/fQpj8HmIGOQu5MuMaQYcgwSg8ZQYqgxtBh6DFEpUSlRKVEpo6Jj0BhKDDWGFkOPweZQlxhGpYwhxyAxaAwlhhpDi6HHYHNoo9LGkGLIMUgMGkOJocbQYugx2Bz6qNgYUgw5BokhKj0qPSq9xfd6DDYHi4rFc7F4LhYV0xhKDDUGf6uWMfZ1tBjTsoAEMhCgoIAKGuiAcqKcKCfKiXLycnYUUIGXxdGBrcgLSCADAQroiD9KHf6o4shAgIICKmigA1vhaz1A2dd7ag4BCrzcHRU00IGt8PUfSCADOr7Wkzl8A/qs+noPJJCBAAUFVNBAB5Qb5UbZ90H298t3QkCBl33mfT8EGujAVvi+CCSQAR3fA9nfQd8F2efZ90EggQwEKCigggY6WMt5WUACXm4OAQq83B0VNNCBrfB9EUggAzq+5rM5/Dg45jn7mg8kkIEABQVU0EAHlIWy7wvxA7nvi4AAL4ujgAoa6MBWzM+AiQTozKO9OvxRPr3ziO+Yx/yJBDLw5+MT7sf+QAEVNNCBrfB9EfCyT6/vi4AABQVU0EAHtsL3RYByo9woN8qNcqPcKDfKjXKn3Cl3yp1yp9wpd8qdcqfcKRtlo2yUjbJRNspG2SgbZVvLsiwggQwEKCigggY6oJwoJ8qJcqKcKCfKiXKinCgnyplyppwpZ8qZcqacKWfKmXKmLJSFslAWykJZKAtloSyUfX/p2LDi+yuQQAYCFBRQQQMdUJ57sDoSyECAggIqaMDLyWEr5rnYRAIZCFBQQAUNUPY9qH7e6XswkEAGAhQUUEEDXhaHrfA9GEggAwEKCqigAcq2HhjFt56qIwMBHiyOAipooAMLqG+9QAIZCFBQQAUNeLk6bIVvvUACGQhQUEAF6wFf59Zz5PXjT32jaXMIUOCd+TMVNNCBrfCNFkggAwEKKAtloSyUfaP5p7D6RgtInIqob6tAARV4Z/5wB7bCt1UggQwEKCigAsqFcqFcKc+LnMWRgQAFBVTQQAe2Yp7y+fs1T/kmNM48tRVQQQPeSQ5b4ZsokEAGAhQUUEEDlDtlo2yUfTf5KbH6bgrUuApQa6CD9bKi+N4pEwlkIEBBiauJMi+FJtoK3wVlooAKGujAVvgHUCCBDARQzpQz5UzZ90UpDlvh+yKQQAYCFBTg5erwcnN0YCt8X8yf8Q+gQAYC6hr0XVC6X7D7D5sjAwHjadTFUUAFDYynUZPDVlSe6lzGEx3YCl/GNTsSyECAggIqaKADW9Epd8qdcqfcKXfKnXKn3Cl3ykbZKBtlo2yUjbIxP7bOT/WFHUggAwEKCqigrfADvr8F1Q/4AQUFVNBAB7bCl3oggRorquYGOlgXW53LeCKBDAQoKIAyy7iyjCvLuCplpayUlbJSVspKWSkrZaVcKBfKhXKhXCgXyoVyoVwoF8qVcl03Wp2H9+4ooIIGOlg3Y/V9EUggAx7eeHjj4XPxi98hW0ACGQhQUEAFdObC9nt0xg/P9TzvvlXQgK8of8lzhVe/NbcAfxrdsQbbXOETXtb397sTdzm/vL48PPhNzg+3PcfN0F/3Lw/Pr6dPz29PT3en/9w/vc0f+uvX/fMcX+9fxnfHsn14/j7GEfzx+PTger+7PHrZfqj5uc18sFU7P7xc//hWeHzPBx4/bqnVNTDYjhSy8BLSuJo+VLBEYVweHimIXgpl2SrU7cI4DdO1MNjPBbk+oMs5UMpGoO+9hlLOr6Eut87CsXdC2uU5dN0qpLSX6HZOWDuSuO692C/4JdU/vRnXToT1I1OpiacwbgHL5jzUvYToOSF2JDGu8FmVg7o1lbsF43WMOwByYCo/ToTWQ1N5fhHjBvnmzsh7q3JcapwTlo8krpvK/cLNU/lhIuzIx8X45cH5zRhXDpvzsLcgrHO8H/caNudh71C5JD5zxi2JeqhwPlaO+xT9wEx+nActh2aynI9z40JiqyDp1pncK1w3k/uFm2fywzzYkROI8XsRnsL4zcjmh47sHSlTPb+d47dPW/OwW7DLWczStgp972UYEzF+r3PwZfR+fhmbC+LKqRxzcuTNSLKcC2K3Fqoc2RhjJS6XRbn5ZujeqmyXVbl9InN1wpYjh8rrXoffTdx+EnI5TsnmeUjWKz/6er05sf3pmez2c5l06xl2yTefm+4d7cYxksJiRw6XVx2pllsPVGXvKDN+q3aehaTHLhpvO06NR9nlRdQjL+K69bSbuG5v7SVyUj0f68qhKy7r59fx8RygXH/IX84fO8vHibg+kC6BD+cQ/yJwOeAvH+fx2DPYegm17Z6E9L89GXt//zy+uP/2+PJ/f8/27qmXx/uvTw/rlz/enr99+O7rf3/xHf4e7tfLz28P399eHrx0+aO48c8fbZxMNtHP/idP40vJd9L8izS/t9y1lD+/+1P5Hw==", + "debug_symbols": "pdrfbtu4EoDxd/F1LkTO8M/0VRZFkbbpIkCQFtnkAAdF3v1wOPrsHGCl9co35S+b+FuLJmXJye/T94evb39+eXz+8fOv06c/fp++vjw+PT3++eXp57f718efz+O//j4t/k+W06d0d8oaQ4mhxtBi6KdPeQw2BxmD3J10iSHFkGOQGDSGEkONocXQY4hKiUqJSolKGRUdg8ZQYqgxtBh6DDaHusQwKmUMOQaJQWMoMdQYWgw9BptDG5U2hhRDjkFi0BhKDDWGFkOPwebQR8XGkGLIMUgMUelR6VHpLb7XY7A5WFQsnovFc7GomMZQYqgx+Eu1jLGvo8WYlgUkkIEABQVU0EAHlBPlRDlRTpSTl7OjgAq8LI4ObEVeQAIZCFBAR/xR6vBHFUcGAhQUUEEDHdgKX+sByr7eU3MIUODl7qiggQ5sha//QAIZ0PG1nszhG9Bn1dd7IIEMBCgooIIGOqDcKDfKvg+yv16+EwIKvOwz7/sh0EAHtsL3RSCBDOj4Hsj+CvouyD7Pvg8CCWQgQEEBFTTQwVrOywIS8HJzCFDg5e6ooIEObIXvi0ACGdDxNZ/N4efBMc/Z13wggQwEKCigggY6oCyUfV+In8h9XwQEeFkcBVTQQAe2Yr4HTCRAZ57t1eGP8umdZ3zHPOdPJJCBPx+fcD/3BwqooIEObIXvi4CXfXp9XwQEKCigggY6sBW+LwKUG+VGuVFulBvlRrlRbpQ75U65U+6UO+VOuVPulDvlTtkoG2WjbJSNslE2ykbZKNtalmUBCWQgQEEBFTTQAeVEOVFOlBPlRDlRTpQT5UQ5Uc6UM+VMOVPOlDPlTDlTzpQzZaEslIWyUBbKQlkoC2Wh7PtLx4YV31+BBDIQoKCAChrogPLcg9WRQAYCFBRQQQNeTg5bMa/FJhLIQICCAipogLLvQfXrTt+DgQQyEKCggAoa8LI4bIXvwUACGQhQUEAFDVC29cQovvVUHRkI8GBxFFBBAx1YQH3rBRLIQICCAipowMvVYSt86wUSyECAggIqWE/4OreeI69vf+obTZtDgALvzJ+poIEObIVvtEACGQhQQFkoC2Wh7BvN34XVN1pA4lJEfVsFCqjAO/OHO7AVvq0CCWQgQEEBFVAulAvlSnne5CyODAQoKKCCBjqwFfOSz1+veck3oXHlqa2AChrwTnLYCt9EgQQyEKCggAoaoNwpG2Wj7LvJL4nVd1Ogxl2AWgMdrLcVxfdOmUggAwEKStxNlHkrNNFW+C4oEwVU0EAHtsLfgAIJZCCAcqacKWfKvi9KcdgK3xeBBDIQoKAAL1eHl5ujA1vh+2L+jL8BBTIQUNeg74LS/Ybdf9gcGQgYT6MujgIqaGA8jZoctsKXekBAX//vcxk7Gk+18VT93aFmhwAFBVTQQAe2whd2IAHKnXKn3Cl3yp1yp9wpG2WjbJSNslE2ykbZ1nL1he3TUn1hBzIQoKCACtoKP+H7S1D9hB9QUEAFDXRgK3ypB0ospDqX8UQDHayLrc5lPJFABgIUUBbKLOPKMq4s46qUlbJSVspKWSkrZaWslJVyoVwoF8qFcqFcKBfKhXKhXCjXdaPVeXrvDgUFVNBAB+tmrHNfTCTAwxsPbzx8Ln5x2Iq5+CcSyECAggLozIXtH80ZPzzX8/zQrYAKfEX5IfupO2CBNld4d6zBNlf4hJf1/f3uxIebX15fHh78s80Pn3aOz0B/3b88PL+ePj2/PT3dnf5z//Q2f+ivX/fPc3y9fxnfHcv24fn7GEfwx+PTg+v97vLoZfuh5pc088FW7fzwcv3jW+HxPR94/Pgkra6BwXakkIVDSOMm+lDBEoVxV3ikIHoplGWrULcL4+pL18JgPxfk+oAu50ApG4G+dwylnI+hLrfOwrFXQtrlOXTdKqS0l+h2Tlg7krjutdgv+J3UP70Y106E9SNTqYmnMD75lc15qHsJ0XNC7Ehi3NizKgd1ayp3C8ZxjBt/OTCVHydC66GpPB/E+Fx8c2fkvVU57jDOCctHEtdN5X7h5qn8MBF25O1i/M7g/GKMG4bNedhbENY534+PGDbnYe9UuSTec8YnEfVQ4XyuHB9P9AMz+XEetByayXI+z437h62CpFtncq9w3UzuF26eyQ/zYEcuIMavQ3gK4xcim286snemTPX8co5fOm3Nw27BLlcxS9sq9L3DMCZi/Drn4GH0fj6MzQVx5VSOOTnyYiRZzgWxWwtVjmyMsRKXy6LcfDF0b1W2y6rcvpC5OmHLkVPldcfhHyJuPwm5nKdk8zok65Vvfb3enNh+90x2+7VMuvUKu+Sbr033znbjHElhsSOny6vOVMutJ6qyd5YZv0w7z0LSYzeNt52nxqPschD1yEFct552E9ftrb1ETqrnc105dMdl/XwcH68ByvWn/OX8trN8nIjrA+kS+HAN8S8ClxP+8nEejz2DrUOobfcipP/txdj7++fxxf23x5f/+zO2d0+9PN5/fXpYv/zx9vztw3df//uL7/BncL9efn57+P728uCly9/CjX/+aCnfNZHP/pdO40vJd9L8i+TfGy9PS+nzuz+V/wE=", "file_map": { "3": { "source": "use crate::cmp::{Eq, Ord};\nuse crate::convert::From;\nuse crate::runtime::is_unconstrained;\n\nmod check_shuffle;\nmod quicksort;\n\nimpl [T; N] {\n /// Returns the length of this array.\n ///\n /// ```noir\n /// fn len(self) -> Field\n /// ```\n ///\n /// example\n ///\n /// ```noir\n /// fn main() {\n /// let array = [42, 42];\n /// assert(array.len() == 2);\n /// }\n /// ```\n #[builtin(array_len)]\n pub fn len(self) -> u32 {}\n\n /// Returns this array as a slice.\n ///\n /// ```noir\n /// let array = [1, 2];\n /// let slice = array.as_slice();\n /// assert_eq(slice, &[1, 2]);\n /// ```\n #[builtin(as_slice)]\n pub fn as_slice(self) -> [T] {}\n\n /// Applies a function to each element of this array, returning a new array containing the mapped elements.\n ///\n /// Example:\n ///\n /// ```rust\n /// let a = [1, 2, 3];\n /// let b = a.map(|a| a * 2);\n /// assert_eq(b, [2, 4, 6]);\n /// ```\n pub fn map(self, f: fn[Env](T) -> U) -> [U; N] {\n let uninitialized = crate::mem::zeroed();\n let mut ret = [uninitialized; N];\n\n for i in 0..self.len() {\n ret[i] = f(self[i]);\n }\n\n ret\n }\n\n /// Applies a function to each element of this array along with its index,\n /// returning a new array containing the mapped elements.\n ///\n /// Example:\n ///\n /// ```rust\n /// let a = [1, 2, 3];\n /// let b = a.mapi(|i, a| i + a * 2);\n /// assert_eq(b, [2, 5, 8]);\n /// ```\n pub fn mapi(self, f: fn[Env](u32, T) -> U) -> [U; N] {\n let uninitialized = crate::mem::zeroed();\n let mut ret = [uninitialized; N];\n\n for i in 0..self.len() {\n ret[i] = f(i, self[i]);\n }\n\n ret\n }\n\n /// Applies a function to each element of this array.\n ///\n /// Example:\n ///\n /// ```rust\n /// let a = [1, 2, 3];\n /// let mut b = [0; 3];\n /// let mut i = 0;\n /// a.for_each(|x| {\n /// b[i] = x;\n /// i += 1;\n /// });\n /// assert_eq(a, b);\n /// ```\n pub fn for_each(self, f: fn[Env](T) -> ()) {\n for i in 0..self.len() {\n f(self[i]);\n }\n }\n\n /// Applies a function to each element of this array along with its index.\n ///\n /// Example:\n ///\n /// ```rust\n /// let a = [1, 2, 3];\n /// let mut b = [0; 3];\n /// a.for_eachi(|i, x| {\n /// b[i] = x;\n /// });\n /// assert_eq(a, b);\n /// ```\n pub fn for_eachi(self, f: fn[Env](u32, T) -> ()) {\n for i in 0..self.len() {\n f(i, self[i]);\n }\n }\n\n /// Applies a function to each element of the array, returning the final accumulated value. The first\n /// parameter is the initial value.\n ///\n /// This is a left fold, so the given function will be applied to the accumulator and first element of\n /// the array, then the second, and so on. For a given call the expected result would be equivalent to:\n ///\n /// ```rust\n /// let a1 = [1];\n /// let a2 = [1, 2];\n /// let a3 = [1, 2, 3];\n ///\n /// let f = |a, b| a - b;\n /// a1.fold(10, f); //=> f(10, 1)\n /// a2.fold(10, f); //=> f(f(10, 1), 2)\n /// a3.fold(10, f); //=> f(f(f(10, 1), 2), 3)\n ///\n /// assert_eq(a3.fold(10, f), 10 - 1 - 2 - 3);\n /// ```\n pub fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U {\n for elem in self {\n accumulator = f(accumulator, elem);\n }\n accumulator\n }\n\n /// Same as fold, but uses the first element as the starting element.\n ///\n /// Requires the input array to be non-empty.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn main() {\n /// let arr = [1, 2, 3, 4];\n /// let reduced = arr.reduce(|a, b| a + b);\n /// assert(reduced == 10);\n /// }\n /// ```\n pub fn reduce(self, f: fn[Env](T, T) -> T) -> T {\n let mut accumulator = self[0];\n for i in 1..self.len() {\n accumulator = f(accumulator, self[i]);\n }\n accumulator\n }\n\n /// Returns true if all the elements in this array satisfy the given predicate.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn main() {\n /// let arr = [2, 2, 2, 2, 2];\n /// let all = arr.all(|a| a == 2);\n /// assert(all);\n /// }\n /// ```\n pub fn all(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = true;\n for elem in self {\n ret &= predicate(elem);\n }\n ret\n }\n\n /// Returns true if any of the elements in this array satisfy the given predicate.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn main() {\n /// let arr = [2, 2, 2, 2, 5];\n /// let any = arr.any(|a| a == 5);\n /// assert(any);\n /// }\n /// ```\n pub fn any(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = false;\n for elem in self {\n ret |= predicate(elem);\n }\n ret\n }\n\n /// Concatenates this array with another array.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn main() {\n /// let arr1 = [1, 2, 3, 4];\n /// let arr2 = [6, 7, 8, 9, 10, 11];\n /// let concatenated_arr = arr1.concat(arr2);\n /// assert(concatenated_arr == [1, 2, 3, 4, 6, 7, 8, 9, 10, 11]);\n /// }\n /// ```\n pub fn concat(self, array2: [T; M]) -> [T; N + M] {\n let mut result = [crate::mem::zeroed(); N + M];\n for i in 0..N {\n result[i] = self[i];\n }\n for i in 0..M {\n result[i + N] = array2[i];\n }\n result\n }\n}\n\nimpl [T; N]\nwhere\n T: Ord + Eq,\n{\n /// Returns a new sorted array. The original array remains untouched. Notice that this function will\n /// only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting\n /// logic it uses internally is optimized specifically for these values. If you need a sort function to\n /// sort any type, you should use the `sort_via` function.\n ///\n /// Example:\n ///\n /// ```rust\n /// fn main() {\n /// let arr = [42, 32];\n /// let sorted = arr.sort();\n /// assert(sorted == [32, 42]);\n /// }\n /// ```\n pub fn sort(self) -> Self {\n self.sort_via(|a, b| a <= b)\n }\n}\n\nimpl [T; N]\nwhere\n T: Eq,\n{\n /// Returns a new sorted array by sorting it with a custom comparison function.\n /// The original array remains untouched.\n /// The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument.\n ///\n /// Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements.\n ///\n /// Example:\n ///\n /// ```rust\n /// fn main() {\n /// let arr = [42, 32]\n /// let sorted_ascending = arr.sort_via(|a, b| a <= b);\n /// assert(sorted_ascending == [32, 42]); // verifies\n ///\n /// let sorted_descending = arr.sort_via(|a, b| a >= b);\n /// assert(sorted_descending == [32, 42]); // does not verify\n /// }\n /// ```\n pub fn sort_via(self, ordering: fn[Env](T, T) -> bool) -> Self {\n // Safety: `sorted` array is checked to be:\n // a. a permutation of `input`'s elements\n // b. satisfying the predicate `ordering`\n let sorted = unsafe { quicksort::quicksort(self, ordering) };\n\n if !is_unconstrained() {\n for i in 0..N - 1 {\n assert(\n ordering(sorted[i], sorted[i + 1]),\n \"Array has not been sorted correctly according to `ordering`.\",\n );\n }\n check_shuffle::check_shuffle(self, sorted);\n }\n sorted\n }\n}\n\nimpl [u8; N] {\n /// Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation -\n /// the given array is interpreted as-is as a string.\n ///\n /// Example:\n ///\n /// ```rust\n /// fn main() {\n /// let hi = [104, 105].as_str_unchecked();\n /// assert_eq(hi, \"hi\");\n /// }\n /// ```\n #[builtin(array_as_str_unchecked)]\n pub fn as_str_unchecked(self) -> str {}\n}\n\nimpl From> for [u8; N] {\n /// Returns an array of the string bytes.\n fn from(s: str) -> Self {\n s.as_bytes()\n }\n}\n\nmod test {\n #[test]\n fn map_empty() {\n assert_eq([].map(|x| x + 1), []);\n }\n\n global arr_with_100_values: [u32; 100] = [\n 42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2, 54,\n 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41, 19, 98,\n 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21, 43, 86, 35,\n 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15, 127, 81, 30, 8,\n 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,\n ];\n global expected_with_100_values: [u32; 100] = [\n 0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30, 32,\n 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58, 61, 62,\n 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82, 84, 84, 86,\n 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114, 114, 116, 118,\n 119, 120, 121, 123, 123, 123, 125, 126, 127,\n ];\n fn sort_u32(a: u32, b: u32) -> bool {\n a <= b\n }\n\n #[test]\n fn test_sort() {\n let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];\n\n let sorted = arr.sort();\n\n let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];\n assert(sorted == expected);\n }\n\n #[test]\n fn test_sort_100_values() {\n let mut arr: [u32; 100] = [\n 42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,\n 54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,\n 19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,\n 43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,\n 127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,\n ];\n\n let sorted = arr.sort();\n\n let expected: [u32; 100] = [\n 0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30,\n 32, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,\n 61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,\n 84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,\n 114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,\n ];\n assert(sorted == expected);\n }\n\n #[test]\n fn test_sort_100_values_comptime() {\n let sorted = arr_with_100_values.sort();\n assert(sorted == expected_with_100_values);\n }\n\n #[test]\n fn test_sort_via() {\n let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];\n\n let sorted = arr.sort_via(sort_u32);\n\n let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];\n assert(sorted == expected);\n }\n\n #[test]\n fn test_sort_via_100_values() {\n let mut arr: [u32; 100] = [\n 42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,\n 54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,\n 19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,\n 43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,\n 127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,\n ];\n\n let sorted = arr.sort_via(sort_u32);\n\n let expected: [u32; 100] = [\n 0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30,\n 32, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,\n 61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,\n 84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,\n 114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,\n ];\n assert(sorted == expected);\n }\n\n #[test]\n fn mapi_empty() {\n assert_eq([].mapi(|i, x| i * x + 1), []);\n }\n\n #[test]\n fn for_each_empty() {\n let empty_array: [Field; 0] = [];\n empty_array.for_each(|_x| assert(false));\n }\n\n #[test]\n fn for_eachi_empty() {\n let empty_array: [Field; 0] = [];\n empty_array.for_eachi(|_i, _x| assert(false));\n }\n\n #[test]\n fn map_example() {\n let a = [1, 2, 3];\n let b = a.map(|a| a * 2);\n assert_eq(b, [2, 4, 6]);\n }\n\n #[test]\n fn mapi_example() {\n let a = [1, 2, 3];\n let b = a.mapi(|i, a| i + a * 2);\n assert_eq(b, [2, 5, 8]);\n }\n\n #[test]\n fn for_each_example() {\n let a = [1, 2, 3];\n let mut b = [0, 0, 0];\n let b_ref = &mut b;\n let mut i = 0;\n let i_ref = &mut i;\n a.for_each(|x| {\n b_ref[*i_ref] = x * 2;\n *i_ref += 1;\n });\n assert_eq(b, [2, 4, 6]);\n assert_eq(i, 3);\n }\n\n #[test]\n fn for_eachi_example() {\n let a = [1, 2, 3];\n let mut b = [0, 0, 0];\n let b_ref = &mut b;\n a.for_eachi(|i, a| { b_ref[i] = i + a * 2; });\n assert_eq(b, [2, 5, 8]);\n }\n\n #[test]\n fn concat() {\n let arr1 = [1, 2, 3, 4];\n let arr2 = [6, 7, 8, 9, 10, 11];\n let concatenated_arr = arr1.concat(arr2);\n assert_eq(concatenated_arr, [1, 2, 3, 4, 6, 7, 8, 9, 10, 11]);\n }\n\n #[test]\n fn concat_zero_length_with_something() {\n let arr1 = [];\n let arr2 = [1];\n let concatenated_arr = arr1.concat(arr2);\n assert_eq(concatenated_arr, [1]);\n }\n\n #[test]\n fn concat_something_with_zero_length() {\n let arr1 = [1];\n let arr2 = [];\n let concatenated_arr = arr1.concat(arr2);\n assert_eq(concatenated_arr, [1]);\n }\n\n #[test]\n fn concat_zero_lengths() {\n let arr1: [Field; 0] = [];\n let arr2: [Field; 0] = [];\n let concatenated_arr = arr1.concat(arr2);\n assert_eq(concatenated_arr, []);\n }\n}\n", diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__expanded.snap new file mode 100644 index 00000000000..122c53ae64e --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__expanded.snap @@ -0,0 +1,17 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +fn main() -> pub str<2> { + let mut b: i8 = 0_i8; + // Safety: comment added by `nargo expand` + if unsafe { func_1_proxy() } { + b = 112_i8; + b = -b * b; + }; + "ok" +} + +unconstrained fn func_1_proxy() -> bool { + false +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap new file mode 100644 index 00000000000..dd1a608f953 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_-9223372036854775808.snap @@ -0,0 +1,34 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "EXPR [ (1, _0) -111 ]", + "EXPR [ (1, _1) -107 ]" + ], + "debug_symbols": "jZDdCoMwDIXfpde9WPePrzKGxBqlENoSW2GI774ournBYFfpydfvQDuoGqvcls43oVPFbVAVOyLXlhQsJBe8bIdRqzWWiRFlpTZcrAiMPqnCZyKteqA8X+oi+HkmYKE7rdDXMqWwcYTTadRve/dbPR8X93J9yae/bWMOi2725sO/SwLr+Pu9PbCDinCJTfZ2Q9MjrmT9r8jBYp0Zp6aZSfcT", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..dd1a608f953 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,34 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "EXPR [ (1, _0) -111 ]", + "EXPR [ (1, _1) -107 ]" + ], + "debug_symbols": "jZDdCoMwDIXfpde9WPePrzKGxBqlENoSW2GI774ournBYFfpydfvQDuoGqvcls43oVPFbVAVOyLXlhQsJBe8bIdRqzWWiRFlpTZcrAiMPqnCZyKteqA8X+oi+HkmYKE7rdDXMqWwcYTTadRve/dbPR8X93J9yae/bWMOi2725sO/SwLr+Pu9PbCDinCJTfZ2Q9MjrmT9r8jBYp0Zp6aZSfcT", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_9223372036854775807.snap new file mode 100644 index 00000000000..dd1a608f953 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_false_inliner_9223372036854775807.snap @@ -0,0 +1,34 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "EXPR [ (1, _0) -111 ]", + "EXPR [ (1, _1) -107 ]" + ], + "debug_symbols": "jZDdCoMwDIXfpde9WPePrzKGxBqlENoSW2GI774ournBYFfpydfvQDuoGqvcls43oVPFbVAVOyLXlhQsJBe8bIdRqzWWiRFlpTZcrAiMPqnCZyKteqA8X+oi+HkmYKE7rdDXMqWwcYTTadRve/dbPR8X93J9yae/bWMOi2725sO/SwLr+Pu9PbCDinCJTfZ2Q9MjrmT9r8jBYp0Zp6aZSfcT", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap new file mode 100644 index 00000000000..368c5c1747b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_-9223372036854775808.snap @@ -0,0 +1,47 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": { + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "BRILLIG CALL func 0: inputs: [], outputs: [Array([Witness(0), Witness(1)])]", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 18 }, Call { location: 19 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 2 }, Mov { destination: Direct(32771), source: Relative(2) }, Mov { destination: Direct(32772), source: Relative(3) }, Mov { destination: Direct(32773), source: Relative(4) }, Call { location: 33 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Return, Call { location: 44 }, Const { destination: Relative(1), bit_size: Integer(U8), value: 111 }, Const { destination: Relative(2), bit_size: Integer(U8), value: 107 }, Mov { destination: Relative(3), source: Direct(1) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(4) }, IndirectConst { destination_pointer: Relative(3), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(3), rhs: Direct(2) }, Mov { destination: Relative(5), source: Relative(4) }, Store { destination_pointer: Relative(5), source: Relative(1) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(1), source: Relative(3) }, Return, BinaryIntOp { destination: Direct(32775), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32773) }, Mov { destination: Direct(32776), source: Direct(32771) }, Mov { destination: Direct(32777), source: Direct(32772) }, BinaryIntOp { destination: Direct(32778), op: Equals, bit_size: U32, lhs: Direct(32776), rhs: Direct(32775) }, JumpIf { condition: Direct(32778), location: 43 }, Load { destination: Direct(32774), source_pointer: Direct(32776) }, Store { destination_pointer: Direct(32777), source: Direct(32774) }, BinaryIntOp { destination: Direct(32776), op: Add, bit_size: U32, lhs: Direct(32776), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32777), rhs: Direct(2) }, Jump { location: 36 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 49 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZBNDoMgEIXvMmsWIvT3KsYY1LEhIWgoNGkMd+8gWu2iGz5mhveGvBl6bMOj0XYYn3CvZmidNkY/GjN2yuvRUneGIh0lnZxByTPKDJEhM04Z54xLxjXjtkAklxgZbAsa7xCT/2Ej/WNSDq2Huw3GMHgpE5ZHz0nZhV45mhYM0PZEMhy0wXSLbFcX/6Wci1XMS/6Vn0hfU6U67X4yiMnJadUaXMsh2O4w9e9pm2wZTm7ssA8Ok9MeJEVWCcGkqBlw6lRSMnmrY9r8AQ==", + "file_map": { + "50": { + "source": "fn main() -> pub str<2> {\n let mut b = 0_i8;\n if unsafe { func_1_proxy() } {\n b = 112;\n b = ((-b) * b);\n };\n \"ok\"\n}\nunconstrained fn func_1_proxy() -> bool {\n false\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_0.snap new file mode 100644 index 00000000000..368c5c1747b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_0.snap @@ -0,0 +1,47 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": { + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "BRILLIG CALL func 0: inputs: [], outputs: [Array([Witness(0), Witness(1)])]", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 18 }, Call { location: 19 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 2 }, Mov { destination: Direct(32771), source: Relative(2) }, Mov { destination: Direct(32772), source: Relative(3) }, Mov { destination: Direct(32773), source: Relative(4) }, Call { location: 33 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Return, Call { location: 44 }, Const { destination: Relative(1), bit_size: Integer(U8), value: 111 }, Const { destination: Relative(2), bit_size: Integer(U8), value: 107 }, Mov { destination: Relative(3), source: Direct(1) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(4) }, IndirectConst { destination_pointer: Relative(3), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(3), rhs: Direct(2) }, Mov { destination: Relative(5), source: Relative(4) }, Store { destination_pointer: Relative(5), source: Relative(1) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(1), source: Relative(3) }, Return, BinaryIntOp { destination: Direct(32775), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32773) }, Mov { destination: Direct(32776), source: Direct(32771) }, Mov { destination: Direct(32777), source: Direct(32772) }, BinaryIntOp { destination: Direct(32778), op: Equals, bit_size: U32, lhs: Direct(32776), rhs: Direct(32775) }, JumpIf { condition: Direct(32778), location: 43 }, Load { destination: Direct(32774), source_pointer: Direct(32776) }, Store { destination_pointer: Direct(32777), source: Direct(32774) }, BinaryIntOp { destination: Direct(32776), op: Add, bit_size: U32, lhs: Direct(32776), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32777), rhs: Direct(2) }, Jump { location: 36 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 49 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZBNDoMgEIXvMmsWIvT3KsYY1LEhIWgoNGkMd+8gWu2iGz5mhveGvBl6bMOj0XYYn3CvZmidNkY/GjN2yuvRUneGIh0lnZxByTPKDJEhM04Z54xLxjXjtkAklxgZbAsa7xCT/2Ej/WNSDq2Huw3GMHgpE5ZHz0nZhV45mhYM0PZEMhy0wXSLbFcX/6Wci1XMS/6Vn0hfU6U67X4yiMnJadUaXMsh2O4w9e9pm2wZTm7ssA8Ok9MeJEVWCcGkqBlw6lRSMnmrY9r8AQ==", + "file_map": { + "50": { + "source": "fn main() -> pub str<2> {\n let mut b = 0_i8;\n if unsafe { func_1_proxy() } {\n b = 112;\n b = ((-b) * b);\n };\n \"ok\"\n}\nunconstrained fn func_1_proxy() -> bool {\n false\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_9223372036854775807.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_9223372036854775807.snap new file mode 100644 index 00000000000..368c5c1747b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__force_brillig_true_inliner_9223372036854775807.snap @@ -0,0 +1,47 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": { + "abi_type": { + "kind": "string", + "length": 2 + }, + "visibility": "public" + }, + "error_types": { + "17843811134343075018": { + "error_kind": "string", + "string": "Stack too deep" + } + } + }, + "bytecode": [ + "func 0", + "current witness index : _1", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : [_0, _1]", + "BRILLIG CALL func 0: inputs: [], outputs: [Array([Witness(0), Witness(1)])]", + "unconstrained func 0", + "[Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32838 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(1), bit_size: Integer(U32), value: 0 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(1), offset_address: Relative(2) }, Call { location: 18 }, Call { location: 19 }, BinaryIntOp { destination: Relative(2), op: Add, bit_size: U32, lhs: Relative(1), rhs: Direct(2) }, Const { destination: Relative(3), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 2 }, Mov { destination: Direct(32771), source: Relative(2) }, Mov { destination: Direct(32772), source: Relative(3) }, Mov { destination: Direct(32773), source: Relative(4) }, Call { location: 33 }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32836 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Return, Call { location: 44 }, Const { destination: Relative(1), bit_size: Integer(U8), value: 111 }, Const { destination: Relative(2), bit_size: Integer(U8), value: 107 }, Mov { destination: Relative(3), source: Direct(1) }, Const { destination: Relative(4), bit_size: Integer(U32), value: 3 }, BinaryIntOp { destination: Direct(1), op: Add, bit_size: U32, lhs: Direct(1), rhs: Relative(4) }, IndirectConst { destination_pointer: Relative(3), bit_size: Integer(U32), value: 1 }, BinaryIntOp { destination: Relative(4), op: Add, bit_size: U32, lhs: Relative(3), rhs: Direct(2) }, Mov { destination: Relative(5), source: Relative(4) }, Store { destination_pointer: Relative(5), source: Relative(1) }, BinaryIntOp { destination: Relative(5), op: Add, bit_size: U32, lhs: Relative(5), rhs: Direct(2) }, Store { destination_pointer: Relative(5), source: Relative(2) }, Mov { destination: Relative(1), source: Relative(3) }, Return, BinaryIntOp { destination: Direct(32775), op: Add, bit_size: U32, lhs: Direct(32771), rhs: Direct(32773) }, Mov { destination: Direct(32776), source: Direct(32771) }, Mov { destination: Direct(32777), source: Direct(32772) }, BinaryIntOp { destination: Direct(32778), op: Equals, bit_size: U32, lhs: Direct(32776), rhs: Direct(32775) }, JumpIf { condition: Direct(32778), location: 43 }, Load { destination: Direct(32774), source_pointer: Direct(32776) }, Store { destination_pointer: Direct(32777), source: Direct(32774) }, BinaryIntOp { destination: Direct(32776), op: Add, bit_size: U32, lhs: Direct(32776), rhs: Direct(2) }, BinaryIntOp { destination: Direct(32777), op: Add, bit_size: U32, lhs: Direct(32777), rhs: Direct(2) }, Jump { location: 36 }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 49 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 17843811134343075018 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return]" + ], + "debug_symbols": "dZBNDoMgEIXvMmsWIvT3KsYY1LEhIWgoNGkMd+8gWu2iGz5mhveGvBl6bMOj0XYYn3CvZmidNkY/GjN2yuvRUneGIh0lnZxByTPKDJEhM04Z54xLxjXjtkAklxgZbAsa7xCT/2Ej/WNSDq2Huw3GMHgpE5ZHz0nZhV45mhYM0PZEMhy0wXSLbFcX/6Wci1XMS/6Vn0hfU6U67X4yiMnJadUaXMsh2O4w9e9pm2wZTm7ssA8Ok9MeJEVWCcGkqBlw6lRSMnmrY9r8AQ==", + "file_map": { + "50": { + "source": "fn main() -> pub str<2> {\n let mut b = 0_i8;\n if unsafe { func_1_proxy() } {\n b = 112;\n b = ((-b) * b);\n };\n \"ok\"\n}\nunconstrained fn func_1_proxy() -> bool {\n false\n}\n", + "path": "" + } + }, + "names": [ + "main" + ], + "brillig_names": [ + "main" + ] +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__stdout.snap new file mode 100644 index 00000000000..774290bd52f --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_9047/execute__tests__stdout.snap @@ -0,0 +1,5 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stdout +--- +[regression_9047] Circuit output: String("ok") diff --git a/tooling/ssa_fuzzer/src/builder.rs b/tooling/ssa_fuzzer/src/builder.rs index c58b1d9d1fb..1ef13677696 100644 --- a/tooling/ssa_fuzzer/src/builder.rs +++ b/tooling/ssa_fuzzer/src/builder.rs @@ -223,7 +223,7 @@ impl FuzzerBuilder { TypedValue::new(res, lhs.type_of_variable) } - /// Inserts a bitwise XOR instruction between two values + /// Inserts a bitwise XOR instruction between two values pub fn insert_xor_instruction(&mut self, lhs: TypedValue, rhs: TypedValue) -> TypedValue { if !lhs.supports_bitwise() { return lhs; diff --git a/tooling/ssa_fuzzer/src/lib.rs b/tooling/ssa_fuzzer/src/lib.rs index 1031f8537ba..4b74bbe56fe 100644 --- a/tooling/ssa_fuzzer/src/lib.rs +++ b/tooling/ssa_fuzzer/src/lib.rs @@ -10,8 +10,8 @@ mod tests { use crate::builder::{FuzzerBuilder, FuzzerBuilderError, InstructionWithTwoArgs}; use crate::runner::{CompareResults, run_and_compare}; use crate::typed_value::{TypedValue, ValueType}; - use acvm::FieldElement; use acvm::acir::native_types::{Witness, WitnessMap}; + use acvm::{AcirField, FieldElement}; use noirc_driver::{CompileOptions, CompiledProgram}; use rand::Rng; @@ -66,9 +66,8 @@ mod tests { witness_map } - fn compare_results(computed_rust: u64, computed_noir: FieldElement) { - let computed_rust = FieldElement::from(computed_rust); - assert_eq!(computed_rust, computed_noir, "Noir doesn't match Rust"); + fn compare_results>(computed_rust: T, computed_noir: FieldElement) { + assert_eq!(computed_rust.into(), computed_noir, "Noir doesn't match Rust"); } /// Runs the given instruction with the given values and returns the results of the ACIR and Brillig programs @@ -128,7 +127,7 @@ mod tests { } #[test] - fn test_add() { + fn test_unsigned_add() { let mut rng = rand::thread_rng(); let mut lhs: u64 = rng.r#gen(); let rhs: u64 = rng.r#gen(); @@ -143,8 +142,44 @@ mod tests { compare_results(lhs + rhs, noir_res); } + fn parse_integer_to_signed>(integer: T) -> FieldElement { + let integer: i128 = integer.into(); + let width = 8 * size_of::(); + + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; + + if integer < min { + panic!("value is less than min"); + } else if integer > max { + panic!("value is greater than max"); + } + + if integer < 0 { + FieldElement::from(2u32).pow(&width.into()) + FieldElement::from(integer) + } else { + FieldElement::from(integer) + } + } + + #[test] + fn test_signed_add() { + let mut rng = rand::thread_rng(); + let mut lhs: i64 = rng.r#gen(); + let rhs: i64 = rng.r#gen(); + + // to prevent `attempt to add with overflow` + lhs %= 12341234; + let noir_res = run_instruction_double_arg( + FuzzerBuilder::insert_add_instruction_checked, + (parse_integer_to_signed(lhs), ValueType::I64), + (parse_integer_to_signed(rhs), ValueType::I64), + ); + compare_results(parse_integer_to_signed(lhs + rhs), noir_res); + } + #[test] - fn test_sub() { + fn test_unsigned_sub() { let mut rng = rand::thread_rng(); let mut lhs: u64 = rng.r#gen(); let mut rhs: u64 = rng.r#gen(); @@ -162,7 +197,24 @@ mod tests { } #[test] - fn test_mul() { + fn test_signed_sub() { + let mut rng = rand::thread_rng(); + let mut lhs: i64 = rng.r#gen(); + let mut rhs: i64 = rng.r#gen(); + + // to prevent `attempt to subtract with overflow` + lhs %= 12341234; + rhs %= 12341234; + let noir_res = run_instruction_double_arg( + FuzzerBuilder::insert_sub_instruction_checked, + (parse_integer_to_signed(lhs), ValueType::I64), + (parse_integer_to_signed(rhs), ValueType::I64), + ); + compare_results(parse_integer_to_signed(lhs - rhs), noir_res); + } + + #[test] + fn test_unsigned_mul() { let mut rng = rand::thread_rng(); let mut lhs: u64 = rng.r#gen(); let mut rhs: u64 = rng.r#gen(); @@ -178,6 +230,23 @@ mod tests { compare_results(lhs * rhs, noir_res); } + #[test] + fn test_signed_mul() { + let mut rng = rand::thread_rng(); + let mut lhs: u64 = rng.r#gen(); + let mut rhs: u64 = rng.r#gen(); + + // to prevent `attempt to multiply with overflow` + lhs %= 12341234; + rhs %= 12341234; + let noir_res = run_instruction_double_arg( + FuzzerBuilder::insert_mul_instruction_checked, + (parse_integer_to_signed(lhs), ValueType::I64), + (parse_integer_to_signed(rhs), ValueType::I64), + ); + compare_results(parse_integer_to_signed(lhs * rhs), noir_res); + } + #[test] fn test_div() { let mut rng = rand::thread_rng(); @@ -277,62 +346,6 @@ mod tests { } } - #[test] - fn regression_multiplication_without_range_check() { - let mut acir_builder = FuzzerBuilder::new_acir(); - let mut brillig_builder = FuzzerBuilder::new_brillig(); - - let field_acir_var = acir_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id; - let field_brillig_var = - brillig_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id; - - let truncated_acir = acir_builder.builder.insert_truncate(field_acir_var, 16, 254); - let truncated_brillig = brillig_builder.builder.insert_truncate(field_brillig_var, 16, 254); - - let field_casted_i16_acir = - acir_builder.builder.insert_cast(truncated_acir, NumericType::Signed { bit_size: 16 }); - let field_casted_i16_brillig = brillig_builder - .builder - .insert_cast(truncated_brillig, NumericType::Signed { bit_size: 16 }); - - let casted_pow_2_acir = acir_builder.builder.insert_binary( - field_casted_i16_acir, - BinaryOp::Mul { unchecked: false }, - field_casted_i16_acir, - ); - let casted_pow_2_brillig = brillig_builder.builder.insert_binary( - field_casted_i16_brillig, - BinaryOp::Mul { unchecked: false }, - field_casted_i16_brillig, - ); - - let last_var = acir_builder.builder.insert_binary( - casted_pow_2_acir, - BinaryOp::Div, - field_casted_i16_acir, - ); - let last_var_brillig = brillig_builder.builder.insert_binary( - casted_pow_2_brillig, - BinaryOp::Div, - field_casted_i16_brillig, - ); - - acir_builder.builder.terminate_with_return(vec![last_var]); - brillig_builder.builder.terminate_with_return(vec![last_var_brillig]); - - let acir_result = acir_builder.compile(CompileOptions::default()); - check_expected_validation_error( - acir_result, - "Signed binary operation does not follow overflow pattern", - ); - - let brillig_result = brillig_builder.compile(CompileOptions::default()); - check_expected_validation_error( - brillig_result, - "Signed binary operation does not follow overflow pattern", - ); - } - #[test] fn regression_cast_without_truncate() { let mut acir_builder = FuzzerBuilder::new_acir(); @@ -378,42 +391,4 @@ mod tests { "Invalid cast from Field, not preceded by valid truncation or known safe value", ); } - - #[test] - fn regression_signed_sub() { - let mut acir_builder = FuzzerBuilder::new_acir(); - let mut brillig_builder = FuzzerBuilder::new_brillig(); - - let i16_acir_var_1 = acir_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id; - let i16_acir_var_2 = acir_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id; - let i16_brillig_var_1 = - brillig_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id; - let i16_brillig_var_2 = - brillig_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id; - - let sub_acir = acir_builder.builder.insert_binary( - i16_acir_var_1, - BinaryOp::Sub { unchecked: false }, - i16_acir_var_2, - ); - let sub_brillig = brillig_builder.builder.insert_binary( - i16_brillig_var_1, - BinaryOp::Sub { unchecked: false }, - i16_brillig_var_2, - ); - - acir_builder.builder.terminate_with_return(vec![sub_acir]); - brillig_builder.builder.terminate_with_return(vec![sub_brillig]); - - let acir_result = acir_builder.compile(CompileOptions::default()); - check_expected_validation_error( - acir_result, - "Signed binary operation does not follow overflow pattern", - ); - let brillig_result = brillig_builder.compile(CompileOptions::default()); - check_expected_validation_error( - brillig_result, - "Signed binary operation does not follow overflow pattern", - ); - } }