Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d005ba1
feat: add unsafe u128 type
TomAFrench Feb 5, 2025
2ba4b04
.
TomAFrench Feb 6, 2025
49fdfb7
.
TomAFrench Feb 6, 2025
17fc271
fix: avoid overflows when printing u128 values
TomAFrench Feb 6, 2025
28a217a
.
TomAFrench Feb 6, 2025
096d34c
.
TomAFrench Feb 6, 2025
a6eafec
.
TomAFrench Feb 10, 2025
644a5ef
.
TomAFrench Feb 10, 2025
a58c84b
fix: made changes to the shifts (#7352)
kashbrti Feb 12, 2025
e3c41d2
chore: hacks to unblock khashayar
TomAFrench Feb 12, 2025
17ceaf2
Update compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs
TomAFrench Feb 13, 2025
5d8f6c5
Merge branch 'master' into tf/u128-hacky
TomAFrench Feb 14, 2025
025534f
.
TomAFrench Feb 14, 2025
138b7db
chore: address some cases where 128 bits is problematic
TomAFrench Feb 14, 2025
0486ab9
Merge branch 'master' into tf/u128-hacky
TomAFrench Feb 14, 2025
32c0166
Merge branch 'master' into tf/u128-hacky
TomAFrench Feb 17, 2025
f88338d
Avoid some overflows for u128
asterite Feb 17, 2025
049d211
Add a test program for u128
asterite Feb 17, 2025
9db6892
Disallow i128
asterite Feb 17, 2025
34d8d88
Generalize getting the max value of an integer in one more place
asterite Feb 17, 2025
4f0d085
Simpler math
asterite Feb 17, 2025
ec7194b
Remove extra comments
asterite Feb 17, 2025
3d7ad18
Add a test
asterite Feb 17, 2025
a7cf75b
Revert a commit
asterite Feb 17, 2025
eab39ca
Check u128 multiplication overflow
asterite Feb 18, 2025
90e96dd
Merge branch 'master' into tf/u128-hacky
asterite Feb 18, 2025
ab4aaa4
Better comments
asterite Feb 18, 2025
59da45a
Use division instead of and
asterite Feb 18, 2025
171766c
Remove now unneeded check
asterite Feb 18, 2025
f7ee9d8
Update compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
asterite Feb 18, 2025
476536e
Make it an SSA pass, and add tests
asterite Feb 18, 2025
245abc1
Merge branch 'master' into tf/u128-hacky
asterite Feb 18, 2025
29eea20
chore: add justification for unchecked mul
TomAFrench Feb 18, 2025
db96a51
Add two more impls for u128
asterite Feb 19, 2025
21f3e64
Merge branch 'tf/u128-hacky' of github.com:noir-lang/noir into tf/u12…
asterite Feb 19, 2025
c087c4a
Make sure the not operator works well for u128
asterite Feb 19, 2025
c0b6579
Test more operations with vars that can't be optimized out
asterite Feb 19, 2025
8b6e33e
Move main to another program so it's checked
asterite Feb 19, 2025
8eb4a07
Remove dead code, replace with assertions
asterite Feb 19, 2025
cd32a03
Revert "Remove dead code, replace with assertions"
asterite Feb 19, 2025
6cad9a8
Avoid checking the same condition twice
asterite Feb 19, 2025
f4f1318
Handle overflow for u128 in euclidean_division_var
asterite Feb 19, 2025
00d6fbf
Include a few more operators
asterite Feb 19, 2025
6fb23d1
A better test
asterite Feb 19, 2025
3bc69f8
Use `power_of_two`
asterite Feb 19, 2025
4c0d27a
Make sure shifts aren't replaced with divisions
asterite Feb 19, 2025
8159a92
Let LSP know about u128
asterite Feb 19, 2025
2b6ce10
chore: nits
TomAFrench Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 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 @@ -908,19 +909,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 +935,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
19 changes: 0 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
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
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,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
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