diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 4d1483913e..a2068d9466 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -76,7 +76,7 @@ pub(crate) struct Function { /// Name of the function for debugging only name: String, - id: FunctionId, + id: Option, /// The DataFlowGraph holds the majority of data pertaining to the function /// including its blocks, instructions, and values. @@ -90,14 +90,22 @@ 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 } + Self { name, id: Some(id), entry_block, dfg } + } + + /// Globals are generated using the same codegen process as functions. + /// To avoid a recursive global context we should create a pseudo function to mock a globals context. + pub(crate) fn new_for_globals() -> Self { + let mut dfg = DataFlowGraph::default(); + let entry_block = dfg.make_block(); + Self { name: "globals".to_owned(), id: None, 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 } + Self { name: another.name.clone(), id: Some(id), entry_block, dfg } } /// Takes the signature (function name & runtime) from a function but does not copy the body. @@ -115,7 +123,7 @@ impl Function { /// The id of the function. pub(crate) fn id(&self) -> FunctionId { - self.id + self.id.expect("FunctionId should be initialized") } /// Runtime type of the function. diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index bdd289a655..7fe12b83ea 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -20,13 +20,13 @@ use super::{ impl Display for Ssa { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - for (id, global_value) in self.globals.values_iter() { + for (id, global_value) in self.globals.dfg.values_iter() { match global_value { Value::NumericConstant { constant, typ } => { writeln!(f, "g{} = {typ} {constant}", id.to_u32())?; } Value::Instruction { instruction, .. } => { - display_instruction(&self.globals, *instruction, true, f)?; + display_instruction(&self.globals.dfg, *instruction, true, f)?; } Value::Global(_) => { panic!("Value::Global should only be in the function dfg"); @@ -35,7 +35,7 @@ impl Display for Ssa { }; } - if self.globals.values_iter().len() > 0 { + if self.globals.dfg.values_iter().len() > 0 { writeln!(f)?; } diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index c61ff208b1..eb3c93823b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -12,7 +12,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, call_stack::CallStackId, - dfg::{DataFlowGraph, InsertInstructionResult}, + dfg::InsertInstructionResult, function::{Function, FunctionId, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, @@ -99,7 +99,7 @@ struct InlineContext<'global> { // These are the functions of the program that we shouldn't inline. functions_not_to_inline: BTreeSet, - globals: &'global DataFlowGraph, + globals: &'global Function, } /// The per-function inlining context contains information that is only valid for one function. @@ -382,7 +382,7 @@ impl<'global> InlineContext<'global> { let mut context = PerFunctionContext::new(&mut self, entry_point); context.inlining_entry = true; - for (_, value) in ssa.globals.values_iter() { + for (_, value) in ssa.globals.dfg.values_iter() { context.context.builder.current_function.dfg.make_global(value.get_type().into_owned()); } @@ -491,10 +491,10 @@ impl<'function, 'global> PerFunctionContext<'function, 'global> { Value::Global(_) => { // TODO: Inlining the global into the function is only a temporary measure // until Brillig gen with globals is working end to end - match &self.context.globals[id] { + match &self.context.globals.dfg[id] { Value::Instruction { instruction, .. } => { let Instruction::MakeArray { elements, typ } = - &self.context.globals[*instruction] + &self.context.globals.dfg[*instruction] else { panic!("Only expect Instruction::MakeArray for a global"); }; diff --git a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index af0add9279..5f21e3816f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -26,7 +26,7 @@ impl Ssa { let mut context = Context::default(); context.populate_functions(&self.functions); for function in self.functions.values_mut() { - context.normalize_ids(function, &self.globals); + context.normalize_ids(function, &self.globals.dfg); } self.functions = context.functions.into_btree(); } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index af98eb6f93..9aaa23cbd3 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -11,7 +11,6 @@ use noirc_frontend::monomorphization::ast::{FuncId, Program}; 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}; use crate::ssa::ir::instruction::BinaryOp; @@ -56,7 +55,6 @@ pub(super) struct FunctionContext<'a> { /// SSA can be generated by continuously popping from this function_queue and using /// FunctionContext to generate from the popped function id. Once the queue is empty, /// no other functions are reachable and the SSA generation is finished. -#[derive(Default)] pub(super) struct SharedContext { /// All currently known functions which have already been assigned function ids. /// These functions are all either currently having their SSA generated or are @@ -74,7 +72,11 @@ pub(super) struct SharedContext { /// Shared counter used to assign the ID of the next function function_counter: AtomicCounter, - pub(super) globals_context: DataFlowGraph, + /// A pseudo function that represents global values. + /// Globals are only concerned with the values and instructions (due to Instruction::MakeArray) + /// in a function's DataFlowGraph. However, in order to re-use various codegen methods + /// we need to use the same `Function` type. + pub(super) globals_context: Function, pub(super) globals: BTreeMap, @@ -142,7 +144,7 @@ impl<'a> FunctionContext<'a> { } fn add_globals(&mut self) { - for (_, value) in self.shared_context.globals_context.values_iter() { + for (_, value) in self.shared_context.globals_context.dfg.values_iter() { self.builder.current_function.dfg.make_global(value.get_type().into_owned()); } } @@ -1044,7 +1046,7 @@ fn convert_operator(op: BinaryOpKind) -> BinaryOp { impl SharedContext { /// Create a new SharedContext for the given monomorphized program. pub(super) fn new(program: Program) -> Self { - let globals_shared_context = SharedContext::default(); + let globals_shared_context = SharedContext::new_for_globals(); let globals_id = Program::global_space_id(); @@ -1068,11 +1070,24 @@ impl SharedContext { function_queue: Default::default(), function_counter: Default::default(), program, - globals_context: context.builder.current_function.dfg, + globals_context: context.builder.current_function, globals, } } + pub(super) fn new_for_globals() -> Self { + let globals_context = Function::new_for_globals(); + + Self { + functions: Default::default(), + function_queue: Default::default(), + function_counter: Default::default(), + program: Default::default(), + globals_context, + globals: Default::default(), + } + } + /// Pops the next function from the shared function queue, returning None if the queue is empty. pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 79cbc8cadf..f3474b9f4a 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use crate::ssa::ir::{ - dfg::DataFlowGraph, function::{Function, FunctionId}, map::AtomicCounter, }; @@ -18,7 +17,7 @@ use noirc_frontend::hir_def::types::Type as HirType; pub(crate) struct Ssa { #[serde_as(as = "Vec<(_, _)>")] pub(crate) functions: BTreeMap, - pub(crate) globals: DataFlowGraph, + pub(crate) globals: Function, pub(crate) main_id: FunctionId, #[serde(skip)] pub(crate) next_id: AtomicCounter, @@ -55,7 +54,9 @@ impl Ssa { next_id: AtomicCounter::starting_after(max_id), entry_point_to_generated_index: BTreeMap::new(), error_selector_to_type: error_types, - globals: DataFlowGraph::default(), + // This field should be set afterwards as globals are generated + // outside of the FunctionBuilder, which is where the `Ssa` is instantiated. + globals: Function::new_for_globals(), } }