Skip to content

Commit

Permalink
Merge branch 'master' into mv/publish-profiler-binaries
Browse files Browse the repository at this point in the history
  • Loading branch information
vezenovm authored Feb 20, 2025
2 parents cd870e2 + 1fa9b33 commit 8f68fdf
Show file tree
Hide file tree
Showing 88 changed files with 714 additions and 134 deletions.
54 changes: 30 additions & 24 deletions compiler/noirc_evaluator/src/acir/acir_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,8 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result<AcirVar, RuntimeError> {
let bit_size = typ.bit_size::<F>();
// Subtracting from max flips the bits
let max = self.add_constant((1_u128 << bit_size) - 1);
let max = power_of_two::<F>(bit_size) - F::one();
let max = self.add_constant(max);
self.sub_var(max, x)
}

Expand Down Expand Up @@ -841,18 +842,19 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
}

// maximum bit size for q and for [r and rhs]
let mut max_q_bits = bit_size;
let mut max_rhs_bits = bit_size;
// when rhs is constant, we can better estimate the maximum bit sizes
if let Some(rhs_const) = rhs_expr.to_const() {
max_rhs_bits = rhs_const.num_bits();
if max_rhs_bits != 0 {
if max_rhs_bits > bit_size {
return Ok((zero, zero));
}
max_q_bits = bit_size - max_rhs_bits + 1;
}
}
let (max_q_bits, max_rhs_bits) = if let Some(rhs_const) = rhs_expr.to_const() {
// when rhs is constant, we can better estimate the maximum bit sizes
let max_rhs_bits = rhs_const.num_bits();
assert!(
max_rhs_bits <= bit_size,
"attempted to divide by constant larger than operand type"
);

let max_q_bits = bit_size - max_rhs_bits + 1;
(max_q_bits, max_rhs_bits)
} else {
(bit_size, bit_size)
};

let [q_value, r_value]: [AcirValue; 2] = self
.brillig_call(
Expand Down Expand Up @@ -908,19 +910,9 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
self.assert_eq_var(lhs_constraint, rhs_constraint, None)?;

// Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits'
let mut avoid_overflow = false;
if max_q_bits + max_rhs_bits >= F::max_num_bits() - 1 {
// q*b+r can overflow; we avoid this when b is constant
if rhs_expr.is_const() {
avoid_overflow = true;
} else {
// we do not support unbounded division
unreachable!("overflow in unbounded division");
}
}

if let Some(rhs_const) = rhs_expr.to_const() {
if avoid_overflow {
if let Some(rhs_const) = rhs_expr.to_const() {
// we compute q0 = p/rhs
let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes());
let q0_big = F::modulus() / &rhs_big;
Expand All @@ -944,6 +936,20 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
predicate,
rhs_const.num_bits(),
)?;
} else if bit_size == 128 {
// q and b are u128 and q*b could overflow so we check that either q or b are less than 2^64
let two_pow_64: F = power_of_two(64);
let two_pow_64 = self.add_constant(two_pow_64);

let (q_upper, _) =
self.euclidean_division_var(quotient_var, two_pow_64, bit_size, predicate)?;
let (rhs_upper, _) =
self.euclidean_division_var(rhs, two_pow_64, bit_size, predicate)?;
let mul_uppers = self.mul_var(q_upper, rhs_upper)?;
self.assert_eq_var(mul_uppers, zero, None)?;
} else {
// we do not support unbounded division
unreachable!("overflow in unbounded division");
}
}

Expand Down
25 changes: 6 additions & 19 deletions compiler/noirc_evaluator/src/acir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1973,26 +1973,7 @@ impl<'a> Context<'a> {
) -> Result<AcirVar, RuntimeError> {
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);
match &binary_type {
Type::Numeric(NumericType::Unsigned { bit_size })
| Type::Numeric(NumericType::Signed { bit_size }) => {
// Conservative max bit size that is small enough such that two operands can be
// multiplied and still fit within the field modulus. This is necessary for the
// truncation technique: result % 2^bit_size to be valid.
let max_integer_bit_size = FieldElement::max_num_bits() / 2;
if *bit_size > max_integer_bit_size {
return Err(RuntimeError::UnsupportedIntegerSize {
num_bits: *bit_size,
max_num_bits: max_integer_bit_size,
call_stack: self.acir_context.get_call_stack(),
});
}
}
_ => {}
}

