Skip to content

Commit

Permalink
chore(ssa refactor): Handle codegen for literals (#1209)
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

* Correctly handle function parameters

* Rename Nested to Tree; add comment

* Add codegen for literals

* PR feedback

* chore(ssa refactor): Add debug printing for the new ssa ir (#1211)

Implement debug printing for the new ssa ir
  • Loading branch information
jfecher authored Apr 24, 2023
1 parent ac87a80 commit dc3fb48
Show file tree
Hide file tree
Showing 14 changed files with 566 additions and 60 deletions.
2 changes: 2 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub(crate) mod basic_block;
pub(crate) mod constant;
pub(crate) mod dfg;
pub(crate) mod function;
pub(crate) mod instruction;
pub(crate) mod map;
pub(crate) mod printer;
pub(crate) mod types;
pub(crate) mod value;
22 changes: 22 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/basic_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,29 @@ impl BasicBlock {
self.instructions.push(instruction);
}

pub(crate) fn instructions(&self) -> &[InstructionId] {
&self.instructions
}

pub(crate) fn set_terminator(&mut self, terminator: TerminatorInstruction) {
self.terminator = Some(terminator);
}

pub(crate) fn terminator(&self) -> Option<&TerminatorInstruction> {
self.terminator.as_ref()
}

/// Iterate over all the successors of the currently block, as determined by
/// the blocks jumped to in the terminator instruction. If there is no terminator
/// instruction yet, this will iterate 0 times.
pub(crate) fn successors(&self) -> impl ExactSizeIterator<Item = BasicBlockId> {
match &self.terminator {
Some(TerminatorInstruction::Jmp { destination, .. }) => vec![*destination].into_iter(),
Some(TerminatorInstruction::JmpIf { then_destination, else_destination, .. }) => {
vec![*then_destination, *else_destination].into_iter()
}
Some(TerminatorInstruction::Return { .. }) => vec![].into_iter(),
None => vec![].into_iter(),
}
}
}
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)
}
}
76 changes: 64 additions & 12 deletions crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use super::{
basic_block::{BasicBlock, BasicBlockId},
constant::{NumericConstant, NumericConstantId},
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)]
Expand All @@ -20,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 @@ -29,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 @@ -55,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 Down Expand Up @@ -91,41 +100,50 @@ impl DataFlowGraph {
}

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

/// Insert a value into the dfg's storage and return an id to reference it.
/// Until the value is used in an instruction it is unreachable.
pub(crate) fn make_value(&mut self, value: Value) -> ValueId {
self.values.insert(value)
}

/// Attaches results to the instruction.
/// Creates a new constant value, or returns the Id to an existing one if
/// one already exists.
pub(crate) fn make_constant(&mut self, value: FieldElement, typ: Type) -> ValueId {
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 @@ -181,6 +199,42 @@ impl DataFlowGraph {
block.add_parameter(parameter);
parameter
}

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

impl std::ops::Index<InstructionId> for DataFlowGraph {
type Output = Instruction;
fn index(&self, id: InstructionId) -> &Self::Output {
&self.instructions[id]
}
}

impl std::ops::Index<ValueId> for DataFlowGraph {
type Output = Value;
fn index(&self, id: ValueId) -> &Self::Output {
&self.values[id]
}
}

impl std::ops::Index<NumericConstantId> for DataFlowGraph {
type Output = NumericConstant;
fn index(&self, id: NumericConstantId) -> &Self::Output {
&self.constants[id]
}
}

impl std::ops::Index<BasicBlockId> for DataFlowGraph {
type Output = BasicBlock;
fn index(&self, id: BasicBlockId) -> &Self::Output {
&self.blocks[id]
}
}

#[cfg(test)]
Expand All @@ -190,19 +244,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);
}
}
15 changes: 12 additions & 3 deletions crates/noirc_evaluator/src/ssa_refactor/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ pub(crate) struct Function {
source_locations: SecondaryMap<Instruction, Location>,

/// The first basic block in the function
entry_block: BasicBlockId,
pub(super) entry_block: BasicBlockId,

/// Name of the function for debugging only
pub(super) name: String,

pub(crate) dfg: DataFlowGraph,
}
Expand All @@ -27,10 +30,10 @@ impl Function {
/// 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 {
pub(crate) fn new(name: String) -> Self {
let mut dfg = DataFlowGraph::default();
let entry_block = dfg.new_block();
Self { source_locations: SecondaryMap::new(), entry_block, dfg }
Self { name, source_locations: SecondaryMap::new(), entry_block, dfg }
}

pub(crate) fn entry_block(&self) -> BasicBlockId {
Expand All @@ -47,6 +50,12 @@ pub(crate) struct Signature {
pub(crate) returns: Vec<Type>,
}

impl std::fmt::Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
super::printer::display_function(self, f)
}
}

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

0 comments on commit dc3fb48

Please sign in to comment.