Skip to content

Commit

Permalink
chore(ssa refactor): Add debug printing for the new ssa ir (#1211)
Browse files Browse the repository at this point in the history
Implement debug printing for the new ssa ir
  • Loading branch information
jfecher authored Apr 24, 2023
1 parent 4427898 commit 12691f7
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 14 deletions.
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ 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(),
}
}
}
30 changes: 29 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
basic_block::{BasicBlock, BasicBlockId},
constant::NumericConstant,
constant::{NumericConstant, NumericConstantId},
function::Signature,
instruction::{Instruction, InstructionId},
map::{DenseMap, Id, SecondaryMap, TwoWayMap},
Expand Down Expand Up @@ -209,6 +209,34 @@ impl DataFlowGraph {
}
}

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)]
mod tests {
use super::DataFlowGraph;
Expand Down
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
21 changes: 20 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ pub(crate) type InstructionId = Id<Instruction>;
/// of this is println.
pub(crate) struct IntrinsicOpcodes;

impl std::fmt::Display for IntrinsicOpcodes {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!("intrinsics have no opcodes yet")
}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
/// Instructions are used to perform tasks.
/// The instructions that the IR is able to specify are listed below.
Expand Down Expand Up @@ -189,5 +195,18 @@ pub(crate) enum BinaryOp {
/// Checks whether two types are equal.
/// Returns true if the types were not equal and
/// false otherwise.
Ne,
Neq,
}

impl std::fmt::Display for BinaryOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryOp::Add => write!(f, "add"),
BinaryOp::Sub => write!(f, "sub"),
BinaryOp::Mul => write!(f, "mul"),
BinaryOp::Div => write!(f, "div"),
BinaryOp::Eq => write!(f, "eq"),
BinaryOp::Neq => write!(f, "neq"),
}
}
}
6 changes: 6 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ impl<T> std::fmt::Debug for Id<T> {
}
}

impl<T> std::fmt::Display for Id<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "${}", self.index)
}
}

/// A DenseMap is a Vec wrapper where each element corresponds
/// to a unique ID that can be used to access the element. No direct
/// access to indices is provided. Since IDs must be stable and correspond
Expand Down
115 changes: 115 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! This file is for pretty-printing the SSA IR in a human-readable form for debugging.
use std::fmt::{Formatter, Result};

use iter_extended::vecmap;

use super::{
basic_block::BasicBlockId,
function::Function,
instruction::{Instruction, InstructionId, TerminatorInstruction},
value::ValueId,
};

pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result {
writeln!(f, "fn {} {{", function.name)?;
display_block_with_successors(function, function.entry_block, f)?;
write!(f, "}}")
}

pub(crate) fn display_block_with_successors(
function: &Function,
block_id: BasicBlockId,
f: &mut Formatter,
) -> Result {
display_block(function, block_id, f)?;

for successor in function.dfg[block_id].successors() {
display_block(function, successor, f)?;
}
Ok(())
}

pub(crate) fn display_block(
function: &Function,
block_id: BasicBlockId,
f: &mut Formatter,
) -> Result {
let block = &function.dfg[block_id];

writeln!(f, "{}({}):", block_id, value_list(block.parameters()))?;

for instruction in block.instructions() {
display_instruction(function, *instruction, f)?;
}

display_terminator(block.terminator(), f)
}

fn value_list(values: &[ValueId]) -> String {
vecmap(values, ToString::to_string).join(", ")
}

pub(crate) fn display_terminator(
terminator: Option<&TerminatorInstruction>,
f: &mut Formatter,
) -> Result {
match terminator {
Some(TerminatorInstruction::Jmp { destination, arguments }) => {
writeln!(f, " jmp {}({})", destination, value_list(arguments))
}
Some(TerminatorInstruction::JmpIf {
condition,
arguments,
then_destination,
else_destination,
}) => {
let args = value_list(arguments);
writeln!(
f,
" jmpif {}({}) then: {}, else: {}",
condition, args, then_destination, else_destination
)
}
Some(TerminatorInstruction::Return { return_values }) => {
writeln!(f, " return {}", value_list(return_values))
}
None => writeln!(f, " (no terminator instruction)"),
}
}

