Skip to content

Commit

Permalink
chore(ssa refactor): Add Context structs and start ssa gen pass (#1196)
Browse files Browse the repository at this point in the history
* Add Context structs and start ssa gen pass

* Fix block arguments

* Fix clippy lint

* Use the correct dfg

* Rename contexts to highlight the inner contexts are shared rather than used directly

* Update crates/noirc_evaluator/src/ssa_refactor/ssa_gen/value.rs

Co-authored-by: kevaundray <kevtheappdev@gmail.com>

---------

Co-authored-by: kevaundray <kevtheappdev@gmail.com>
  • Loading branch information
jfecher and kevaundray authored Apr 21, 2023
1 parent 7f8d2c2 commit e4c7bb2
Show file tree
Hide file tree
Showing 16 changed files with 442 additions and 114 deletions.
2 changes: 1 addition & 1 deletion crates/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl Evaluator {
// u8 and arrays are assumed to be private
// This is not a short-coming of the ABI, but of the grammar
// The new grammar has been conceived, and will be implemented.
let main = ir_gen.program.main();
let main = ir_gen.program.main_mut();
let main_params = std::mem::take(&mut main.parameters);
let abi_params = std::mem::take(&mut ir_gen.program.main_function_signature.0);

Expand Down
9 changes: 4 additions & 5 deletions crates/noirc_evaluator/src/ssa_refactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
//! elimination and constant folding.
//!
//! This module heavily borrows from Cranelift
#[allow(dead_code)]
mod basic_block;
#[allow(dead_code)]
mod dfg;
#[allow(dead_code)]
#![allow(dead_code)]

mod ir;
mod ssa_builder;
mod ssa_gen;
5 changes: 3 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ir.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub(crate) mod extfunc;
mod function;
pub(crate) mod basic_block;
pub(crate) mod dfg;
pub(crate) mod function;
pub(crate) mod instruction;
pub(crate) mod map;
pub(crate) mod types;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use super::ir::instruction::{Instruction, TerminatorInstruction};
use super::{
instruction::{InstructionId, TerminatorInstruction},
map::Id,
value::ValueId,
};

/// A Basic block is a maximal collection of instructions
/// such that there are only jumps at the end of block
Expand All @@ -8,10 +12,11 @@ use super::ir::instruction::{Instruction, TerminatorInstruction};
/// block, then all instructions are executed. ie single-entry single-exit.
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub(crate) struct BasicBlock {
/// Arguments to the basic block.
phi_nodes: Vec<BlockArguments>,
/// Parameters to the basic block.
parameters: Vec<ValueId>,

/// Instructions in the basic block.
instructions: Vec<Instruction>,
instructions: Vec<InstructionId>,

/// A basic block is considered sealed
/// if no further predecessors will be added to it.
Expand All @@ -21,17 +26,16 @@ pub(crate) struct BasicBlock {

/// The terminating instruction for the basic block.
///
/// This will be a control flow instruction.
terminator: TerminatorInstruction,
/// This will be a control flow instruction. This is only
/// None if the block is still being constructed.
terminator: Option<TerminatorInstruction>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
/// An identifier for a Basic Block.
pub(crate) struct BasicBlockId;
pub(crate) type BasicBlockId = Id<BasicBlock>;

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
/// Arguments to the basic block.
/// We use the modern Crane-lift strategy
/// of representing phi nodes as basic block
/// arguments.
pub(crate) struct BlockArguments;
impl BasicBlock {
pub(super) fn new(parameters: Vec<ValueId>) -> Self {
Self { parameters, instructions: Vec::new(), is_sealed: false, terminator: None }
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use super::{
basic_block::{BasicBlock, BasicBlockId},
ir::{
extfunc::Signature,
instruction::{Instruction, InstructionId, Instructions},
map::{Id, SparseMap},
types::Type,
value::{Value, ValueId},
},
function::Signature,
instruction::{Instruction, InstructionId},
map::{DenseMap, Id, SecondaryMap},
types::Type,
value::{Value, ValueId},
};
use std::collections::HashMap;

#[derive(Debug, Default)]
/// A convenience wrapper to store `Value`s.
Expand Down Expand Up @@ -39,7 +36,7 @@ impl ValueList {
#[derive(Debug, Default)]
pub(crate) struct DataFlowGraph {
/// All of the instructions in a function
instructions: Instructions,
instructions: DenseMap<Instruction>,

/// Stores the results for a particular instruction.
///
Expand All @@ -50,17 +47,17 @@ 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: HashMap<InstructionId, ValueList>,
results: SecondaryMap<Instruction, ValueList>,

/// Storage for all of the values defined in this
/// function.
values: SparseMap<Value>,
values: DenseMap<Value>,

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

/// All blocks in a function
blocks: SparseMap<BasicBlock>,
blocks: DenseMap<BasicBlock>,
}

impl DataFlowGraph {
Expand All @@ -71,13 +68,17 @@ impl DataFlowGraph {

/// Inserts a new instruction into the DFG.
pub(crate) fn make_instruction(&mut self, instruction_data: Instruction) -> InstructionId {
let id = self.instructions.push(instruction_data);
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
}

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

/// Attaches results to the instruction.
///
/// Returns the number of results that this instruction
Expand Down Expand Up @@ -126,9 +127,9 @@ impl DataFlowGraph {
let results = self.results.get_mut(&instruction_id).unwrap();
let expected_res_position = results.len();

let value_id = self.values.push(Value::Instruction {
let value_id = self.values.insert(Value::Instruction {
typ,
position: expected_res_position as u16,
position: expected_res_position,
instruction: instruction_id,
});

Expand Down
20 changes: 0 additions & 20 deletions crates/noirc_evaluator/src/ssa_refactor/ir/extfunc.rs

This file was deleted.

63 changes: 54 additions & 9 deletions crates/noirc_evaluator/src/ssa_refactor/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,70 @@
use crate::ssa_refactor::basic_block::{BasicBlock, BasicBlockId};

use super::basic_block::{BasicBlock, BasicBlockId};
use super::dfg::DataFlowGraph;
use super::instruction::Instruction;
use super::map::{DenseMap, Id, SecondaryMap};
use super::types::Type;
use super::value::Value;

use iter_extended::vecmap;
use noirc_errors::Location;
use std::collections::HashMap;

/// A function holds a list of instructions.
/// These instructions are further grouped into
/// Basic blocks
/// These instructions are further grouped into Basic blocks
///
/// Like Crane-lift all functions outside of the current function is seen as external.
/// To reference external functions, one must first import the function signature
/// into the current function's context.
#[derive(Debug)]
pub(crate) struct Function {
/// Basic blocks associated to this particular function
basic_blocks: HashMap<BasicBlockId, BasicBlock>,
basic_blocks: DenseMap<BasicBlock>,

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

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

dfg: DataFlowGraph,
}

impl Function {
pub(crate) fn new(parameter_count: usize) -> 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 }
}

pub(crate) fn entry_block(&self) -> BasicBlockId {
self.entry_block
}
}

/// FunctionId is a reference for a function
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub(crate) struct FunctionId(pub(crate) u32);
pub(crate) type FunctionId = Id<Function>;

#[derive(Debug, Default, Clone)]
pub(crate) struct Signature {
pub(crate) params: Vec<Type>,
pub(crate) returns: Vec<Type>,
}

#[test]
fn sign_smoke() {
let mut signature = Signature::default();

signature.params.push(Type::Numeric(super::types::NumericType::NativeField));
signature.returns.push(Type::Numeric(super::types::NumericType::Unsigned { bit_size: 32 }));
}
44 changes: 3 additions & 41 deletions crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
use acvm::FieldElement;

use super::{
function::FunctionId,
map::{Id, SparseMap},
types::Type,
value::ValueId,
basic_block::BasicBlockId, function::FunctionId, map::Id, types::Type, value::ValueId,
};
use crate::ssa_refactor::basic_block::{BasicBlockId, BlockArguments};

// Container for all Instructions, per-function
pub(crate) type Instructions = SparseMap<Instruction>;

/// Reference to an instruction
pub(crate) type InstructionId = Id<Instruction>;
Expand Down Expand Up @@ -134,12 +127,12 @@ pub(crate) enum TerminatorInstruction {
condition: ValueId,
then_destination: BasicBlockId,
else_destination: BasicBlockId,
arguments: BlockArguments,
arguments: Vec<ValueId>,
},
/// Unconditional Jump
///
/// Jumps to specified `destination` with `arguments`
Jmp { destination: BasicBlockId, arguments: BlockArguments },
Jmp { destination: BasicBlockId, arguments: Vec<ValueId> },
}

/// A binary instruction in the IR.
Expand Down Expand Up @@ -181,34 +174,3 @@ pub(crate) enum BinaryOp {
/// false otherwise.
Ne,
}

#[test]
fn smoke_instructions_map_duplicate() {
let id = Id::test_new(0);

let ins = Instruction::Not(id);
let same_ins = Instruction::Not(id);

let mut ins_map = Instructions::default();

// Document what happens when we insert the same instruction twice
let id = ins_map.push(ins);
let id_same_ins = ins_map.push(same_ins);

// The map is quite naive and does not check if the instruction has ben inserted
// before. We simply assign a different Id.
assert_ne!(id, id_same_ins)
}

#[test]
fn num_instructions_smoke() {
let n = 100;

let mut ins_map = Instructions::default();
for i in 0..n {
let ins = Instruction::Not(Id::test_new(i));
ins_map.push(ins);
}

assert_eq!(n, ins_map.len())
}
Loading

0 comments on commit e4c7bb2

Please sign in to comment.