Skip to content

Commit

Permalink
feat: optimize away constant calls to black box functions
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Aug 2, 2023
1 parent f7742ab commit 33b119c
Show file tree
Hide file tree
Showing 2 changed files with 344 additions and 152 deletions.
156 changes: 4 additions & 152 deletions crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::rc::Rc;

use acvm::{acir::BlackBoxFunc, FieldElement};
use iter_extended::vecmap;
use num_bigint::BigUint;
Expand All @@ -14,6 +12,10 @@ use super::{
value::{Value, ValueId},
};

mod call;

use call::simplify_call;

/// Reference to an instruction
///
/// Note that InstructionIds are not unique. That is, two InstructionIds
Expand Down Expand Up @@ -385,156 +387,6 @@ fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> Sim
}
}

/// Try to simplify this call instruction. If the instruction can be simplified to a known value,
/// that value is returned. Otherwise None is returned.
fn simplify_call(func: ValueId, arguments: &[ValueId], dfg: &mut DataFlowGraph) -> SimplifyResult {
use SimplifyResult::*;
let intrinsic = match &dfg[func] {
Value::Intrinsic(intrinsic) => *intrinsic,
_ => return None,
};

let constant_args: Option<Vec<_>> =
arguments.iter().map(|value_id| dfg.get_numeric_constant(*value_id)).collect();

match intrinsic {
Intrinsic::ToBits(endian) => {
if let Some(constant_args) = constant_args {
let field = constant_args[0];
let limb_count = constant_args[1].to_u128() as u32;
SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg))
} else {
None
}
}
Intrinsic::ToRadix(endian) => {
if let Some(constant_args) = constant_args {
let field = constant_args[0];
let radix = constant_args[1].to_u128() as u32;
let limb_count = constant_args[2].to_u128() as u32;
SimplifiedTo(constant_to_radix(endian, field, radix, limb_count, dfg))
} else {
None
}
}
Intrinsic::ArrayLen => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((slice, _)) = slice {
SimplifiedTo(dfg.make_constant((slice.len() as u128).into(), Type::field()))
} else if let Some(length) = dfg.try_get_array_length(arguments[0]) {
SimplifiedTo(dfg.make_constant((length as u128).into(), Type::field()))
} else {
None
}
}
Intrinsic::SlicePushBack => {
let slice = dfg.get_array_constant(arguments[0]);
if let (Some((mut slice, element_type)), elem) = (slice, arguments[1]) {
slice.push_back(elem);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedTo(new_slice)
} else {
None
}
}
Intrinsic::SlicePushFront => {
let slice = dfg.get_array_constant(arguments[0]);
if let (Some((mut slice, element_type)), elem) = (slice, arguments[1]) {
slice.push_front(elem);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedTo(new_slice)
} else {
None
}
}
Intrinsic::SlicePopBack => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((mut slice, element_type)) = slice {
let elem =
slice.pop_back().expect("There are no elements in this slice to be removed");
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![new_slice, elem])
} else {
None
}
}
Intrinsic::SlicePopFront => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((mut slice, element_type)) = slice {
let elem =
slice.pop_front().expect("There are no elements in this slice to be removed");
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![elem, new_slice])
} else {
None
}
}
Intrinsic::SliceInsert => {
let slice = dfg.get_array_constant(arguments[0]);
let index = dfg.get_numeric_constant(arguments[1]);
if let (Some((mut slice, element_type)), Some(index), value) =
(slice, index, arguments[2])
{
slice.insert(index.to_u128() as usize, value);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedTo(new_slice)
} else {
None
}
}
Intrinsic::SliceRemove => {
let slice = dfg.get_array_constant(arguments[0]);
let index = dfg.get_numeric_constant(arguments[1]);
if let (Some((mut slice, element_type)), Some(index)) = (slice, index) {
let removed_elem = slice.remove(index.to_u128() as usize);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![new_slice, removed_elem])
} else {
None
}
}
Intrinsic::BlackBox(_) | Intrinsic::Println | Intrinsic::Sort => None,
}
}

/// Returns a Value::Array of constants corresponding to the limbs of the radix decomposition.
fn constant_to_radix(
endian: Endian,
field: FieldElement,
radix: u32,
limb_count: u32,
dfg: &mut DataFlowGraph,
) -> ValueId {
let bit_size = u32::BITS - (radix - 1).leading_zeros();
let radix_big = BigUint::from(radix);
assert_eq!(BigUint::from(2u128).pow(bit_size), radix_big, "ICE: Radix must be a power of 2");
let big_integer = BigUint::from_bytes_be(&field.to_be_bytes());

// Decompose the integer into its radix digits in little endian form.
let decomposed_integer = big_integer.to_radix_le(radix);
let mut limbs = vecmap(0..limb_count, |i| match decomposed_integer.get(i as usize) {
Some(digit) => FieldElement::from_be_bytes_reduce(&[*digit]),
None => FieldElement::zero(),
});
if endian == Endian::Big {
limbs.reverse();
}

// For legacy reasons (see #617) the to_radix interface supports 256 bits even though
// FieldElement::max_num_bits() is only 254 bits. Any limbs beyond the specified count
// become zero padding.
let max_decomposable_bits: u32 = 256;
let limb_count_with_padding = max_decomposable_bits / bit_size;
while limbs.len() < limb_count_with_padding as usize {
limbs.push(FieldElement::zero());
}
let result_constants: im::Vector<ValueId> =
limbs.into_iter().map(|limb| dfg.make_constant(limb, Type::unsigned(bit_size))).collect();

let typ = Type::Array(Rc::new(vec![Type::unsigned(bit_size)]), result_constants.len());
dfg.make_array(result_constants, typ)
}

/// The possible return values for Instruction::return_types
pub(crate) enum InstructionResultType {
/// The result type of this instruction matches that of this operand
Expand Down
Loading

0 comments on commit 33b119c

Please sign in to comment.