let binary_type = AcirType::from(binary_type);
let bit_count = binary_type.bit_size::<FieldElement>();
let num_type = binary_type.to_numeric_type();
Expand Down Expand Up @@ -2113,6 +2094,12 @@ impl<'a> Context<'a> {
max_bit_size: u32,
dfg: &DataFlowGraph,
) -> Result<AcirVar, RuntimeError> {
assert_ne!(bit_size, max_bit_size, "Attempted to generate a noop truncation");
assert!(
bit_size < max_bit_size,
"Attempted to generate a truncation into size larger than max input"
);

let mut var = self.convert_numeric_value(value_id, dfg)?;
match &dfg[value_id] {
Value::Instruction { instruction, .. } => {
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result<Ss
.run_pass(Ssa::remove_enable_side_effects, "EnableSideEffectsIf removal")
.run_pass(Ssa::fold_constants_using_constraints, "Constraint Folding")
.run_pass(Ssa::make_constrain_not_equal_instructions, "Adding constrain not equal")
.run_pass(Ssa::check_u128_mul_overflow, "Check u128 mul overflow")
.run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (1st)")
.run_pass(Ssa::simplify_cfg, "Simplifying (3rd):")
.run_pass(Ssa::array_set_optimization, "Array Set Optimizations")
Expand Down
8 changes: 5 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use binary::truncate_field;
use binary::{truncate, truncate_field};
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};

Expand Down Expand Up @@ -911,8 +911,10 @@ impl Instruction {
// would be incorrect however since the extra bits on the field would not be flipped.
Value::NumericConstant { constant, typ } if typ.is_unsigned() => {
// As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills.
let value = !constant.to_u128() % (1 << typ.bit_size());
SimplifiedTo(dfg.make_constant(value.into(), *typ))
let bit_size = typ.bit_size();
assert!(bit_size <= 128);
let not_value: u128 = truncate(!constant.to_u128(), bit_size);
SimplifiedTo(dfg.make_constant(not_value.into(), *typ))
}
Value::Instruction { instruction, .. } => {
// !!v => v
Expand Down
22 changes: 13 additions & 9 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,18 @@ impl Binary {
let bitmask_plus_one = bitmask.to_u128() + 1;
if bitmask_plus_one.is_power_of_two() {
let value = if lhs_value.is_some() { rhs } else { lhs };
let num_bits = bitmask_plus_one.ilog2();
return SimplifyResult::SimplifiedToInstruction(
Instruction::Truncate {
value,
bit_size: num_bits,
max_bit_size: lhs_type.bit_size(),
},
);
let bit_size = bitmask_plus_one.ilog2();
let max_bit_size = lhs_type.bit_size();

if bit_size == max_bit_size {
// If we're truncating a value into the full size of its type then
// the truncation is a noop.
return SimplifyResult::SimplifiedTo(value);
} else {
return SimplifyResult::SimplifiedToInstruction(
Instruction::Truncate { value, bit_size, max_bit_size },
);
}
}
}

Expand Down Expand Up @@ -509,7 +513,7 @@ fn convert_signed_integer_to_field_element(int: i128, bit_size: u32) -> FieldEle
}

/// Truncates `int` to fit within `bit_size` bits.
fn truncate(int: u128, bit_size: u32) -> u128 {
pub(super) fn truncate(int: u128, bit_size: u32) -> u128 {
if bit_size == 128 {
int
} else {
Expand Down
22 changes: 22 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,26 @@ mod tests {
";
assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn simplifies_out_noop_bitwise_ands() {
// Regression test for https://github.com/noir-lang/noir/issues/7451
let src = "
acir(inline) predicate_pure fn main f0 {
b0(v0: u8):
v1 = and u8 255, v0
return v1
}
";

let ssa = Ssa::from_str_simplifying(src).unwrap();

let expected = "
acir(inline) fn main f0 {
b0(v0: u8):
return v0
}
";
assert_normalized_ssa_equals(ssa, expected);
}
}
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl NumericType {
) -> Option<String> {
match self {
NumericType::Unsigned { bit_size } => {
let max = 2u128.pow(bit_size) - 1;
let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 };
if negative {
return Some(format!("0..={}", max));
}
Expand Down
Loading

0 comments on commit 8f68fdf

Please sign in to comment.