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

feat: Sync from noir #5234

Merged
merged 5 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
283 changes: 249 additions & 34 deletions noir/noir-repo/Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use noirc_frontend::{
},
macros_api::{FileId, HirContext, MacroError},
node_interner::FuncId,
parse_program, FunctionReturnType, NoirFunction, UnresolvedTypeData,
parse_program, FunctionReturnType, ItemVisibility, NoirFunction, UnresolvedTypeData,
};

use crate::utils::hir_utils::fetch_struct_trait_impls;
Expand Down Expand Up @@ -113,7 +113,7 @@ pub fn inject_compute_note_hash_and_nullifier(
context.def_map_mut(crate_id).unwrap()
.modules_mut()[module_id.0]
.declare_function(
func.name_ident().clone(), func_id
func.name_ident().clone(), ItemVisibility::Public, func_id
).expect(
"Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647."
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ impl<'block> BrilligBlock<'block> {
dfg,
);
}
_ => {
todo!("ICE: Param type not supported")
}
Type::Function => todo!("ICE: Type::Function Param not supported"),
}
}
}
Expand Down Expand Up @@ -658,15 +656,29 @@ impl<'block> BrilligBlock<'block> {
let rc_register = match self.convert_ssa_value(*value, dfg) {
BrilligVariable::BrilligArray(BrilligArray { rc, .. })
| BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc,
_ => unreachable!("ICE: increment rc on non-array"),
other => unreachable!("ICE: increment rc on non-array: {other:?}"),
};
self.brillig_context.codegen_usize_op_in_place(
rc_register,
BrilligBinaryOp::Add,
1,
);
}
_ => todo!("ICE: Instruction not supported {instruction:?}"),
Instruction::DecrementRc { value } => {
let rc_register = match self.convert_ssa_value(*value, dfg) {
BrilligVariable::BrilligArray(BrilligArray { rc, .. })
| BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc,
other => unreachable!("ICE: decrement rc on non-array: {other:?}"),
};
self.brillig_context.codegen_usize_op_in_place(
rc_register,
BrilligBinaryOp::Sub,
1,
);
}
Instruction::EnableSideEffects { .. } => {
todo!("enable_side_effects not supported by brillig")
}
};

