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

chore(ssa refactor): Handle codegen for literals #1209

Merged
merged 13 commits into from
Apr 24, 2023
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod basic_block;
pub(crate) mod constant;
pub(crate) mod dfg;
pub(crate) mod function;
pub(crate) mod instruction;
Expand Down
19 changes: 18 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir/basic_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,24 @@ pub(crate) struct BasicBlock {
pub(crate) type BasicBlockId = Id<BasicBlock>;

impl BasicBlock {
pub(super) fn new(parameters: Vec<ValueId>) -> Self {
pub(crate) fn new(parameters: Vec<ValueId>) -> Self {
Self { parameters, instructions: Vec::new(), is_sealed: false, terminator: None }
}

pub(crate) fn parameters(&self) -> &[ValueId] {
&self.parameters
}

pub(crate) fn add_parameter(&mut self, parameter: ValueId) {
self.parameters.push(parameter);
}

/// Insert an instruction at the end of this block
pub(crate) fn insert_instruction(&mut self, instruction: InstructionId) {
self.instructions.push(instruction);
}

pub(crate) fn set_terminator(&mut self, terminator: TerminatorInstruction) {
self.terminator = Some(terminator);
}
}
56 changes: 56 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/constant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use acvm::FieldElement;

use super::map::Id;

/// Represents a numeric constant in Ssa. Constants themselves are
/// uniqued in the DataFlowGraph and immutable.
///
/// This is just a thin wrapper around FieldElement so that
/// we can use Id<NumericConstant> without it getting confused
/// with a possible future use of Id<FieldElement>.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub(crate) struct NumericConstant(FieldElement);

impl NumericConstant {
pub(crate) fn new(value: FieldElement) -> Self {
Self(value)
}

pub(crate) fn value(&self) -> &FieldElement {
&self.0
}
}

pub(crate) type NumericConstantId = Id<NumericConstant>;

impl std::ops::Add for NumericConstant {
type Output = NumericConstant;

fn add(self, rhs: Self) -> Self::Output {
Self::new(self.0 + rhs.0)
}
}

impl std::ops::Sub for NumericConstant {
type Output = NumericConstant;

fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.0 - rhs.0)
}
}

impl std::ops::Mul for NumericConstant {
type Output = NumericConstant;

fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.0 * rhs.0)
}
}

impl std::ops::Div for NumericConstant {
type Output = NumericConstant;

fn div(self, rhs: Self) -> Self::Output {
Self::new(self.0 / rhs.0)
}
}
86 changes: 73 additions & 13 deletions crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use super::{
basic_block::{BasicBlock, BasicBlockId},
constant::NumericConstant,
function::Signature,
instruction::{Instruction, InstructionId},
map::{DenseMap, Id, SecondaryMap},
map::{DenseMap, Id, SecondaryMap, TwoWayMap},
types::Type,
value::{Value, ValueId},
};

use acvm::FieldElement;
use iter_extended::vecmap;

#[derive(Debug, Default)]
/// A convenience wrapper to store `Value`s.
pub(crate) struct ValueList(Vec<Id<Value>>);
Expand All @@ -18,6 +22,7 @@ impl ValueList {
self.0.push(value);
self.len() - 1
}

