Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: builtin wrapping shift left #3270

Merged
merged 49 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0ee3188
builtin for wrapping shift left (not working..)
guipublic Oct 20, 2023
9f5da2a
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 24, 2023
67ae3d4
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 24, 2023
c64796a
only const
guipublic Oct 24, 2023
655fba7
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 24, 2023
02df005
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 24, 2023
95dc22c
fix wrapping shift left
guipublic Oct 26, 2023
3a8e7b7
fix: Fix lexer error formatting (#3274)
jfecher Oct 24, 2023
338a322
chore: standardize workflow triggers (#3277)
TomAFrench Oct 24, 2023
3302523
chore: add constrain formatter (#3272)
Oct 24, 2023
4248a87
fix: Add size checks to integer literals (#3236)
jfecher Oct 24, 2023
5caff7a
feat: implement `bound_constraint_with_offset` in terms of `AcirVar`s…
TomAFrench Oct 24, 2023
ea88441
chore: adding fix to master branch (#3279)
signorecello Oct 24, 2023
c3be58b
feat!: expose pedersen hash in acir and bb solver (#3269)
sirasistant Oct 25, 2023
7ac8dc9
chore: turn off `aztec` flag by default (#3280)
TomAFrench Oct 25, 2023
17dbfa7
chore: Release Noir(0.18.0) (#3242)
kevaundray Oct 25, 2023
d36c159
chore: pinning NoirJS guide versions to 0.17.0 and adding note on noi…
signorecello Oct 25, 2023
ed3800e
feat: Extract Brillig VM to allow step debugging (#3259)
ggiraldez Oct 25, 2023
7730337
feat: perform compile-time euclidean division on constants (#3231)
TomAFrench Oct 25, 2023
4e29fb5
chore: update artefacts (#3287)
kevaundray Oct 25, 2023
6a98579
chore: remove `default` empty features array (#3285)
alexghr Oct 25, 2023
84ca6c1
chore(docs): Cut v0.17.0 docs (#3290)
Savio-Sou Oct 25, 2023
2106390
chore: update relative paths for docs imports (#3291)
critesjosh Oct 25, 2023
0aa1172
chore(docs): Recommend `yarn` instead of for cutting doc versions (#3…
Savio-Sou Oct 25, 2023
3b35ec1
feat: Allow a trait to be implemented multiple times for the same str…
jfecher Oct 25, 2023
71ff678
chore: format submodule/contract (#3286)
Oct 25, 2023
0de5aab
chore(github): Update PR template per new documentation workflow (#3241)
Savio-Sou Oct 25, 2023
448eaf7
fmt
guipublic Oct 26, 2023
2e5ef23
chore: add constrain formatter (#3272)
Oct 24, 2023
a9d7e82
fix: Add size checks to integer literals (#3236)
jfecher Oct 24, 2023
52c3ebc
feat!: expose pedersen hash in acir and bb solver (#3269)
sirasistant Oct 25, 2023
57bab26
chore: turn off `aztec` flag by default (#3280)
TomAFrench Oct 25, 2023
33d596b
feat: perform compile-time euclidean division on constants (#3231)
TomAFrench Oct 25, 2023
04e1870
chore: update artefacts (#3287)
kevaundray Oct 25, 2023
f31da68
chore: remove `default` empty features array (#3285)
alexghr Oct 25, 2023
85a4ed3
chore: format submodule/contract (#3286)
Oct 25, 2023
a348ead
feat: handle warnings in evaluator (#3205)
guipublic Oct 26, 2023
0cfc280
feat: `compute_note_hash_and_nullifier` check (#3216)
benesjan Oct 26, 2023
9d722bc
feat: Make generic impls callable (#3297)
jfecher Oct 26, 2023
fc15867
chore: create publish-docs.yml (#3298)
signorecello Oct 26, 2023
2a12e35
fmt
guipublic Oct 26, 2023
0ec17be
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 26, 2023
225ddd1
fmt
guipublic Oct 26, 2023
1cba7d4
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 26, 2023
1f5f9df
document wrapping_shift_left
guipublic Oct 26, 2023
4ea8bfd
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 26, 2023
33651ac
document pow
guipublic Oct 26, 2023
c361231
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 27, 2023
e0f3240
Merge branch 'master' into gd/builtin-shift-left
guipublic Oct 27, 2023
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
122 changes: 120 additions & 2 deletions compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, rc::Rc};

use acvm::FieldElement;
use noirc_errors::Location;
Expand All @@ -16,7 +16,8 @@ use super::{
basic_block::BasicBlock,
dfg::{CallStack, InsertInstructionResult},
function::RuntimeType,
instruction::{InstructionId, Intrinsic},
instruction::{Endian, InstructionId, Intrinsic},
types::NumericType,
},
ssa_gen::Ssa,
};
Expand Down Expand Up @@ -259,9 +260,126 @@ impl FunctionBuilder {
arguments: Vec<ValueId>,
result_types: Vec<Type>,
) -> Cow<[ValueId]> {
if let Value::Intrinsic(intrinsic) = &self.current_function.dfg[func] {
if intrinsic == &Intrinsic::WrappingShiftLeft {
let result_type = self.current_function.dfg.type_of_value(arguments[0]);
let bit_size = match result_type {
Type::Numeric(NumericType::Signed { bit_size })
| Type::Numeric(NumericType::Unsigned { bit_size }) => bit_size,
_ => {
unreachable!("ICE: Truncation attempted on non-integer");
}
};
return self
.insert_wrapping_shift_left(arguments[0], arguments[1], bit_size)
.results();
}
}

self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results()
}

/// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs
pub(crate) fn insert_shift_left(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let base = self.field_constant(FieldElement::from(2_u128));
let pow = self.pow(base, rhs);
let typ = self.current_function.dfg.type_of_value(lhs);
let pow = self.insert_cast(pow, typ);
self.insert_binary(lhs, BinaryOp::Mul, pow)
}

/// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs
/// and truncate the result to bit_size
fn insert_wrapping_shift_left(
&mut self,
lhs: ValueId,
rhs: ValueId,
bit_size: u32,
) -> InsertInstructionResult {
let base = self.field_constant(FieldElement::from(2_u128));
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let typ = self.current_function.dfg.type_of_value(lhs);
let (max_bit, pow) = if let Some(rhs_constant) =
self.current_function.dfg.get_numeric_constant(rhs)
{
// Happy case is that we know precisely by how many bits the the integer will
// increase: lhs_bit_size + rhs
let (rhs_bit_size_pow_2, overflows) =
2_u32.overflowing_pow(rhs_constant.to_u128() as u32);
if overflows {
let zero = self.numeric_constant(FieldElement::zero(), typ);
return InsertInstructionResult::SimplifiedTo(zero);
}
let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2 as u128), typ);
(bit_size + (rhs_constant.to_u128() as u32), pow)
} else {
// we use a predicate to nullify the result in case of overflow
let bit_size_var =
self.numeric_constant(FieldElement::from(bit_size as u128), typ.clone());
let overflow = self.insert_binary(rhs, BinaryOp::Lt, bit_size_var);
let one = self.numeric_constant(FieldElement::one(), Type::unsigned(1));
let predicate = self.insert_binary(overflow, BinaryOp::Eq, one);
let predicate = self.insert_cast(predicate, typ.clone());

let pow = self.pow(base, rhs);
let pow = self.insert_cast(pow, typ);
(FieldElement::max_num_bits(), self.insert_binary(predicate, BinaryOp::Mul, pow))
};

let instruction = Instruction::Binary(Binary { lhs, rhs: pow, operator: BinaryOp::Mul });
if max_bit <= bit_size {
self.insert_instruction(instruction, None)
} else {
let result = self.insert_instruction(instruction, None).first();
self.insert_instruction(
Instruction::Truncate { value: result, bit_size, max_bit_size: max_bit },
None,
)
}
}

/// Insert ssa instructions which computes lhs >> rhs by doing lhs/2^rhs
pub(crate) fn insert_shift_right(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let base = self.field_constant(FieldElement::from(2_u128));
let pow = self.pow(base, rhs);
self.insert_binary(lhs, BinaryOp::Div, pow)
}

/// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs
/// Pseudo-code of the computation:
guipublic marked this conversation as resolved.
Show resolved Hide resolved
/// let mut r = 1;
/// let rhs_bits = to_bits(rhs);
/// for i in 1 .. bit_size + 1 {
/// let r_squared = r * r;
/// let b = rhs_bits[bit_size - i];
/// r = (r_squared * lhs * b) + (1 - b) * r_squared;
/// }
pub(crate) fn pow(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let typ = self.current_function.dfg.type_of_value(rhs);
if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ {
let to_bits = self.import_intrinsic_id(Intrinsic::ToBits(Endian::Little));
let length = self.field_constant(FieldElement::from(bit_size as i128));
let result_types =
vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)];
let rhs_bits = self.insert_call(to_bits, vec![rhs, length], result_types);
let rhs_bits = rhs_bits[1];
let one = self.field_constant(FieldElement::one());
let mut r = one;
for i in 1..bit_size + 1 {
let r_squared = self.insert_binary(r, BinaryOp::Mul, r);
let a = self.insert_binary(r_squared, BinaryOp::Mul, lhs);
let idx = self.field_constant(FieldElement::from((bit_size - i) as i128));
let b = self.insert_array_get(rhs_bits, idx, Type::field());
let r1 = self.insert_binary(a, BinaryOp::Mul, b);
let c = self.insert_binary(one, BinaryOp::Sub, b);
let r2 = self.insert_binary(c, BinaryOp::Mul, r_squared);
r = self.insert_binary(r1, BinaryOp::Add, r2);
}
r
} else {
unreachable!("Value must be unsigned in power operation");
}
}

/// Insert an instruction to extract an element from an array
pub(crate) fn insert_array_get(
&mut self,
Expand Down
6 changes: 5 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub(crate) enum Intrinsic {
BlackBox(BlackBoxFunc),
FromField,
AsField,
WrappingShiftLeft,
}

impl std::fmt::Display for Intrinsic {
Expand All @@ -68,6 +69,7 @@ impl std::fmt::Display for Intrinsic {
Intrinsic::BlackBox(function) => write!(f, "{function}"),
Intrinsic::FromField => write!(f, "from_field"),
Intrinsic::AsField => write!(f, "as_field"),
Intrinsic::WrappingShiftLeft => write!(f, "wrapping_shift_left"),
}
}
}
Expand All @@ -92,7 +94,8 @@ impl Intrinsic {
| Intrinsic::ToBits(_)
| Intrinsic::ToRadix(_)
| Intrinsic::FromField
| Intrinsic::AsField => false,
| Intrinsic::AsField
| Intrinsic::WrappingShiftLeft => false,

// Some black box functions have side-effects
Intrinsic::BlackBox(func) => matches!(func, BlackBoxFunc::RecursiveAggregation),
Expand All @@ -119,6 +122,7 @@ impl Intrinsic {
"to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)),
"from_field" => Some(Intrinsic::FromField),
"as_field" => Some(Intrinsic::AsField),
"wrapping_shift_left" => Some(Intrinsic::WrappingShiftLeft),
other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox),
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ pub(super) fn simplify_call(
let instruction = Instruction::Cast(arguments[0], ctrl_typevars.unwrap().remove(0));
SimplifyResult::SimplifiedToInstruction(instruction)
}
Intrinsic::WrappingShiftLeft => {
unreachable!("ICE - wrapping shift left should have been proccessed before")
}
}
}

Expand Down
50 changes: 3 additions & 47 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::ssa::function_builder::FunctionBuilder;
use crate::ssa::ir::dfg::DataFlowGraph;
use crate::ssa::ir::function::FunctionId as IrFunctionId;
use crate::ssa::ir::function::{Function, RuntimeType};
use crate::ssa::ir::instruction::{BinaryOp, Endian, Intrinsic};
use crate::ssa::ir::instruction::BinaryOp;
use crate::ssa::ir::map::AtomicCounter;
use crate::ssa::ir::types::{NumericType, Type};
use crate::ssa::ir::value::ValueId;
Expand Down Expand Up @@ -265,50 +265,6 @@ impl<'a> FunctionContext<'a> {
Ok(self.builder.numeric_constant(value, typ))
}

/// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs
fn insert_shift_left(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let base = self.builder.field_constant(FieldElement::from(2_u128));
let pow = self.pow(base, rhs);
let typ = self.builder.current_function.dfg.type_of_value(lhs);
let pow = self.builder.insert_cast(pow, typ);
self.builder.insert_binary(lhs, BinaryOp::Mul, pow)
}

/// Insert ssa instructions which computes lhs >> rhs by doing lhs/2^rhs
fn insert_shift_right(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let base = self.builder.field_constant(FieldElement::from(2_u128));
let pow = self.pow(base, rhs);
self.builder.insert_binary(lhs, BinaryOp::Div, pow)
}

/// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs
fn pow(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
let typ = self.builder.current_function.dfg.type_of_value(rhs);
if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ {
let to_bits = self.builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little));
let length = self.builder.field_constant(FieldElement::from(bit_size as i128));
let result_types =
vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)];
let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types);
let rhs_bits = rhs_bits[1];
let one = self.builder.field_constant(FieldElement::one());
let mut r = one;
for i in 1..bit_size + 1 {
let r1 = self.builder.insert_binary(r, BinaryOp::Mul, r);
let a = self.builder.insert_binary(r1, BinaryOp::Mul, lhs);
let idx = self.builder.field_constant(FieldElement::from((bit_size - i) as i128));
let b = self.builder.insert_array_get(rhs_bits, idx, Type::field());
let r2 = self.builder.insert_binary(a, BinaryOp::Mul, b);
let c = self.builder.insert_binary(one, BinaryOp::Sub, b);
let r3 = self.builder.insert_binary(c, BinaryOp::Mul, r1);
r = self.builder.insert_binary(r2, BinaryOp::Add, r3);
}
r
} else {
unreachable!("Value must be unsigned in power operation");
}
}

/// 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.
Expand All @@ -321,8 +277,8 @@ impl<'a> FunctionContext<'a> {
location: Location,
) -> Values {
let mut result = match operator {
BinaryOpKind::ShiftLeft => self.insert_shift_left(lhs, rhs),
BinaryOpKind::ShiftRight => self.insert_shift_right(lhs, rhs),
BinaryOpKind::ShiftLeft => self.builder.insert_shift_left(lhs, rhs),
BinaryOpKind::ShiftRight => self.builder.insert_shift_right(lhs, rhs),
BinaryOpKind::Equal | BinaryOpKind::NotEqual
if matches!(self.builder.type_of_value(lhs), Type::Array(..)) =>
{
Expand Down
7 changes: 4 additions & 3 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub fn wrapping_mul<T>(x : T, y: T) -> T {
crate::from_field(crate::as_field(x) * crate::as_field(y))
}

pub fn wrapping_shift_left<T>(x : T, y: T) -> T {
crate::from_field(crate::as_field(x) * 2.pow_32(crate::as_field(y)))
}
guipublic marked this conversation as resolved.
Show resolved Hide resolved
/// Shift-left x by y bits
/// If the result overflow the bitsize; it does not fail and returns 0 instead
#[builtin(wrapping_shift_left)]
pub fn wrapping_shift_left<T>(x : T, y: T) -> T {}
2 changes: 1 addition & 1 deletion noir_stdlib/src/sha256.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn rotr32(a: u32, b: u32) -> u32 // 32-bit right rotation
{
// None of the bits overlap between `(a >> b)` and `(a << (32 - b))`
// Addition is then equivalent to OR, with fewer constraints.
(a >> b) + (a << (32 - b))
(a >> b) + (crate::wrapping_shift_left(a, 32 - b))
}

fn ch(x: u32, y: u32, z: u32) -> u32
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/sha512.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn rotr64(a: u64, b: u64) -> u64 // 64-bit right rotation
{
// None of the bits overlap between `(a >> b)` and `(a << (64 - b))`
// Addition is then equivalent to OR, with fewer constraints.
(a >> b) + (a << (64 - b))
(a >> b) + (crate::wrapping_shift_left(a, 64 - b))
}

fn sha_ch(x: u64, y: u64, z: u64) -> u64
Expand Down
Loading