diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index a82c54d8ce..e7fa601fbe 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -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)?; diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index f531e8307f..8f1b360a12 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -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, }, @@ -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, @@ -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. @@ -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, @@ -184,6 +204,10 @@ impl DataFlowGraph { ctrl_typevars: Option>, 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, @@ -194,7 +218,8 @@ 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, @@ -202,6 +227,9 @@ impl DataFlowGraph { ctrl_typevars: Option>, 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) diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 6413107c04..109c2a5978 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -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 /// @@ -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, @@ -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 } @@ -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 { diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index a66e272fb0..aadd35040c 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -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: diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index ec7a8e2524..39c63e3efc 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -20,10 +20,10 @@ pub(crate) type ValueId = Id; 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. diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index d16edd1355..9f533bd040 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -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}, }, @@ -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); diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 64f6e2ddfe..e25ad35014 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -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()); @@ -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())); diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index afada8c0e2..fcaaf74f53 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -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;