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): Add Context structs and start ssa gen pass #1196

Merged
merged 7 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
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>,
kevaundray marked this conversation as resolved.
Show resolved Hide resolved

/// 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