/// Returns the number of values in the list.
fn len(&self) -> usize {
self.0.len()
Expand All @@ -27,6 +32,7 @@ impl ValueList {
fn clear(&mut self) {
self.0.clear();
}

/// Returns the ValueId's as a slice.
pub(crate) fn as_slice(&self) -> &[ValueId] {
&self.0
Expand All @@ -53,6 +59,11 @@ pub(crate) struct DataFlowGraph {
/// function.
values: DenseMap<Value>,

/// Storage for all constants used within a function.
/// Each constant is unique, attempting to insert the same constant
/// twice will return the same ConstantId.
constants: TwoWayMap<NumericConstant>,

/// Function signatures of external methods
signatures: DenseMap<Signature>,

Expand All @@ -61,47 +72,82 @@ pub(crate) struct DataFlowGraph {
}

impl DataFlowGraph {
/// Creates a new `empty` basic block
/// 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.
pub(crate) fn new_block(&mut self) -> BasicBlockId {
todo!()
self.blocks.insert(BasicBlock::new(Vec::new()))
}

/// Creates a new basic block with the given parameters.
/// After being created, the block is unreachable in the current function
/// until another block is made to jump to it.
pub(crate) fn new_block_with_parameters(
&mut self,
parameter_types: impl Iterator<Item = Type>,
) -> BasicBlockId {
self.blocks.insert_with_id(|entry_block| {
let parameters = vecmap(parameter_types.enumerate(), |(position, typ)| {
self.values.insert(Value::Param { block: entry_block, position, typ })
});

BasicBlock::new(parameters)
})
}

pub(crate) fn block_parameters(&self, block: BasicBlockId) -> &[ValueId] {
self.blocks[block].parameters()
}

/// Inserts a new instruction into the DFG.
/// This does not add the instruction to the block or populate the instruction's result list
pub(crate) fn make_instruction(&mut self, instruction_data: Instruction) -> InstructionId {
let id = self.instructions.insert(instruction_data);

// Create a new vector to store the potential results for the instruction.
self.results.insert(id, Default::default());

id
}

jfecher marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) fn make_allocate(&mut self, size: u32) -> (InstructionId, ValueId) {
let id = self.make_instruction(Instruction::Allocate { size });
self.make_instruction_results(id, Type::Reference);
(id, self.instruction_results(id)[0])
}

pub(crate) fn make_value(&mut self, value: Value) -> ValueId {
self.values.insert(value)
}

/// Attaches results to the instruction.
pub(crate) fn constant(&mut self, value: FieldElement, typ: Type) -> ValueId {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let constant = self.constants.insert(NumericConstant::new(value));
self.values.insert(Value::NumericConstant { constant, typ })
}

/// Attaches results to the instruction, clearing any previous results.
///
/// Returns the number of results that this instruction
/// produces.
/// Returns the results of the instruction
pub(crate) fn make_instruction_results(
&mut self,
instruction_id: InstructionId,
ctrl_typevar: Type,
) -> usize {
) -> &[ValueId] {
// Clear all of the results instructions associated with this
// instruction.
self.results.get_mut(&instruction_id).expect("all instructions should have a `result` allocation when instruction was added to the DFG").clear();

// Get all of the types that this instruction produces
// and append them as results.
let typs = self.instruction_result_types(instruction_id, ctrl_typevar);
let num_typs = typs.len();

for typ in typs {
self.append_result(instruction_id, typ);
}

num_typs
self.results.get_mut(&instruction_id)
.expect("all instructions should have a `result` allocation when instruction was added to the DFG")
.as_slice()
}

/// Return the result types of this instruction.
Expand Down Expand Up @@ -149,6 +195,22 @@ impl DataFlowGraph {
pub(crate) fn instruction_results(&self, instruction_id: InstructionId) -> &[ValueId] {
self.results.get(&instruction_id).expect("expected a list of Values").as_slice()
}

pub(crate) fn add_block_parameter(&mut self, block_id: BasicBlockId, typ: Type) -> Id<Value> {
let block = &mut self.blocks[block_id];
let position = block.parameters().len();
let parameter = self.values.insert(Value::Param { block: block_id, position, typ });
block.add_parameter(parameter);
parameter
}

pub(crate) fn insert_instruction_in_block(
&mut self,
block: BasicBlockId,
instruction: InstructionId,
) {
self.blocks[block].insert_instruction(instruction);
}
}

#[cfg(test)]
Expand All @@ -158,19 +220,17 @@ mod tests {
instruction::Instruction,
types::{NumericType, Type},
};
use acvm::FieldElement;

#[test]
fn make_instruction() {
let mut dfg = DataFlowGraph::default();
let ins = Instruction::Immediate { value: FieldElement::from(0u128) };
let ins = Instruction::Allocate { size: 20 };
let ins_id = dfg.make_instruction(ins);

let num_results =
dfg.make_instruction_results(ins_id, Type::Numeric(NumericType::NativeField));
dfg.make_instruction_results(ins_id, Type::Numeric(NumericType::NativeField)).len();

let results = dfg.instruction_results(ins_id);

assert_eq!(results.len(), num_results);
}
}
32 changes: 9 additions & 23 deletions crates/noirc_evaluator/src/ssa_refactor/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use super::basic_block::{BasicBlock, BasicBlockId};
use super::basic_block::BasicBlockId;
use super::dfg::DataFlowGraph;
use super::instruction::Instruction;
use super::map::{DenseMap, Id, SecondaryMap};
use super::map::{Id, SecondaryMap};
use super::types::Type;
use super::value::Value;

use iter_extended::vecmap;
use noirc_errors::Location;

/// A function holds a list of instructions.
Expand All @@ -16,35 +14,23 @@ use noirc_errors::Location;
/// into the current function's context.
#[derive(Debug)]
pub(crate) struct Function {
/// Basic blocks associated to this particular function
basic_blocks: DenseMap<BasicBlock>,

/// Maps instructions to source locations
source_locations: SecondaryMap<Instruction, Location>,

/// The first basic block in the function
entry_block: BasicBlockId,

dfg: DataFlowGraph,
pub(crate) dfg: DataFlowGraph,
}

impl Function {
pub(crate) fn new(parameter_count: usize) -> Self {
/// Creates a new function with an automatically inserted entry block.
///
/// Note that any parameters to the function must be manually added later.
pub(crate) fn new() -> Self {
let mut dfg = DataFlowGraph::default();
let mut basic_blocks = DenseMap::default();

// The parameters for each function are stored as the block parameters
// of the function's entry block
let entry_block = basic_blocks.insert_with_id(|entry_block| {
// TODO: Give each parameter its correct type
let parameters = vecmap(0..parameter_count, |i| {
dfg.make_value(Value::Param { block: entry_block, position: i, typ: Type::Unit })
});

BasicBlock::new(parameters)
});

Self { basic_blocks, source_locations: SecondaryMap::new(), entry_block, dfg }
let entry_block = dfg.new_block();
Self { source_locations: SecondaryMap::new(), entry_block, dfg }
}

pub(crate) fn entry_block(&self) -> BasicBlockId {
Expand Down
Loading