pub(crate) fn display_instruction(
function: &Function,
instruction: InstructionId,
f: &mut Formatter,
) -> Result {
// instructions are always indented within a function
write!(f, " ")?;

let results = function.dfg.instruction_results(instruction);
if !results.is_empty() {
write!(f, "{} = ", value_list(results))?;
}

match &function.dfg[instruction] {
Instruction::Binary(binary) => {
writeln!(f, "{} {}, {}", binary.operator, binary.lhs, binary.rhs)
}
Instruction::Cast(value, typ) => writeln!(f, "cast {value} as {typ}"),
Instruction::Not(value) => writeln!(f, "not {value}"),
Instruction::Truncate { value, bit_size, max_bit_size } => {
writeln!(f, "truncate {value} to {bit_size} bits, max_bit_size: {max_bit_size}")
}
Instruction::Constrain(value) => {
writeln!(f, "constrain {value}")
}
Instruction::Call { func, arguments } => {
writeln!(f, "call {func}({})", value_list(arguments))
}
Instruction::Intrinsic { func, arguments } => {
writeln!(f, "intrinsic {func}({})", value_list(arguments))
}
Instruction::Allocate { size } => writeln!(f, "alloc {size} fields"),
Instruction::Load { address } => writeln!(f, "load {address}"),
Instruction::Store { address, value } => writeln!(f, "store {value} at {address}"),
}
}
21 changes: 21 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,24 @@ impl Type {
Type::Numeric(NumericType::NativeField)
}
}

impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Numeric(numeric) => numeric.fmt(f),
Type::Reference => write!(f, "reference"),
Type::Function => write!(f, "function"),
Type::Unit => write!(f, "unit"),
}
}
}

impl std::fmt::Display for NumericType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumericType::Signed { bit_size } => write!(f, "i{bit_size}"),
NumericType::Unsigned { bit_size } => write!(f, "u{bit_size}"),
NumericType::NativeField => write!(f, "Field"),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ pub(crate) struct FunctionBuilder<'ssa> {
}

impl<'ssa> FunctionBuilder<'ssa> {
pub(crate) fn new(context: &'ssa SharedBuilderContext) -> Self {
let new_function = Function::new();
pub(crate) fn new(function_name: String, context: &'ssa SharedBuilderContext) -> Self {
let new_function = Function::new(function_name);
let current_block = new_function.entry_block();

Self {
Expand All @@ -43,8 +43,8 @@ impl<'ssa> FunctionBuilder<'ssa> {
}

/// Finish the current function and create a new function
pub(crate) fn new_function(&mut self) {
let new_function = Function::new();
pub(crate) fn new_function(&mut self, name: String) {
let new_function = Function::new(name);
let old_function = std::mem::replace(&mut self.current_function, new_function);

self.finished_functions.push((self.current_function_id, old_function));
Expand Down
7 changes: 4 additions & 3 deletions crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,23 @@ pub(super) struct SharedContext {

impl<'a> FunctionContext<'a> {
pub(super) fn new(
function_name: String,
parameters: &Parameters,
shared_context: &'a SharedContext,
shared_builder_context: &'a SharedBuilderContext,
) -> Self {
let mut this = Self {
definitions: HashMap::new(),
builder: FunctionBuilder::new(shared_builder_context),
builder: FunctionBuilder::new(function_name, shared_builder_context),
shared_context,
};
this.add_parameters_to_scope(parameters);
this
}

pub(super) fn new_function(&mut self, parameters: &Parameters) {
pub(super) fn new_function(&mut self, name: String, parameters: &Parameters) {
self.definitions.clear();
self.builder.new_function();
self.builder.new_function(name);
self.add_parameters_to_scope(parameters);
}

Expand Down
5 changes: 3 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ pub(crate) fn generate_ssa(program: Program) {
let builder_context = SharedBuilderContext::default();

let main = context.program.main();
let mut function_context =
FunctionContext::new(main.name.clone(), &main.parameters, &context, &builder_context);

let mut function_context = FunctionContext::new(&main.parameters, &context, &builder_context);
function_context.codegen_expression(&main.body);

while let Some((src_function_id, _new_id)) = context.pop_next_function_in_queue() {
let function = &context.program[src_function_id];
// TODO: Need to ensure/assert the new function's id == new_id
function_context.new_function(&function.parameters);
function_context.new_function(function.name.clone(), &function.parameters);
function_context.codegen_expression(&function.body);
}
}
Expand Down

0 comments on commit 12691f7

Please sign in to comment.