From 7672edb07fb8ab56d769261ed0c69b07f028156f Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 27 Apr 2023 09:47:24 -0500 Subject: [PATCH 1/7] Implement first-class functions --- .../src/ssa_refactor/ir/dfg.rs | 21 ++++++++++++++++--- .../src/ssa_refactor/ir/function.rs | 10 +++++---- .../src/ssa_refactor/ir/instruction.rs | 6 ++---- .../src/ssa_refactor/ir/map.rs | 14 ------------- .../src/ssa_refactor/ir/printer.rs | 17 +++++++++------ .../src/ssa_refactor/ir/value.rs | 13 +++++++++++- .../src/ssa_refactor/ssa_builder/mod.rs | 2 +- .../src/ssa_refactor/ssa_gen/context.rs | 2 +- .../src/ssa_refactor/ssa_gen/mod.rs | 14 ++----------- 9 files changed, 53 insertions(+), 46 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs index ab2018b1df8..5fd12b58eee 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; + use super::{ basic_block::{BasicBlock, BasicBlockId}, constant::{NumericConstant, NumericConstantId}, - function::Signature, + function::{FunctionId, Signature}, instruction::{Instruction, InstructionId, InstructionResultType, TerminatorInstruction}, - map::{DenseMap, Id, SecondaryMap, TwoWayMap}, + map::{DenseMap, Id, TwoWayMap}, types::Type, value::{Value, ValueId}, }; @@ -53,7 +55,7 @@ pub(crate) struct DataFlowGraph { /// Currently, we need to define them in a better way /// Call instructions require the func signature, but /// other instructions may need some more reading on my part - results: SecondaryMap, + results: HashMap, /// Storage for all of the values defined in this /// function. @@ -64,6 +66,11 @@ pub(crate) struct DataFlowGraph { /// twice will return the same ConstantId. constants: TwoWayMap, + /// Contains each function that has been imported into the current function. + /// Each function's Value::Function is uniqued here so any given FunctionId + /// will always have the same ValueId within this function. + functions: HashMap, + /// Function signatures of external methods signatures: DenseMap, @@ -150,6 +157,14 @@ impl DataFlowGraph { self.values.insert(Value::NumericConstant { constant, typ }) } + /// Gets or creats a ValueId for the given FunctionId. + pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { + if let Some(existing) = self.functions.get(&function) { + return *existing; + } + self.values.insert(Value::Function { id: function }) + } + /// Attaches results to the instruction, clearing any previous results. /// /// This does not normally need to be called manually as it is called within diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs index ca486d0258a..e40c086c0e6 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs @@ -1,7 +1,9 @@ +use std::collections::HashMap; + use super::basic_block::BasicBlockId; use super::dfg::DataFlowGraph; -use super::instruction::Instruction; -use super::map::{Id, SecondaryMap}; +use super::instruction::InstructionId; +use super::map::Id; use super::types::Type; use noirc_errors::Location; @@ -15,7 +17,7 @@ use noirc_errors::Location; #[derive(Debug)] pub(crate) struct Function { /// Maps instructions to source locations - source_locations: SecondaryMap, + source_locations: HashMap, /// The first basic block in the function entry_block: BasicBlockId, @@ -35,7 +37,7 @@ impl Function { pub(crate) fn new(name: String, id: FunctionId) -> Self { let mut dfg = DataFlowGraph::default(); let entry_block = dfg.make_block(); - Self { name, source_locations: SecondaryMap::new(), id, entry_block, dfg } + Self { name, source_locations: HashMap::new(), id, entry_block, dfg } } pub(crate) fn name(&self) -> &str { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs index 11c6b8dc05f..5e9e7229e3a 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs @@ -1,6 +1,4 @@ -use super::{ - basic_block::BasicBlockId, function::FunctionId, map::Id, types::Type, value::ValueId, -}; +use super::{basic_block::BasicBlockId, map::Id, types::Type, value::ValueId}; /// Reference to an instruction pub(crate) type InstructionId = Id; @@ -41,7 +39,7 @@ pub(crate) enum Instruction { Constrain(ValueId), /// Performs a function call with a list of its arguments. - Call { func: FunctionId, arguments: Vec }, + Call { func: ValueId, arguments: Vec }, /// Performs a call to an intrinsic function and stores the /// results in `return_arguments`. diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/map.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/map.rs index 24b30241293..a99ff06c5fb 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/map.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/map.rs @@ -260,20 +260,6 @@ impl std::ops::Index> for TwoWayMap { } } -/// A SecondaryMap is for storing secondary data for a given key. Since this -/// map is for secondary data, it will not return fresh Ids for data, instead -/// it expects users to provide these ids in order to associate existing ids with -/// additional data. -/// -/// Unlike SecondaryMap in cranelift, this version is sparse and thus -/// does not require inserting default elements for each key in between -/// the desired key and the previous length of the map. -/// -/// There is no expectation that there is always secondary data for all relevant -/// Ids of a given type, so unlike the other Map types, it is possible for -/// a call to .get(id) to return None. -pub(crate) type SecondaryMap = HashMap, V>; - /// A simple counter to create fresh Ids without any storage. /// Useful for assigning ids before the storage is created or assigning ids /// for types that have no single owner. diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs index ff46b49b9b4..4873f436dca 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs @@ -55,12 +55,17 @@ pub(crate) fn display_block( display_terminator(function, block.terminator(), f) } -/// Specialize displaying value ids so that if they refer to constants we -/// print the constant directly +/// Specialize displaying value ids so that if they refer to a numeric +/// constant or a function we print those directly. fn value(function: &Function, id: ValueId) -> String { - match function.dfg.get_numeric_constant_with_type(id) { - Some((value, typ)) => format!("{} {}", value, typ), - None => id.to_string(), + use super::value::Value; + match &function.dfg[id] { + Value::NumericConstant { constant, typ } => { + let value = function.dfg[*constant].value(); + format!("{} {}", typ, value) + } + Value::Function { id } => id.to_string(), + _ => id.to_string(), } } @@ -120,7 +125,7 @@ pub(crate) fn display_instruction( writeln!(f, "constrain {}", show(*value)) } Instruction::Call { func, arguments } => { - writeln!(f, "call {func}({})", value_list(function, arguments)) + writeln!(f, "call {}({})", show(*func), value_list(function, arguments)) } Instruction::Intrinsic { func, arguments } => { writeln!(f, "intrinsic {func}({})", value_list(function, arguments)) diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs index a559522fadd..39228ae655b 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs @@ -1,6 +1,9 @@ use crate::ssa_refactor::ir::basic_block::BasicBlockId; -use super::{constant::NumericConstantId, instruction::InstructionId, map::Id, types::Type}; +use super::{ + constant::NumericConstantId, function::FunctionId, instruction::InstructionId, map::Id, + types::Type, +}; pub(crate) type ValueId = Id; @@ -27,6 +30,13 @@ pub(crate) enum Value { /// This Value originates from a numeric constant NumericConstant { constant: NumericConstantId, typ: Type }, + + /// This Value refers to a function in the IR. + /// Functions always have the type Type::Function. + /// If the argument or return types are needed, users should retrieve + /// their types via the Call instruction's arguments or the Call instruction's + /// result types respectively. + Function { id: FunctionId }, } impl Value { @@ -35,6 +45,7 @@ impl Value { Value::Instruction { typ, .. } => *typ, Value::Param { typ, .. } => *typ, Value::NumericConstant { typ, .. } => *typ, + Value::Function { .. } => Type::Function, } } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs index fdbaa36308b..7da88e47157 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs @@ -160,7 +160,7 @@ impl FunctionBuilder { /// the results of the call. pub(crate) fn insert_call( &mut self, - func: FunctionId, + func: ValueId, arguments: Vec, result_types: Vec, ) -> &[ValueId] { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs index df5329fed92..bd04f90d063 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs @@ -164,7 +164,7 @@ impl<'a> FunctionContext<'a> { /// back into a Values tree of the proper shape. pub(super) fn insert_call( &mut self, - function: IrFunctionId, + function: ValueId, arguments: Vec, result_type: &ast::Type, ) -> Values { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index 4aad2aafec1..8475b3c84c7 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -11,7 +11,7 @@ use self::{ value::{Tree, Values}, }; -use super::ir::{function::FunctionId, instruction::BinaryOp, types::Type, value::ValueId}; +use super::ir::{instruction::BinaryOp, types::Type, value::ValueId}; pub(crate) fn generate_ssa(program: Program) { let context = SharedContext::new(program); @@ -255,18 +255,8 @@ impl<'a> FunctionContext<'a> { Self::get_field(tuple, field_index) } - fn codegen_function(&mut self, function: &Expression) -> FunctionId { - use crate::ssa_refactor::ssa_gen::value::Value; - match self.codegen_expression(function) { - Tree::Leaf(Value::Function(id)) => id, - other => { - panic!("codegen_function: expected function value, found {other:?}") - } - } - } - fn codegen_call(&mut self, call: &ast::Call) -> Values { - let function = self.codegen_function(&call.func); + let function = self.codegen_non_tuple_expression(&call.func); let arguments = call .arguments From a0cbb674a9c1fac1c8c63f0bec7e6fd0a82fa732 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 27 Apr 2023 12:56:23 -0500 Subject: [PATCH 2/7] Update crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs Co-authored-by: kevaundray --- crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs index 5fd12b58eee..60591da311c 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs @@ -157,7 +157,7 @@ impl DataFlowGraph { self.values.insert(Value::NumericConstant { constant, typ }) } - /// Gets or creats a ValueId for the given FunctionId. + /// Gets or creates a ValueId for the given FunctionId. pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { if let Some(existing) = self.functions.get(&function) { return *existing; From e6fcaa8dbd3428f03970bf6d281e25680cbdf1ce Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 27 Apr 2023 13:36:36 -0500 Subject: [PATCH 3/7] Implement intrinsics --- .../src/ssa_refactor/ir/dfg.rs | 19 +++++- .../src/ssa_refactor/ir/instruction.rs | 63 +++++++++++++------ .../src/ssa_refactor/ir/printer.rs | 6 +- .../src/ssa_refactor/ir/value.rs | 12 +++- .../src/ssa_refactor/ssa_builder/mod.rs | 15 +++++ .../src/ssa_refactor/ssa_gen/context.rs | 4 +- .../src/ssa_refactor/ssa_gen/mod.rs | 8 ++- .../src/ssa_refactor/ssa_gen/value.rs | 3 - 8 files changed, 96 insertions(+), 34 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs index 60591da311c..4d2ebe31efb 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs @@ -4,7 +4,9 @@ use super::{ basic_block::{BasicBlock, BasicBlockId}, constant::{NumericConstant, NumericConstantId}, function::{FunctionId, Signature}, - instruction::{Instruction, InstructionId, InstructionResultType, TerminatorInstruction}, + instruction::{ + Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction, + }, map::{DenseMap, Id, TwoWayMap}, types::Type, value::{Value, ValueId}, @@ -71,6 +73,11 @@ pub(crate) struct DataFlowGraph { /// will always have the same ValueId within this function. functions: HashMap, + /// Contains each intrinsic that has been imported into the current function. + /// This map is used to ensure that the ValueId for any given intrinsic is always + /// represented by only 1 ValueId within this function. + intrinsics: HashMap, + /// Function signatures of external methods signatures: DenseMap, @@ -162,7 +169,15 @@ impl DataFlowGraph { if let Some(existing) = self.functions.get(&function) { return *existing; } - self.values.insert(Value::Function { id: function }) + self.values.insert(Value::Function(function)) + } + + /// Gets or creates a ValueId for the given Intrinsic. + pub(crate) fn import_intrinsic(&mut self, intrinsic: Intrinsic) -> ValueId { + if let Some(existing) = self.intrinsics.get(&intrinsic) { + return *existing; + } + self.values.insert(Value::Intrinsic(intrinsic)) } /// Attaches results to the instruction, clearing any previous results. diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs index 5e9e7229e3a..756c7ae5a13 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs @@ -1,9 +1,10 @@ +use acvm::acir::BlackBoxFunc; + use super::{basic_block::BasicBlockId, map::Id, types::Type, value::ValueId}; /// Reference to an instruction pub(crate) type InstructionId = Id; -#[derive(Debug, PartialEq, Eq, Hash, Clone)] /// These are similar to built-ins in other languages. /// These can be classified under two categories: /// - Opcodes which the IR knows the target machine has @@ -11,14 +12,50 @@ pub(crate) type InstructionId = Id; /// - Opcodes which have no function definition in the /// source code and must be processed by the IR. An example /// of this is println. -pub(crate) struct IntrinsicOpcodes; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum Intrinsic { + Sort, + Println, + ToBits(Endian), + ToRadix(Endian), + BlackBox(BlackBoxFunc), +} + +impl std::fmt::Display for Intrinsic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Intrinsic::Println => write!(f, "println"), + Intrinsic::Sort => write!(f, "sort"), + Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"), + Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"), + Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"), + Intrinsic::ToRadix(Endian::Little) => write!(f, "to_le_radix"), + Intrinsic::BlackBox(function) => write!(f, "{function}"), + } + } +} -impl std::fmt::Display for IntrinsicOpcodes { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!("intrinsics have no opcodes yet") +impl Intrinsic { + pub(crate) fn lookup(name: &str) -> Option { + match name { + "println" => Some(Intrinsic::Println), + "array_sort" => Some(Intrinsic::Sort), + "to_le_radix" => Some(Intrinsic::ToRadix(Endian::Little)), + "to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)), + "to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)), + "to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)), + other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox), + } } } +/// The endian-ness of bits when encoding values as bits in e.g. ToBits or ToRadix +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub(crate) enum Endian { + Big, + Little, +} + #[derive(Debug, PartialEq, Eq, Hash, Clone)] /// Instructions are used to perform tasks. /// The instructions that the IR is able to specify are listed below. @@ -41,10 +78,6 @@ pub(crate) enum Instruction { /// Performs a function call with a list of its arguments. Call { func: ValueId, arguments: Vec }, - /// Performs a call to an intrinsic function and stores the - /// results in `return_arguments`. - Intrinsic { func: IntrinsicOpcodes, arguments: Vec }, - /// Allocates a region of memory. Note that this is not concerned with /// the type of memory, the type of element is determined when loading this memory. /// @@ -72,9 +105,6 @@ impl Instruction { Instruction::Constrain(_) => 0, // This returns 0 as the result depends on the function being called Instruction::Call { .. } => 0, - // This also returns 0, but we could get it a compile time, - // since we know the signatures for the intrinsics - Instruction::Intrinsic { .. } => 0, Instruction::Allocate { .. } => 1, Instruction::Load { .. } => 1, Instruction::Store { .. } => 0, @@ -94,9 +124,6 @@ impl Instruction { Instruction::Constrain(_) => 1, // This returns 0 as the arguments depend on the function being called Instruction::Call { .. } => 0, - // This also returns 0, but we could get it a compile time, - // since we know the function definition for the intrinsics - Instruction::Intrinsic { .. } => 0, Instruction::Allocate { size: _ } => 1, Instruction::Load { address: _ } => 1, Instruction::Store { address: _, value: _ } => 2, @@ -113,9 +140,7 @@ impl Instruction { InstructionResultType::Operand(*value) } Instruction::Constrain(_) | Instruction::Store { .. } => InstructionResultType::None, - Instruction::Load { .. } | Instruction::Call { .. } | Instruction::Intrinsic { .. } => { - InstructionResultType::Unknown - } + Instruction::Load { .. } | Instruction::Call { .. } => InstructionResultType::Unknown, } } } @@ -129,7 +154,7 @@ pub(crate) enum InstructionResultType { Known(Type), /// The result type of this function is unknown and separate from its operand types. - /// This occurs for function and intrinsic calls. + /// This occurs for function calls and load operations. Unknown, /// This instruction does not return any results. diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs index 4873f436dca..1471bd46e35 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs @@ -64,7 +64,8 @@ fn value(function: &Function, id: ValueId) -> String { let value = function.dfg[*constant].value(); format!("{} {}", typ, value) } - Value::Function { id } => id.to_string(), + Value::Function(id) => id.to_string(), + Value::Intrinsic(intrinsic) => intrinsic.to_string(), _ => id.to_string(), } } @@ -127,9 +128,6 @@ pub(crate) fn display_instruction( Instruction::Call { func, arguments } => { writeln!(f, "call {}({})", show(*func), value_list(function, arguments)) } - Instruction::Intrinsic { func, arguments } => { - writeln!(f, "intrinsic {func}({})", value_list(function, arguments)) - } Instruction::Allocate { size } => writeln!(f, "alloc {size} fields"), Instruction::Load { address } => writeln!(f, "load {}", show(*address)), Instruction::Store { address, value } => { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs index 39228ae655b..d7d8d8a41ab 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/value.rs @@ -1,7 +1,10 @@ use crate::ssa_refactor::ir::basic_block::BasicBlockId; use super::{ - constant::NumericConstantId, function::FunctionId, instruction::InstructionId, map::Id, + constant::NumericConstantId, + function::FunctionId, + instruction::{InstructionId, Intrinsic}, + map::Id, types::Type, }; @@ -36,7 +39,11 @@ pub(crate) enum Value { /// If the argument or return types are needed, users should retrieve /// their types via the Call instruction's arguments or the Call instruction's /// result types respectively. - Function { id: FunctionId }, + Function(FunctionId), + + /// An Intrinsic is a special kind of builtin function that may be handled internally + /// or optimized into a special form. + Intrinsic(Intrinsic), } impl Value { @@ -46,6 +53,7 @@ impl Value { Value::Param { typ, .. } => *typ, Value::NumericConstant { typ, .. } => *typ, Value::Function { .. } => Type::Function, + Value::Intrinsic { .. } => Type::Function, } } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs index 7da88e47157..6c407dfcd42 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs @@ -8,6 +8,8 @@ use crate::ssa_refactor::ir::{ value::{Value, ValueId}, }; +use super::ir::instruction::Intrinsic; + /// The per-function context for each ssa function being generated. /// /// This is split from the global SsaBuilder context to allow each function @@ -227,4 +229,17 @@ impl FunctionBuilder { // Clear the results of the previous load for safety self.current_function.dfg.make_instruction_results(instruction, None); } + + /// Returns a ValueId pointing to the given function or imports the function + /// into the current function if it was not already, and returns that ID. + pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { + self.current_function.dfg.import_function(function) + } + + /// Retrieve a value reference to the given intrinsic operation. + /// Returns None if there is no intrinsic matching the given name. + pub(crate) fn import_intrinsic(&mut self, name: &str) -> Option { + Intrinsic::lookup(name) + .map(|intrinsic| self.current_function.dfg.import_intrinsic(intrinsic)) + } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs index bd04f90d063..909ed4ff84d 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs @@ -242,9 +242,9 @@ impl<'a> FunctionContext<'a> { /// Retrieves the given function, adding it to the function queue /// if it is not yet compiled. - pub(super) fn get_or_queue_function(&self, id: FuncId) -> Values { + pub(super) fn get_or_queue_function(&mut self, id: FuncId) -> Values { let function = self.shared_context.get_or_queue_function(id); - Values::Leaf(super::value::Value::Function(function)) + self.builder.import_function(function).into() } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index 8475b3c84c7..715f835ab7f 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -66,8 +66,12 @@ impl<'a> FunctionContext<'a> { match &ident.definition { ast::Definition::Local(id) => self.lookup(*id).map(|value| value.eval(self).into()), ast::Definition::Function(id) => self.get_or_queue_function(*id), - ast::Definition::Builtin(_) => todo!(), - ast::Definition::LowLevel(_) => todo!(), + ast::Definition::Builtin(name) | ast::Definition::LowLevel(name) => { + match self.builder.import_intrinsic(name) { + Some(builtin) => builtin.into(), + None => panic!("No builtin function named '{name}' found"), + } + } } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/value.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/value.rs index 52ff52d75f2..410e375fcd6 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/value.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/value.rs @@ -1,6 +1,5 @@ use iter_extended::vecmap; -use crate::ssa_refactor::ir::function::FunctionId as IrFunctionId; use crate::ssa_refactor::ir::types::Type; use crate::ssa_refactor::ir::value::ValueId as IrValueId; @@ -15,7 +14,6 @@ pub(super) enum Tree { #[derive(Debug, Copy, Clone)] pub(super) enum Value { Normal(IrValueId), - Function(IrFunctionId), /// A mutable variable that must be loaded as the given type before being used Mutable(IrValueId, Type), @@ -32,7 +30,6 @@ impl Value { let offset = ctx.builder.field_constant(0u128); ctx.builder.insert_load(address, offset, typ) } - Value::Function(_) => panic!("Tried to evaluate a function value"), } } } From a3574ecc8d33ae62cab8c6fc7fc564623bb527de Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 27 Apr 2023 15:23:08 -0500 Subject: [PATCH 4/7] Fix no return & duplicate main --- crates/noirc_evaluator/src/ssa_refactor.rs | 2 +- .../src/ssa_refactor/ir/function.rs | 2 +- .../src/ssa_refactor/ir/printer.rs | 16 +++- .../src/ssa_refactor/ssa_builder/mod.rs | 78 +++++++++++-------- .../src/ssa_refactor/ssa_gen/context.rs | 17 +--- .../src/ssa_refactor/ssa_gen/mod.rs | 23 ++++-- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa_refactor.rs b/crates/noirc_evaluator/src/ssa_refactor.rs index 37f1ead2b07..fc45071e579 100644 --- a/crates/noirc_evaluator/src/ssa_refactor.rs +++ b/crates/noirc_evaluator/src/ssa_refactor.rs @@ -9,4 +9,4 @@ mod ir; mod ssa_builder; -mod ssa_gen; +pub mod ssa_gen; diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs index e40c086c0e6..6789e5364fe 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/function.rs @@ -15,7 +15,7 @@ use noirc_errors::Location; /// To reference external functions, one must first import the function signature /// into the current function's context. #[derive(Debug)] -pub(crate) struct Function { +pub struct Function { /// Maps instructions to source locations source_locations: HashMap, diff --git a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs index 1471bd46e35..b0e6d787a6a 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs @@ -46,7 +46,7 @@ pub(crate) fn display_block( ) -> Result { let block = &function.dfg[block_id]; - writeln!(f, " {}({}):", block_id, value_list(function, block.parameters()))?; + writeln!(f, " {}({}):", block_id, value_list_with_types(function, block.parameters()))?; for instruction in block.instructions() { display_instruction(function, *instruction, f)?; @@ -70,6 +70,16 @@ fn value(function: &Function, id: ValueId) -> String { } } +/// Display each value along with its type. E.g. `v0: Field, v1: u64, v2: u1` +fn value_list_with_types(function: &Function, values: &[ValueId]) -> String { + vecmap(values, |id| { + let value = value(function, *id); + let typ = function.dfg.type_of_value(*id); + format!("{value}: {typ}") + }) + .join(", ") +} + fn value_list(function: &Function, values: &[ValueId]) -> String { vecmap(values, |id| value(function, *id)).join(", ") } @@ -87,7 +97,9 @@ pub(crate) fn display_terminator( writeln!( f, " jmpif {} then: {}, else: {}", - condition, then_destination, else_destination + value(function, *condition), + then_destination, + else_destination ) } Some(TerminatorInstruction::Return { return_values }) => { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs index 6c407dfcd42..9abbdb2df65 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs @@ -17,36 +17,48 @@ use super::ir::instruction::Intrinsic; /// /// Contrary to the name, this struct has the capacity to build as many /// functions as needed, although it is limited to one function at a time. +#[derive(Default)] pub(crate) struct FunctionBuilder { - current_function: Function, - current_block: BasicBlockId, - finished_functions: Vec<(FunctionId, Function)>, + current_function: Option, + current_block: Option, + finished_functions: Vec, } impl FunctionBuilder { - pub(crate) fn new(function_name: String, function_id: FunctionId) -> Self { - let new_function = Function::new(function_name, function_id); - let current_block = new_function.entry_block(); - - Self { current_function: new_function, current_block, finished_functions: Vec::new() } - } - /// Finish the current function and create a new function pub(crate) fn new_function(&mut self, name: String, function_id: FunctionId) { + let old_function = std::mem::take(&mut self.current_function); let new_function = Function::new(name, function_id); - let old_function = std::mem::replace(&mut self.current_function, new_function); + self.current_block = Some(new_function.entry_block()); + self.current_function = Some(new_function); - self.finished_functions.push((self.current_function.id(), old_function)); + if let Some(old_function) = old_function { + self.finished_functions.push(old_function); + } } - pub(crate) fn finish(mut self) -> Vec<(FunctionId, Function)> { - self.finished_functions.push((self.current_function.id(), self.current_function)); + pub(crate) fn finish(mut self) -> Vec { + if let Some(old_function) = self.current_function { + self.finished_functions.push(old_function); + } self.finished_functions } + pub(crate) fn current_function(&self) -> &Function { + self.current_function.as_ref().expect("This builder is not currently building a Function") + } + + pub(crate) fn current_function_mut(&mut self) -> &mut Function { + self.current_function.as_mut().expect("This builder is not currently building a Function") + } + + pub(crate) fn current_block(&self) -> BasicBlockId { + self.current_block.expect("This builder has no current_block set") + } + pub(crate) fn add_parameter(&mut self, typ: Type) -> ValueId { - let entry = self.current_function.entry_block(); - self.current_function.dfg.add_block_parameter(entry, typ) + let entry = self.current_function().entry_block(); + self.current_function_mut().dfg.add_block_parameter(entry, typ) } /// Insert a numeric constant into the current function @@ -55,7 +67,7 @@ impl FunctionBuilder { value: impl Into, typ: Type, ) -> ValueId { - self.current_function.dfg.make_constant(value.into(), typ) + self.current_function_mut().dfg.make_constant(value.into(), typ) } /// Insert a numeric constant into the current function of type Field @@ -64,15 +76,15 @@ impl FunctionBuilder { } pub(crate) fn type_of_value(&self, value: ValueId) -> Type { - self.current_function.dfg.type_of_value(value) + self.current_function().dfg.type_of_value(value) } pub(crate) fn insert_block(&mut self) -> BasicBlockId { - self.current_function.dfg.make_block() + self.current_function_mut().dfg.make_block() } pub(crate) fn add_block_parameter(&mut self, block: BasicBlockId, typ: Type) -> ValueId { - self.current_function.dfg.add_block_parameter(block, typ) + self.current_function_mut().dfg.add_block_parameter(block, typ) } /// Inserts a new instruction at the end of the current block and returns its results @@ -81,16 +93,17 @@ impl FunctionBuilder { instruction: Instruction, ctrl_typevars: Option>, ) -> &[ValueId] { - let id = self.current_function.dfg.make_instruction(instruction, ctrl_typevars); - self.current_function.dfg.insert_instruction_in_block(self.current_block, id); - self.current_function.dfg.instruction_results(id) + let id = self.current_function_mut().dfg.make_instruction(instruction, ctrl_typevars); + let current_block = self.current_block(); + self.current_function_mut().dfg.insert_instruction_in_block(current_block, id); + self.current_function_mut().dfg.instruction_results(id) } /// Switch to inserting instructions in the given block. /// Expects the given block to be within the same function. If you want to insert /// instructions into a new function, call new_function instead. pub(crate) fn switch_to_block(&mut self, block: BasicBlockId) { - self.current_block = block; + self.current_block = Some(block); } /// Insert an allocate instruction at the end of the current block, allocating the @@ -113,7 +126,7 @@ impl FunctionBuilder { offset: ValueId, type_to_load: Type, ) -> ValueId { - if let Some(offset) = self.current_function.dfg.get_numeric_constant(offset) { + if let Some(offset) = self.current_function().dfg.get_numeric_constant(offset) { if !offset.is_zero() { let offset = self.field_constant(offset); address = self.insert_binary(address, BinaryOp::Add, offset); @@ -171,7 +184,8 @@ impl FunctionBuilder { /// Terminates the current block with the given terminator instruction fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { - self.current_function.dfg.set_block_terminator(self.current_block, terminator); + let current_block = self.current_block(); + self.current_function_mut().dfg.set_block_terminator(current_block, terminator); } /// Terminate the current block with a jmp instruction to jmp to the given @@ -212,9 +226,9 @@ impl FunctionBuilder { /// (which should always be present to load a mutable value) with a store of the /// assigned value. pub(crate) fn mutate_load_into_store(&mut self, load_result: ValueId, value_to_store: ValueId) { - let (instruction, address) = match &self.current_function.dfg[load_result] { + let (instruction, address) = match &self.current_function().dfg[load_result] { Value::Instruction { instruction, .. } => { - match &self.current_function.dfg[*instruction] { + match &self.current_function().dfg[*instruction] { Instruction::Load { address } => (*instruction, *address), other => { panic!("mutate_load_into_store: Expected Load instruction, found {other:?}") @@ -225,21 +239,21 @@ impl FunctionBuilder { }; let store = Instruction::Store { address, value: value_to_store }; - self.current_function.dfg.replace_instruction(instruction, store); + self.current_function_mut().dfg.replace_instruction(instruction, store); // Clear the results of the previous load for safety - self.current_function.dfg.make_instruction_results(instruction, None); + self.current_function_mut().dfg.make_instruction_results(instruction, None); } /// Returns a ValueId pointing to the given function or imports the function /// into the current function if it was not already, and returns that ID. pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { - self.current_function.dfg.import_function(function) + self.current_function_mut().dfg.import_function(function) } /// Retrieve a value reference to the given intrinsic operation. /// Returns None if there is no intrinsic matching the given name. pub(crate) fn import_intrinsic(&mut self, name: &str) -> Option { Intrinsic::lookup(name) - .map(|intrinsic| self.current_function.dfg.import_intrinsic(intrinsic)) + .map(|intrinsic| self.current_function_mut().dfg.import_intrinsic(intrinsic)) } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs index 909ed4ff84d..999b972becc 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs @@ -36,21 +36,8 @@ pub(super) struct SharedContext { } impl<'a> FunctionContext<'a> { - pub(super) fn new( - function_id: FuncId, - function_name: String, - parameters: &Parameters, - shared_context: &'a SharedContext, - ) -> Self { - let new_id = shared_context.get_or_queue_function(function_id); - - let mut this = Self { - definitions: HashMap::new(), - builder: FunctionBuilder::new(function_name, new_id), - shared_context, - }; - this.add_parameters_to_scope(parameters); - this + pub(super) fn new(shared_context: &'a SharedContext) -> Self { + Self { definitions: HashMap::new(), builder: FunctionBuilder::default(), shared_context } } pub(super) fn new_function(&mut self, id: IrFunctionId, name: String, parameters: &Parameters) { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index 715f835ab7f..3305201910b 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -11,26 +11,35 @@ use self::{ value::{Tree, Values}, }; -use super::ir::{instruction::BinaryOp, types::Type, value::ValueId}; +use super::ir::{function::Function, instruction::BinaryOp, types::Type, value::ValueId}; -pub(crate) fn generate_ssa(program: Program) { +pub fn generate_ssa(program: Program) -> Vec { let context = SharedContext::new(program); - let main = context.program.main(); let main_id = Program::main_id(); - let main_name = main.name.clone(); - let mut function_context = FunctionContext::new(main_id, main_name, &main.parameters, &context); - function_context.codegen_expression(&main.body); + // Queue the main function for compilation + context.get_or_queue_function(main_id); + let mut function_context = FunctionContext::new(&context); while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; function_context.new_function(dest_id, function.name.clone(), &function.parameters); - function_context.codegen_expression(&function.body); + function_context.codegen_function_body(&function.body); } + + function_context.builder.finish() } 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) { + let return_value = self.codegen_expression(body); + let results = return_value.into_value_list(self); + self.builder.terminate_with_return(results); + } + fn codegen_expression(&mut self, expr: &Expression) -> Values { match expr { Expression::Ident(ident) => self.codegen_ident(ident), From f6f18bb60972c16cdd2ccfac39ed49728c7e9b90 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 27 Apr 2023 15:43:42 -0500 Subject: [PATCH 5/7] bad git. remove duplicated functions --- .../src/ssa_refactor/ssa_builder/mod.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs index c1d65517341..9abbdb2df65 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs @@ -256,17 +256,4 @@ impl FunctionBuilder { Intrinsic::lookup(name) .map(|intrinsic| self.current_function_mut().dfg.import_intrinsic(intrinsic)) } - - /// Returns a ValueId pointing to the given function or imports the function - /// into the current function if it was not already, and returns that ID. - pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { - self.current_function.dfg.import_function(function) - } - - /// Retrieve a value reference to the given intrinsic operation. - /// Returns None if there is no intrinsic matching the given name. - pub(crate) fn import_intrinsic(&mut self, name: &str) -> Option { - Intrinsic::lookup(name) - .map(|intrinsic| self.current_function.dfg.import_intrinsic(intrinsic)) - } } From cbb92bab79aa922e1bd9fb17c8d9824e522d8ced Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 28 Apr 2023 09:28:19 -0500 Subject: [PATCH 6/7] Remove Option in builder --- crates/noirc_driver/src/lib.rs | 3 + .../src/ssa_refactor/ssa_builder/mod.rs | 81 ++++++++----------- .../src/ssa_refactor/ssa_gen/context.rs | 16 +++- .../src/ssa_refactor/ssa_gen/mod.rs | 12 ++- .../src/ssa_refactor/ssa_gen/program.rs | 23 ++++++ 5 files changed, 83 insertions(+), 52 deletions(-) create mode 100644 crates/noirc_evaluator/src/ssa_refactor/ssa_gen/program.rs diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 2fcef5bc578..1cec63c522f 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -11,6 +11,7 @@ use iter_extended::try_vecmap; use noirc_abi::FunctionSignature; use noirc_errors::{reporter, ReportedError}; use noirc_evaluator::create_circuit; +use noirc_evaluator::ssa_refactor::ssa_gen::generate_ssa; use noirc_frontend::graph::{CrateId, CrateName, CrateType, LOCAL_CRATE}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; @@ -251,6 +252,8 @@ impl Driver { ) -> Result { let program = monomorphize(main_function, &self.context.def_interner); + println!("{}", generate_ssa(program.clone())); + let np_language = self.language.clone(); let is_opcode_supported = acvm::default_is_opcode_supported(np_language.clone()); diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs index 9abbdb2df65..35c918d645d 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs @@ -8,7 +8,7 @@ use crate::ssa_refactor::ir::{ value::{Value, ValueId}, }; -use super::ir::instruction::Intrinsic; +use super::{ir::instruction::Intrinsic, ssa_gen::Ssa}; /// The per-function context for each ssa function being generated. /// @@ -17,48 +17,37 @@ use super::ir::instruction::Intrinsic; /// /// Contrary to the name, this struct has the capacity to build as many /// functions as needed, although it is limited to one function at a time. -#[derive(Default)] pub(crate) struct FunctionBuilder { - current_function: Option, - current_block: Option, + current_function: Function, + current_block: BasicBlockId, finished_functions: Vec, } impl FunctionBuilder { - /// Finish the current function and create a new function - pub(crate) fn new_function(&mut self, name: String, function_id: FunctionId) { - let old_function = std::mem::take(&mut self.current_function); - let new_function = Function::new(name, function_id); - self.current_block = Some(new_function.entry_block()); - self.current_function = Some(new_function); - - if let Some(old_function) = old_function { - self.finished_functions.push(old_function); - } - } + pub(crate) fn new(function_name: String, function_id: FunctionId) -> Self { + let new_function = Function::new(function_name, function_id); + let current_block = new_function.entry_block(); - pub(crate) fn finish(mut self) -> Vec { - if let Some(old_function) = self.current_function { - self.finished_functions.push(old_function); - } - self.finished_functions + Self { current_function: new_function, current_block, finished_functions: Vec::new() } } - pub(crate) fn current_function(&self) -> &Function { - self.current_function.as_ref().expect("This builder is not currently building a Function") - } + /// Finish the current function and create a new function + pub(crate) fn new_function(&mut self, name: String, function_id: FunctionId) { + let new_function = Function::new(name, function_id); + self.current_block = new_function.entry_block(); - pub(crate) fn current_function_mut(&mut self) -> &mut Function { - self.current_function.as_mut().expect("This builder is not currently building a Function") + let old_function = std::mem::replace(&mut self.current_function, new_function); + self.finished_functions.push(old_function); } - pub(crate) fn current_block(&self) -> BasicBlockId { - self.current_block.expect("This builder has no current_block set") + pub(crate) fn finish(mut self) -> Ssa { + self.finished_functions.push(self.current_function); + Ssa::new(self.finished_functions) } pub(crate) fn add_parameter(&mut self, typ: Type) -> ValueId { - let entry = self.current_function().entry_block(); - self.current_function_mut().dfg.add_block_parameter(entry, typ) + let entry = self.current_function.entry_block(); + self.current_function.dfg.add_block_parameter(entry, typ) } /// Insert a numeric constant into the current function @@ -67,7 +56,7 @@ impl FunctionBuilder { value: impl Into, typ: Type, ) -> ValueId { - self.current_function_mut().dfg.make_constant(value.into(), typ) + self.current_function.dfg.make_constant(value.into(), typ) } /// Insert a numeric constant into the current function of type Field @@ -76,15 +65,15 @@ impl FunctionBuilder { } pub(crate) fn type_of_value(&self, value: ValueId) -> Type { - self.current_function().dfg.type_of_value(value) + self.current_function.dfg.type_of_value(value) } pub(crate) fn insert_block(&mut self) -> BasicBlockId { - self.current_function_mut().dfg.make_block() + self.current_function.dfg.make_block() } pub(crate) fn add_block_parameter(&mut self, block: BasicBlockId, typ: Type) -> ValueId { - self.current_function_mut().dfg.add_block_parameter(block, typ) + self.current_function.dfg.add_block_parameter(block, typ) } /// Inserts a new instruction at the end of the current block and returns its results @@ -93,17 +82,16 @@ impl FunctionBuilder { instruction: Instruction, ctrl_typevars: Option>, ) -> &[ValueId] { - let id = self.current_function_mut().dfg.make_instruction(instruction, ctrl_typevars); - let current_block = self.current_block(); - self.current_function_mut().dfg.insert_instruction_in_block(current_block, id); - self.current_function_mut().dfg.instruction_results(id) + let id = self.current_function.dfg.make_instruction(instruction, ctrl_typevars); + self.current_function.dfg.insert_instruction_in_block(self.current_block, id); + self.current_function.dfg.instruction_results(id) } /// Switch to inserting instructions in the given block. /// Expects the given block to be within the same function. If you want to insert /// instructions into a new function, call new_function instead. pub(crate) fn switch_to_block(&mut self, block: BasicBlockId) { - self.current_block = Some(block); + self.current_block = block; } /// Insert an allocate instruction at the end of the current block, allocating the @@ -126,7 +114,7 @@ impl FunctionBuilder { offset: ValueId, type_to_load: Type, ) -> ValueId { - if let Some(offset) = self.current_function().dfg.get_numeric_constant(offset) { + if let Some(offset) = self.current_function.dfg.get_numeric_constant(offset) { if !offset.is_zero() { let offset = self.field_constant(offset); address = self.insert_binary(address, BinaryOp::Add, offset); @@ -184,8 +172,7 @@ impl FunctionBuilder { /// Terminates the current block with the given terminator instruction fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { - let current_block = self.current_block(); - self.current_function_mut().dfg.set_block_terminator(current_block, terminator); + self.current_function.dfg.set_block_terminator(self.current_block, terminator); } /// Terminate the current block with a jmp instruction to jmp to the given @@ -226,9 +213,9 @@ impl FunctionBuilder { /// (which should always be present to load a mutable value) with a store of the /// assigned value. pub(crate) fn mutate_load_into_store(&mut self, load_result: ValueId, value_to_store: ValueId) { - let (instruction, address) = match &self.current_function().dfg[load_result] { + let (instruction, address) = match &self.current_function.dfg[load_result] { Value::Instruction { instruction, .. } => { - match &self.current_function().dfg[*instruction] { + match &self.current_function.dfg[*instruction] { Instruction::Load { address } => (*instruction, *address), other => { panic!("mutate_load_into_store: Expected Load instruction, found {other:?}") @@ -239,21 +226,21 @@ impl FunctionBuilder { }; let store = Instruction::Store { address, value: value_to_store }; - self.current_function_mut().dfg.replace_instruction(instruction, store); + self.current_function.dfg.replace_instruction(instruction, store); // Clear the results of the previous load for safety - self.current_function_mut().dfg.make_instruction_results(instruction, None); + self.current_function.dfg.make_instruction_results(instruction, None); } /// Returns a ValueId pointing to the given function or imports the function /// into the current function if it was not already, and returns that ID. pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { - self.current_function_mut().dfg.import_function(function) + self.current_function.dfg.import_function(function) } /// Retrieve a value reference to the given intrinsic operation. /// Returns None if there is no intrinsic matching the given name. pub(crate) fn import_intrinsic(&mut self, name: &str) -> Option { Intrinsic::lookup(name) - .map(|intrinsic| self.current_function_mut().dfg.import_intrinsic(intrinsic)) + .map(|intrinsic| self.current_function.dfg.import_intrinsic(intrinsic)) } } diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs index 999b972becc..3a730cca827 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs @@ -36,8 +36,20 @@ pub(super) struct SharedContext { } impl<'a> FunctionContext<'a> { - pub(super) fn new(shared_context: &'a SharedContext) -> Self { - Self { definitions: HashMap::new(), builder: FunctionBuilder::default(), shared_context } + pub(super) fn new( + function_name: String, + parameters: &Parameters, + shared_context: &'a SharedContext, + ) -> Self { + let function_id = shared_context + .pop_next_function_in_queue() + .expect("No function in queue for the FunctionContext to compile") + .1; + + let builder = FunctionBuilder::new(function_name, function_id); + let mut this = Self { definitions: HashMap::new(), builder, shared_context }; + this.add_parameters_to_scope(parameters); + this } pub(super) fn new_function(&mut self, id: IrFunctionId, name: String, parameters: &Parameters) { diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs index 3305201910b..8b168b08836 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs @@ -1,6 +1,9 @@ mod context; +mod program; mod value; +pub use program::Ssa; + use context::SharedContext; use iter_extended::vecmap; use noirc_errors::Location; @@ -11,17 +14,20 @@ use self::{ value::{Tree, Values}, }; -use super::ir::{function::Function, instruction::BinaryOp, types::Type, value::ValueId}; +use super::ir::{instruction::BinaryOp, types::Type, value::ValueId}; -pub fn generate_ssa(program: Program) -> Vec { +pub fn generate_ssa(program: Program) -> Ssa { let context = SharedContext::new(program); let main_id = Program::main_id(); + let main = context.program.main(); // Queue the main function for compilation context.get_or_queue_function(main_id); - let mut function_context = FunctionContext::new(&context); + let mut function_context = FunctionContext::new(main.name.clone(), &main.parameters, &context); + function_context.codegen_function_body(&main.body); + while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; function_context.new_function(dest_id, function.name.clone(), &function.parameters); diff --git a/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/program.rs b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/program.rs new file mode 100644 index 00000000000..03eb76dec50 --- /dev/null +++ b/crates/noirc_evaluator/src/ssa_refactor/ssa_gen/program.rs @@ -0,0 +1,23 @@ +use std::fmt::Display; + +use crate::ssa_refactor::ir::function::Function; + +/// Contains the entire Ssa representation of the program +pub struct Ssa { + functions: Vec, +} + +impl Ssa { + pub fn new(functions: Vec) -> Self { + Self { functions } + } +} + +impl Display for Ssa { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for function in &self.functions { + writeln!(f, "{function}")?; + } + Ok(()) + } +} From 4672d91af11063601ecf09d6892a4ddac74804da Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 28 Apr 2023 09:29:29 -0500 Subject: [PATCH 7/7] Undo debug printing in driver --- crates/noirc_driver/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 1cec63c522f..2fcef5bc578 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -11,7 +11,6 @@ use iter_extended::try_vecmap; use noirc_abi::FunctionSignature; use noirc_errors::{reporter, ReportedError}; use noirc_evaluator::create_circuit; -use noirc_evaluator::ssa_refactor::ssa_gen::generate_ssa; use noirc_frontend::graph::{CrateId, CrateName, CrateType, LOCAL_CRATE}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; @@ -252,8 +251,6 @@ impl Driver { ) -> Result { let program = monomorphize(main_function, &self.context.def_interner); - println!("{}", generate_ssa(program.clone())); - let np_language = self.language.clone(); let is_opcode_supported = acvm::default_is_opcode_supported(np_language.clone());