-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Initial SSA refactor module (#1113)
* add ssa refactor module * add more stub code * move value to its own file * add function * Rollback to simpler case * move types to types module * review * make types pub(crate) * allow dead code * add offline code * remove cfg * clean up * remove changes to old code * fix clippy * remove builder.rs * cargo fmt * clippy
- Loading branch information
1 parent
b799c8a
commit ef07731
Showing
12 changed files
with
613 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod variable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/// A variable in the SSA IR. | ||
/// By definition, a variable can only be defined once. | ||
/// | ||
/// As in Cranelift, we also allow variable use before definition. | ||
/// This will produce side-effects which will need to be handled | ||
/// before sealing a block. | ||
pub struct Variable(u32); | ||
|
||
impl From<u32> for Variable { | ||
fn from(value: u32) -> Self { | ||
Variable(value) | ||
} | ||
} | ||
impl From<u16> for Variable { | ||
fn from(value: u16) -> Self { | ||
Variable(value as u32) | ||
} | ||
} | ||
impl From<u8> for Variable { | ||
fn from(value: u8) -> Self { | ||
Variable(value as u32) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
//! SSA stands for Single Static Assignment | ||
//! The IR presented in this module will already | ||
//! be in SSA form and will be used to apply | ||
//! conventional optimizations like Common Subexpression | ||
//! elimination and constant folding. | ||
//! | ||
//! This module heavily borrows from Cranelift | ||
#[allow(dead_code)] | ||
mod basic_block; | ||
#[allow(dead_code)] | ||
mod dfg; | ||
#[allow(dead_code)] | ||
mod ir; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use super::ir::instruction::{Instruction, TerminatorInstruction}; | ||
|
||
/// A Basic block is a maximal collection of instructions | ||
/// such that there are only jumps at the end of block | ||
/// and one can only enter the block from the beginning. | ||
/// | ||
/// This means that if one instruction is executed in a basic | ||
/// 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>, | ||
/// Instructions in the basic block. | ||
instructions: Vec<Instruction>, | ||
|
||
/// A basic block is considered sealed | ||
/// if no further predecessors will be added to it. | ||
/// Since only filled blocks can have successors, | ||
/// predecessors are always filled. | ||
is_sealed: bool, | ||
|
||
/// The terminating instruction for the basic block. | ||
/// | ||
/// This will be a control flow instruction. | ||
terminator: TerminatorInstruction, | ||
} | ||
|
||
#[derive(Debug, PartialEq, Eq, Hash, Clone)] | ||
/// An identifier for a Basic Block. | ||
pub(crate) struct BasicBlockId; | ||
|
||
#[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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
use super::{ | ||
basic_block::{BasicBlock, BasicBlockId}, | ||
ir::{ | ||
extfunc::{SigRef, Signature}, | ||
instruction::{Instruction, InstructionId, Instructions}, | ||
types::Typ, | ||
value::{Value, ValueId}, | ||
}, | ||
}; | ||
use std::collections::HashMap; | ||
|
||
#[derive(Debug, Default)] | ||
/// A convenience wrapper to store `Value`s. | ||
pub(crate) struct ValueList(Vec<ValueId>); | ||
|
||
impl ValueList { | ||
/// Inserts an element to the back of the list and | ||
/// returns the `position` | ||
pub(crate) fn push(&mut self, value: ValueId) -> usize { | ||
self.0.push(value); | ||
self.len() - 1 | ||
} | ||
/// Returns the number of values in the list. | ||
fn len(&self) -> usize { | ||
self.0.len() | ||
} | ||
|
||
/// Removes all items from the list. | ||
fn clear(&mut self) { | ||
self.0.clear(); | ||
} | ||
/// Returns the ValueId's as a slice. | ||
pub(crate) fn as_slice(&self) -> &[ValueId] { | ||
&self.0 | ||
} | ||
} | ||
#[derive(Debug, Default)] | ||
pub(crate) struct DataFlowGraph { | ||
/// All of the instructions in a function | ||
instructions: Instructions, | ||
|
||
/// Stores the results for a particular instruction. | ||
/// | ||
/// An instruction may return multiple values | ||
/// and for this, we will also use the cranelift strategy | ||
/// to fetch them via indices. | ||
/// | ||
/// 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>, | ||
|
||
/// Storage for all of the values defined in this | ||
/// function. | ||
values: HashMap<ValueId, Value>, | ||
|
||
/// Function signatures of external methods | ||
signatures: HashMap<SigRef, Signature>, | ||
|
||
/// All blocks in a function | ||
blocks: HashMap<BasicBlockId, BasicBlock>, | ||
} | ||
|
||
impl DataFlowGraph { | ||
/// Creates a new `empty` basic block | ||
pub(crate) fn new_block(&mut self) -> BasicBlockId { | ||
todo!() | ||
} | ||
|
||
/// Inserts a new instruction into the DFG. | ||
pub(crate) fn make_instruction(&mut self, instruction_data: Instruction) -> InstructionId { | ||
let id = self.instructions.add_instruction(instruction_data); | ||
|
||
// Create a new vector to store the potential results | ||
// for the instruction. | ||
self.results.insert(id, Default::default()); | ||
|
||
id | ||
} | ||
|
||
/// Attaches results to the instruction. | ||
/// | ||
/// Returns the number of results that this instruction | ||
/// produces. | ||
pub(crate) fn make_instruction_results( | ||
&mut self, | ||
instruction_id: InstructionId, | ||
ctrl_typevar: Typ, | ||
) -> usize { | ||
// 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 | ||
} | ||
|
||
/// Return the result types of this instruction. | ||
/// | ||
/// For example, an addition instruction will return | ||
/// one type which is the type of the operands involved. | ||
/// This is the `ctrl_typevar` in this case. | ||
fn instruction_result_types( | ||
&self, | ||
instruction_id: InstructionId, | ||
ctrl_typevar: Typ, | ||
) -> Vec<Typ> { | ||
// Check if it is a call instruction. If so, we don't support that yet | ||
let ins_data = self.instructions.get_instruction(instruction_id); | ||
match ins_data { | ||
Instruction::Call { .. } => todo!("function calls are not supported yet"), | ||
ins => ins.return_types(ctrl_typevar), | ||
} | ||
} | ||
|
||
/// Appends a result type to the instruction. | ||
pub(crate) fn append_result(&mut self, instruction_id: InstructionId, typ: Typ) -> ValueId { | ||
let next_value_id = self.next_value(); | ||
|
||
// Add value to the list of results for this instruction | ||
let res_position = self.results.get_mut(&instruction_id).unwrap().push(next_value_id); | ||
|
||
self.make_value(Value::Instruction { | ||
typ, | ||
position: res_position as u16, | ||
instruction: instruction_id, | ||
}) | ||
} | ||
|
||
/// Stores a value and returns its `ValueId` reference. | ||
fn make_value(&mut self, data: Value) -> ValueId { | ||
let next_value = self.next_value(); | ||
|
||
self.values.insert(next_value, data); | ||
|
||
next_value | ||
} | ||
|
||
/// Returns the next `ValueId` | ||
fn next_value(&self) -> ValueId { | ||
ValueId(self.values.len() as u32) | ||
} | ||
|
||
/// Returns the number of instructions | ||
/// inserted into functions. | ||
pub(crate) fn num_instructions(&self) -> usize { | ||
self.instructions.num_instructions() | ||
} | ||
|
||
/// Returns all of result values which are attached to this instruction. | ||
pub(crate) fn instruction_results(&self, instruction_id: InstructionId) -> &[ValueId] { | ||
self.results.get(&instruction_id).expect("expected a list of Values").as_slice() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::DataFlowGraph; | ||
use crate::ssa_refactor::ir::{ | ||
instruction::Instruction, | ||
types::{NumericType, Typ}, | ||
}; | ||
use acvm::FieldElement; | ||
|
||
#[test] | ||
fn make_instruction() { | ||
let mut dfg = DataFlowGraph::default(); | ||
let ins = Instruction::Immediate { value: FieldElement::from(0u128) }; | ||
let ins_id = dfg.make_instruction(ins); | ||
|
||
let num_results = | ||
dfg.make_instruction_results(ins_id, Typ::Numeric(NumericType::NativeField)); | ||
|
||
let results = dfg.instruction_results(ins_id); | ||
|
||
assert_eq!(results.len(), num_results); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pub(crate) mod extfunc; | ||
mod function; | ||
pub(crate) mod instruction; | ||
pub(crate) mod types; | ||
pub(crate) mod value; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
//! Like Crane-lift all functions outside of the current function is seen as | ||
//! external. | ||
//! To reference external functions, one uses | ||
|
||
use super::types::Typ; | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub(crate) struct Signature { | ||
pub(crate) params: Vec<Typ>, | ||
pub(crate) returns: Vec<Typ>, | ||
} | ||
/// Reference to a `Signature` in a map inside of | ||
/// a functions DFG. | ||
#[derive(Debug, Default, Clone, Copy)] | ||
pub(crate) struct SigRef(pub(crate) u32); | ||
|
||
#[test] | ||
fn sign_smoke() { | ||
let mut signature = Signature::default(); | ||
|
||
signature.params.push(Typ::Numeric(super::types::NumericType::NativeField)); | ||
signature.returns.push(Typ::Numeric(super::types::NumericType::Unsigned { bit_size: 32 })); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
use crate::ssa_refactor::basic_block::{BasicBlock, BasicBlockId}; | ||
|
||
use super::instruction::Instruction; | ||
|
||
use noirc_errors::Location; | ||
use std::collections::HashMap; | ||
|
||
/// A function holds a list of instructions. | ||
/// These instructions are further grouped into | ||
/// Basic blocks | ||
#[derive(Debug)] | ||
pub(crate) struct Function { | ||
/// Basic blocks associated to this particular function | ||
basic_blocks: HashMap<BasicBlockId, BasicBlock>, | ||
|
||
/// Maps instructions to source locations | ||
source_locations: HashMap<Instruction, Location>, | ||
|
||
/// The first basic block in the function | ||
entry_block: BasicBlockId, | ||
} | ||
|
||
/// FunctionId is a reference for a function | ||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] | ||
pub(crate) struct FunctionId(pub(crate) u32); |
Oops, something went wrong.