let dead_variables = self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ impl Context {
Instruction::Load { .. } => {
unreachable!("Expected all load instructions to be removed before acir_gen")
}
Instruction::IncrementRc { .. } => {
Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } => {
// Do nothing. Only Brillig needs to worry about reference counted arrays
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,20 @@ impl FunctionBuilder {
/// within the given value. If the given value is not an array and does not contain
/// any arrays, this does nothing.
pub(crate) fn increment_array_reference_count(&mut self, value: ValueId) {
self.update_array_reference_count(value, true);
}

/// Insert instructions to decrement the reference count of any array(s) stored
/// within the given value. If the given value is not an array and does not contain
/// any arrays, this does nothing.
pub(crate) fn decrement_array_reference_count(&mut self, value: ValueId) {
self.update_array_reference_count(value, false);
}

/// Increment or decrement the given value's reference count if it is an array.
/// If it is not an array, this does nothing. Note that inc_rc and dec_rc instructions
/// are ignored outside of unconstrained code.
pub(crate) fn update_array_reference_count(&mut self, value: ValueId, increment: bool) {
match self.type_of_value(value) {
Type::Numeric(_) => (),
Type::Function => (),
Expand All @@ -396,7 +410,12 @@ impl FunctionBuilder {
typ @ Type::Array(..) | typ @ Type::Slice(..) => {
// If there are nested arrays or slices, we wait until ArrayGet
// is issued to increment the count of that array.
self.insert_instruction(Instruction::IncrementRc { value }, None);
let instruction = if increment {
Instruction::IncrementRc { value }
} else {
Instruction::DecrementRc { value }
};
self.insert_instruction(instruction, None);

// This is a bit odd, but in brillig the inc_rc instruction operates on
// a copy of the array's metadata, so we need to re-store a loaded array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ pub(crate) enum Instruction {
/// implemented via reference counting. In ACIR code this is done with im::Vector and these
/// IncrementRc instructions are ignored.
IncrementRc { value: ValueId },

/// An instruction to decrement the reference count of a value.
///
/// This currently only has an effect in Brillig code where array sharing and copy on write is
/// implemented via reference counting. In ACIR code this is done with im::Vector and these
/// DecrementRc instructions are ignored.
DecrementRc { value: ValueId },
}

impl Instruction {
Expand All @@ -214,6 +221,7 @@ impl Instruction {
Instruction::Constrain(..)
| Instruction::Store { .. }
| Instruction::IncrementRc { .. }
| Instruction::DecrementRc { .. }
| Instruction::RangeCheck { .. }
| Instruction::EnableSideEffects { .. } => InstructionResultType::None,
Instruction::Allocate { .. }
Expand Down Expand Up @@ -250,6 +258,7 @@ impl Instruction {
| Load { .. }
| Store { .. }
| IncrementRc { .. }
| DecrementRc { .. }
| RangeCheck { .. } => false,

Call { func, .. } => match dfg[*func] {
Expand Down Expand Up @@ -285,6 +294,7 @@ impl Instruction {
| Store { .. }
| EnableSideEffects { .. }
| IncrementRc { .. }
| DecrementRc { .. }
| RangeCheck { .. } => true,

// Some `Intrinsic`s have side effects so we must check what kind of `Call` this is.
Expand Down Expand Up @@ -353,6 +363,7 @@ impl Instruction {
Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) }
}
Instruction::IncrementRc { value } => Instruction::IncrementRc { value: f(*value) },
Instruction::DecrementRc { value } => Instruction::DecrementRc { value: f(*value) },
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
Instruction::RangeCheck {
value: f(*value),
Expand Down Expand Up @@ -409,7 +420,9 @@ impl Instruction {
Instruction::EnableSideEffects { condition } => {
f(*condition);
}
Instruction::IncrementRc { value } | Instruction::RangeCheck { value, .. } => {
Instruction::IncrementRc { value }
| Instruction::DecrementRc { value }
| Instruction::RangeCheck { value, .. } => {
f(*value);
}
}
Expand Down Expand Up @@ -554,6 +567,7 @@ impl Instruction {
Instruction::Load { .. } => None,
Instruction::Store { .. } => None,
Instruction::IncrementRc { .. } => None,
Instruction::DecrementRc { .. } => None,
Instruction::RangeCheck { value, max_bit_size, .. } => {
if let Some(numeric_constant) = dfg.get_numeric_constant(*value) {
if numeric_constant.num_bits() < *max_bit_size {
Expand Down
3 changes: 3 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ fn display_instruction_inner(
Instruction::IncrementRc { value } => {
writeln!(f, "inc_rc {}", show(*value))
}
Instruction::DecrementRc { value } => {
writeln!(f, "dec_rc {}", show(*value))
}
Instruction::RangeCheck { value, max_bit_size, .. } => {
writeln!(f, "range_check {} to {} bits", show(*value), *max_bit_size,)
}
Expand Down
26 changes: 15 additions & 11 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn dead_instruction_elimination(function: &mut Function) {
context.remove_unused_instructions_in_block(function, *block);
}

context.remove_increment_rc_instructions(&mut function.dfg);
context.remove_rc_instructions(&mut function.dfg);
}

/// Per function context for tracking unused values and which instructions to remove.
Expand All @@ -53,10 +53,10 @@ struct Context {
used_values: HashSet<ValueId>,
instructions_to_remove: HashSet<InstructionId>,

/// IncrementRc instructions must be revisited after the main DIE pass since
/// IncrementRc & DecrementRc instructions must be revisited after the main DIE pass since
/// they technically contain side-effects but we still want to remove them if their
/// `value` parameter is not used elsewhere.
increment_rc_instructions: Vec<(InstructionId, BasicBlockId)>,
rc_instructions: Vec<(InstructionId, BasicBlockId)>,
}

impl Context {
Expand Down Expand Up @@ -85,8 +85,9 @@ impl Context {
} else {
let instruction = &function.dfg[*instruction_id];

if let Instruction::IncrementRc { .. } = instruction {
self.increment_rc_instructions.push((*instruction_id, block_id));
use Instruction::*;
if matches!(instruction, IncrementRc { .. } | DecrementRc { .. }) {
self.rc_instructions.push((*instruction_id, block_id));
} else {
instruction.for_each_value(|value| {
self.mark_used_instruction_results(&function.dfg, value);
Expand Down Expand Up @@ -145,16 +146,19 @@ impl Context {
}
}

fn remove_increment_rc_instructions(self, dfg: &mut DataFlowGraph) {
for (increment_rc, block) in self.increment_rc_instructions {
let value = match &dfg[increment_rc] {
fn remove_rc_instructions(self, dfg: &mut DataFlowGraph) {
for (rc, block) in self.rc_instructions {
let value = match &dfg[rc] {
Instruction::IncrementRc { value } => *value,
other => unreachable!("Expected IncrementRc instruction, found {other:?}"),
Instruction::DecrementRc { value } => *value,
other => {
unreachable!("Expected IncrementRc or DecrementRc instruction, found {other:?}")
}
};

// This could be more efficient if we have to remove multiple IncrementRcs in a single block
// This could be more efficient if we have to remove multiple instructions in a single block
if !self.used_values.contains(&value) {
dfg[block].instructions_mut().retain(|instruction| *instruction != increment_rc);
dfg[block].instructions_mut().retain(|instruction| *instruction != rc);
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use noirc_frontend::{BinaryOpKind, Signedness};

use crate::errors::RuntimeError;
use crate::ssa::function_builder::FunctionBuilder;
use crate::ssa::ir::basic_block::BasicBlockId;
use crate::ssa::ir::dfg::DataFlowGraph;
use crate::ssa::ir::function::FunctionId as IrFunctionId;
use crate::ssa::ir::function::{Function, RuntimeType};
Expand Down Expand Up @@ -1022,6 +1023,36 @@ impl<'a> FunctionContext<'a> {
}
}
}

/// Increments the reference count of all parameters. Returns the entry block of the function.
///
/// This is done on parameters rather than call arguments so that we can optimize out
/// paired inc/dec instructions within brillig functions more easily.
pub(crate) fn increment_parameter_rcs(&mut self) -> BasicBlockId {
let entry = self.builder.current_function.entry_block();
let parameters = self.builder.current_function.dfg.block_parameters(entry).to_vec();

for parameter in parameters {
self.builder.increment_array_reference_count(parameter);
}

entry
}

/// Ends a local scope of a function.
/// This will issue DecrementRc instructions for any arrays in the given starting scope
/// block's parameters. Arrays that are also used in terminator instructions for the scope are
/// ignored.
pub(crate) fn end_scope(&mut self, scope: BasicBlockId, terminator_args: &[ValueId]) {
let mut dropped_parameters =
self.builder.current_function.dfg.block_parameters(scope).to_vec();

dropped_parameters.retain(|parameter| !terminator_args.contains(parameter));

for parameter in dropped_parameters {
self.builder.decrement_array_reference_count(parameter);
}
}
}

/// True if the given operator cannot be encoded directly and needs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,11 @@ impl<'a> FunctionContext<'a> {
/// Codegen a function's body and set its return value to that of its last parameter.
/// For functions returning nothing, this will be an empty list.
fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> {
let entry_block = self.increment_parameter_rcs();
let return_value = self.codegen_expression(body)?;
let results = return_value.into_value_list(self);
self.end_scope(entry_block, &results);

self.builder.terminate_with_return(results);
Ok(())
}
Expand Down Expand Up @@ -595,10 +598,8 @@ impl<'a> FunctionContext<'a> {
arguments.append(&mut values);
}

// If an array is passed as an argument we increase its reference count
for argument in &arguments {
self.builder.increment_array_reference_count(*argument);
}
// Don't need to increment array reference counts when passed in as arguments
// since it is done within the function to each parameter already.

self.codegen_intrinsic_call_checks(function, &arguments, call.location);
Ok(self.insert_call(function, arguments, &call.return_type, call.location))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use crate::{
macros_api::MacroProcessor,
node_interner::{FunctionModifiers, TraitId, TypeAliasId},
parser::{SortedModule, SortedSubModule},
FunctionDefinition, Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct,
NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl,
FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction,
NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl,
};

use super::{
Expand Down Expand Up @@ -232,6 +232,7 @@ impl<'a> ModCollector<'a> {

let name = function.name_ident().clone();
let func_id = context.def_interner.push_empty_fn();
let visibility = function.def.visibility;

// First create dummy function in the DefInterner
// So that we can get a FuncId
Expand All @@ -248,7 +249,7 @@ impl<'a> ModCollector<'a> {

// Add function to scope/ns of the module
let result = self.def_collector.def_map.modules[self.module_id.0]
.declare_function(name, func_id);
.declare_function(name, visibility, func_id);

if let Err((first_def, second_def)) = result {
let error = DefCollectorErrorKind::Duplicate {
Expand Down Expand Up @@ -407,7 +408,7 @@ impl<'a> ModCollector<'a> {

let modifiers = FunctionModifiers {
name: name.to_string(),
visibility: crate::ItemVisibility::Public,
visibility: ItemVisibility::Public,
// TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629
attributes: crate::token::Attributes::empty(),
is_unconstrained: false,
Expand All @@ -419,7 +420,7 @@ impl<'a> ModCollector<'a> {
.push_function_definition(func_id, modifiers, trait_id.0, location);

match self.def_collector.def_map.modules[trait_id.0.local_id.0]
.declare_function(name.clone(), func_id)
.declare_function(name.clone(), ItemVisibility::Public, func_id)
{
Ok(()) => {
if let Some(body) = body {
Expand Down
Loading
Loading