Skip to content

Commit

Permalink
feat: add wrapping functions in stdlib and use them in relevant test …
Browse files Browse the repository at this point in the history
…cases (#2725)
  • Loading branch information
guipublic authored Sep 20, 2023
1 parent 8617008 commit 49ab121
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,10 @@ impl GeneratedAcir {
if let Some(rhs_const) = rhs.to_const() {
max_rhs_bits = rhs_const.num_bits();
if max_rhs_bits != 0 {
if max_rhs_bits > max_bit_size {
let zero = self.get_or_create_witness(&Expression::zero());
return Ok((zero, zero));
}
max_q_bits = max_bit_size - max_rhs_bits + 1;
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl DataFlowGraph {
call_stack: CallStack,
) -> InsertInstructionResult {
use InsertInstructionResult::*;
match instruction.simplify(self, block) {
match instruction.simplify(self, block, ctrl_typevars.clone()) {
SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification),
SimplifyResult::SimplifiedToMultiple(simplification) => {
SimplifiedToMultiple(simplification)
Expand Down
21 changes: 18 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub(crate) enum Intrinsic {
ToBits(Endian),
ToRadix(Endian),
BlackBox(BlackBoxFunc),
FromField,
AsField,
}

impl std::fmt::Display for Intrinsic {
Expand All @@ -64,6 +66,8 @@ impl std::fmt::Display for Intrinsic {
Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"),
Intrinsic::ToRadix(Endian::Little) => write!(f, "to_le_radix"),
Intrinsic::BlackBox(function) => write!(f, "{function}"),
Intrinsic::FromField => write!(f, "from_field"),
Intrinsic::AsField => write!(f, "as_field"),
}
}
}
Expand All @@ -86,7 +90,9 @@ impl Intrinsic {
| Intrinsic::SliceRemove
| Intrinsic::StrAsBytes
| Intrinsic::ToBits(_)
| Intrinsic::ToRadix(_) => false,
| Intrinsic::ToRadix(_)
| Intrinsic::FromField
| Intrinsic::AsField => false,

// Some black box functions have side-effects
Intrinsic::BlackBox(func) => matches!(func, BlackBoxFunc::RecursiveAggregation),
Expand All @@ -111,6 +117,8 @@ impl Intrinsic {
"to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)),
"to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)),
"to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)),
"from_field" => Some(Intrinsic::FromField),
"as_field" => Some(Intrinsic::AsField),
other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox),
}
}
Expand Down Expand Up @@ -357,7 +365,12 @@ impl Instruction {
///
/// The `block` parameter indicates the block this new instruction will be inserted into
/// after this call.
pub(crate) fn simplify(&self, dfg: &mut DataFlowGraph, block: BasicBlockId) -> SimplifyResult {
pub(crate) fn simplify(
&self,
dfg: &mut DataFlowGraph,
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
) -> SimplifyResult {
use SimplifyResult::*;
match self {
Instruction::Binary(binary) => binary.simplify(dfg),
Expand Down Expand Up @@ -425,7 +438,9 @@ impl Instruction {
None
}
}
Instruction::Call { func, arguments } => simplify_call(*func, arguments, dfg, block),
Instruction::Call { func, arguments } => {
simplify_call(*func, arguments, dfg, block, ctrl_typevars)
}
Instruction::EnableSideEffects { condition } => {
if let Some(last) = dfg[block].instructions().last().copied() {
let last = &mut dfg[last];
Expand Down
12 changes: 12 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub(super) fn simplify_call(
arguments: &[ValueId],
dfg: &mut DataFlowGraph,
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
) -> SimplifyResult {
let intrinsic = match &dfg[func] {
Value::Intrinsic(intrinsic) => *intrinsic,
Expand Down Expand Up @@ -223,6 +224,17 @@ pub(super) fn simplify_call(
}
Intrinsic::BlackBox(bb_func) => simplify_black_box_func(bb_func, arguments, dfg),
Intrinsic::Sort => simplify_sort(dfg, arguments),
Intrinsic::AsField => {
let instruction = Instruction::Cast(
arguments[0],
Type::Numeric(crate::ssa::ir::types::NumericType::NativeField),
);
SimplifyResult::SimplifiedToInstruction(instruction)
}
Intrinsic::FromField => {
let instruction = Instruction::Cast(arguments[0], ctrl_typevars.unwrap().remove(0));
SimplifyResult::SimplifiedToInstruction(instruction)
}
}
}

Expand Down
25 changes: 25 additions & 0 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,28 @@ fn verify_proof<N>(_verification_key : [Field], _proof : [Field], _public_inputs
// Useful for debugging for-loop bounds.
#[builtin(assert_constant)]
fn assert_constant<T>(_x: T) {}

#[builtin(from_field)]
fn from_field<T>(x : Field) -> T {}

#[builtin(as_field)]
fn as_field<T>(x : T) -> Field {}


fn wrapping_add<T>(x : T, y: T) -> T {
crate::from_field(crate::as_field(x) + crate::as_field(y))
}


fn wrapping_sub<T>(x : T, y: T) -> T {
//340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow
crate::from_field(crate::as_field(x) + 340282366920938463463374607431768211456 - crate::as_field(y))
}

fn wrapping_mul<T>(x : T, y: T) -> T {
crate::from_field(crate::as_field(x) * crate::as_field(y))
}

fn wrapping_shift_left<T>(x : T, y: T) -> T {
crate::from_field(crate::as_field(x) * 2.pow_32(crate::as_field(y)))
}
3 changes: 2 additions & 1 deletion tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use dep::std;
// Test unsafe integer subtraction with underflow: 12 - 2418266113 = 1876701195 modulo 2^32
fn main(mut x: u32, y: u32, z: u32) {
x -= y;
x = std::wrapping_sub(x,y);
assert(x == z);

// Test constant underflow (regression for #2045)
Expand Down
4 changes: 3 additions & 1 deletion tooling/nargo_cli/tests/execution_success/5_over/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use dep::std;

// Test unsafe integer arithmetic
// Test odd bits integer
fn main(mut x: u32, y: u32) {
x = x * x;
x = std::wrapping_mul(x,x);
assert(y == x);

let c:u3 = 2;
Expand Down
7 changes: 4 additions & 3 deletions tooling/nargo_cli/tests/execution_success/6_array/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use dep::std;
//Basic tests for arrays
fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) {
let mut c = 2301;
Expand All @@ -13,8 +14,8 @@ fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) {
c = 2301 as u32;
for i in 0..5 {
c = t+2 as u32;
c = z*z*x[i];
z += x[i]*y[i] - c;
c = std::wrapping_mul(std::wrapping_mul(z,z),x[i]);
z =std::wrapping_add(z, std::wrapping_sub(x[i]*y[i] , c));
}
assert(z == 3814912846);

Expand All @@ -25,7 +26,7 @@ fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) {
z = z + x[i]*y[i];
for _i in 0..3 {
c = i as u32 - 2 as u32;
z *= c;
z = std::wrapping_mul(z,c);
}
}
assert(z == 41472);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use dep::std;
// Testing signed integer division:
// 7/3 = 2
// -7/3 = -2
Expand All @@ -8,9 +9,9 @@ fn main(mut x: i32, mut y: i32, mut z: i32) {
assert(x / y == z);

// -7/3 = -2
let minus_x = 0-x;
let minus_z = 0-z;
let minus_y = 0-y;
let minus_x = std::wrapping_sub(0,x);
let minus_z = std::wrapping_sub(0,z);
let minus_y = std::wrapping_sub(0,y);
assert(x+minus_x == 0);
assert(z+minus_z == 0);
assert(minus_x / y == minus_z);
Expand Down

0 comments on commit 49ab121

Please sign in to comment.