Skip to content

Commit

Permalink
feat(ssa): Immediately simplify away RefCount instructions in ACIR fu…
Browse files Browse the repository at this point in the history
…nctions (#6893)

Co-authored-by: Tom French <[email protected]>
Co-authored-by: Tom French <[email protected]>
Co-authored-by: Maxim Vezenov <[email protected]>
  • Loading branch information
4 people authored and Rumata888 committed Jan 8, 2025
1 parent b728151 commit 6c60f92
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 16 deletions.
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/acir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,8 @@ impl<'a> Context<'a> {
unreachable!("Expected all load instructions to be removed before acir_gen")
}
Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } => {
// Do nothing. Only Brillig needs to worry about reference counted arrays
// Only Brillig needs to worry about reference counted arrays
unreachable!("Expected all Rc instructions to be removed before acir_gen")
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
let acir_var = self.convert_numeric_value(*value, dfg)?;
Expand Down
34 changes: 31 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyR
use super::{
basic_block::{BasicBlock, BasicBlockId},
call_stack::{CallStack, CallStackHelper, CallStackId},
function::FunctionId,
function::{FunctionId, RuntimeType},
instruction::{
Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction,
},
Expand All @@ -26,8 +26,13 @@ use serde_with::DisplayFromStr;
/// owning most data in a function and handing out Ids to this data that can be
/// shared without worrying about ownership.
#[serde_as]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub(crate) struct DataFlowGraph {
/// Runtime of the [Function] that owns this [DataFlowGraph].
/// This might change during the `runtime_separation` pass where
/// ACIR functions are cloned as Brillig functions.
runtime: RuntimeType,

/// All of the instructions in a function
instructions: DenseMap<Instruction>,

Expand Down Expand Up @@ -100,6 +105,16 @@ pub(crate) struct DataFlowGraph {
}

impl DataFlowGraph {
/// Runtime type of the function.
pub(crate) fn runtime(&self) -> RuntimeType {
self.runtime
}

/// Set runtime type of the function.
pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) {
self.runtime = runtime;
}

/// Creates a new basic block with no parameters.
/// After being created, the block is unreachable in the current function
/// until another block is made to jump to it.
Expand Down Expand Up @@ -164,6 +179,11 @@ impl DataFlowGraph {
id
}

/// Check if the function runtime would simply ignore this instruction.
pub(crate) fn is_handled_by_runtime(&self, instruction: &Instruction) -> bool {
!(self.runtime().is_acir() && instruction.is_brillig_only())
}

fn insert_instruction_without_simplification(
&mut self,
instruction_data: Instruction,
Expand All @@ -184,6 +204,10 @@ impl DataFlowGraph {
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction_data) {
return InsertInstructionResult::InstructionRemoved;
}

let id = self.insert_instruction_without_simplification(
instruction_data,
block,
Expand All @@ -194,14 +218,18 @@ impl DataFlowGraph {
InsertInstructionResult::Results(id, self.instruction_results(id))
}

/// Inserts a new instruction at the end of the given block and returns its results
/// Simplifies a new instruction and inserts it at the end of the given block and returns its results.
/// If the instruction is not handled by the current runtime, `InstructionRemoved` is returned.
pub(crate) fn insert_instruction_and_results(
&mut self,
instruction: Instruction,
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction) {
return InsertInstructionResult::InstructionRemoved;
}
match instruction.simplify(self, block, ctrl_typevars.clone(), call_stack) {
SimplifyResult::SimplifiedTo(simplification) => {
InsertInstructionResult::SimplifiedTo(simplification)
Expand Down
18 changes: 11 additions & 7 deletions compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ impl RuntimeType {
}
}

impl Default for RuntimeType {
fn default() -> Self {
RuntimeType::Acir(InlineType::default())
}
}

/// A function holds a list of instructions.
/// These instructions are further grouped into Basic blocks
///
Expand All @@ -72,8 +78,6 @@ pub(crate) struct Function {

id: FunctionId,

runtime: RuntimeType,

/// The DataFlowGraph holds the majority of data pertaining to the function
/// including its blocks, instructions, and values.
pub(crate) dfg: DataFlowGraph,
Expand All @@ -86,20 +90,20 @@ impl Function {
pub(crate) fn new(name: String, id: FunctionId) -> Self {
let mut dfg = DataFlowGraph::default();
let entry_block = dfg.make_block();
Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir(InlineType::default()) }
Self { name, id, entry_block, dfg }
}

/// Creates a new function as a clone of the one passed in with the passed in id.
pub(crate) fn clone_with_id(id: FunctionId, another: &Function) -> Self {
let dfg = another.dfg.clone();
let entry_block = another.entry_block;
Self { name: another.name.clone(), id, entry_block, dfg, runtime: another.runtime }
Self { name: another.name.clone(), id, entry_block, dfg }
}

/// Takes the signature (function name & runtime) from a function but does not copy the body.
pub(crate) fn clone_signature(id: FunctionId, another: &Function) -> Self {
let mut new_function = Function::new(another.name.clone(), id);
new_function.runtime = another.runtime;
new_function.set_runtime(another.runtime());
new_function
}

Expand All @@ -116,12 +120,12 @@ impl Function {

/// Runtime type of the function.
pub(crate) fn runtime(&self) -> RuntimeType {
self.runtime
self.dfg.runtime()
}

/// Set runtime type of the function.
pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) {
self.runtime = runtime;
self.dfg.set_runtime(runtime);
}

pub(crate) fn is_no_predicates(&self) -> bool {
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,12 @@ impl Instruction {
Instruction::Noop => Remove,
}
}

/// Some instructions are only to be used in Brillig and should be eliminated
/// after runtime separation, never to be be reintroduced in an ACIR runtime.
pub(crate) fn is_brillig_only(&self) -> bool {
matches!(self, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. })
}
}

/// Given a chain of operations like:
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ir/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ pub(crate) type ValueId = Id<Value>;
pub(crate) enum Value {
/// This value was created due to an instruction
///
/// instruction -- This is the instruction which defined it
/// typ -- This is the `Type` of the instruction
/// position -- Returns the position in the results
/// vector that this `Value` is located.
/// * `instruction`: This is the instruction which defined it
/// * `typ`: This is the `Type` of the instruction
/// * `position`: Returns the position in the results vector that this `Value` is located.
///
/// Example, if you add two numbers together, then the resulting
/// value would have position `0`, the typ would be the type
/// of the operands, and the instruction would map to an add instruction.
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,9 +836,12 @@ fn simplify(dfg: &DataFlowGraph, lhs: ValueId, rhs: ValueId) -> Option<(ValueId,
mod test {
use std::sync::Arc;

use noirc_frontend::monomorphization::ast::InlineType;

use crate::ssa::{
function_builder::FunctionBuilder,
ir::{
function::RuntimeType,
map::Id,
types::{NumericType, Type},
},
Expand Down Expand Up @@ -1159,6 +1162,7 @@ mod test {

// Compiling main
let mut builder = FunctionBuilder::new("main".into(), main_id);
builder.set_runtime(RuntimeType::Brillig(InlineType::default()));
let v0 = builder.add_parameter(Type::unsigned(64));
let zero = builder.numeric_constant(0u128, NumericType::unsigned(64));
let typ = Type::Array(Arc::new(vec![Type::unsigned(64)]), 25);
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ mod test {
// }
let main_id = Id::test_new(0);
let mut builder = FunctionBuilder::new("mutator".into(), main_id);
builder.set_runtime(RuntimeType::Brillig(InlineType::default()));

let array_type = Type::Array(Arc::new(vec![Type::field()]), 2);
let v0 = builder.add_parameter(array_type.clone());
Expand Down Expand Up @@ -295,6 +296,7 @@ mod test {
// }
let main_id = Id::test_new(0);
let mut builder = FunctionBuilder::new("mutator2".into(), main_id);
builder.set_runtime(RuntimeType::Brillig(InlineType::default()));

let array_type = Type::Array(Arc::new(vec![Type::field()]), 2);
let reference_type = Type::Reference(Arc::new(array_type.clone()));
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ impl Translator {
let main_function = parsed_ssa.functions.remove(0);
let main_id = FunctionId::test_new(0);
let mut builder = FunctionBuilder::new(main_function.external_name.clone(), main_id);
builder.simplify = simplify;
builder.set_runtime(main_function.runtime_type);
builder.simplify = simplify;

// Map function names to their IDs so calls can be resolved
let mut function_id_counter = 1;
Expand Down

0 comments on commit 6c60f92

Please sign in to comment.