Skip to content

Commit

Permalink
feat: SSA globals in monomorphization and SSA gen (noir-lang/noir#6985)
Browse files Browse the repository at this point in the history
feat!: disallow calling unconstrained functions outside of `unsafe` blocks and passing unconstrained functions in place of constrained functions (noir-lang/noir#6938)
chore: simplify a couple of enum variants (noir-lang/noir#7025)
chore: disallow inserting ACIR-only instructions into brillig functions (noir-lang/noir#7017)
feat: unchecked math operations in SSA (noir-lang/noir#7011)
chore: delete docs for versions which aren't used (noir-lang/noir#7020)
feat!: Disable mocks in `execute` (noir-lang/noir#6869)
chore(docs): backport 1.0.0-beta.0 doc fixes (noir-lang/noir#7014)
feat(cli): Add CLI option to filter by contract function name (noir-lang/noir#7018)
chore: Add more Field use info (noir-lang/noir#7019)
fix: let static_assert fail with the provided message (noir-lang/noir#7005)
chore: mark `aztec-nr` as expected to compile (noir-lang/noir#7015)
chore: clarity fix in docs (noir-lang/noir#7016)
fix: require generic trait impls to be in scope to call them (noir-lang/noir#6913)
feat!: require trait primitive functions/calls to have their trait in scope (noir-lang/noir#6901)
feat(lsp): use trait method docs for trait impl method docs on hover (noir-lang/noir#7003)
chore: turn on averaging for protocol circuits metrics in CI (noir-lang/noir#6999)
feat(comptime): Implement to_be_bits and to_le_bits in the interpreter (noir-lang/noir#7008)
chore: Add short circuit in ssa-gen for known if conditions (noir-lang/noir#7007)
chore: Only resolved globals monomorphization (noir-lang/noir#7006)
chore: Remove resolve_is_unconstrained pass (noir-lang/noir#7004)
chore: require safety doc comment for unsafe instead of `//@safety` (noir-lang/noir#6992)
fix: Reproduce and fix bytecode blowup (noir-lang/noir#6972)
chore: mark casts as able to be deduplicated (noir-lang/noir#6996)
fix: return trait impl method as FuncId if there's only one (noir-lang/noir#6989)
chore(ci): fail properly in `external-repo-checks` (noir-lang/noir#6988)
fix: allow multiple trait impls for the same trait as long as one is in scope (noir-lang/noir#6987)
chore: Use DFG in SSA printer (noir-lang/noir#6986)
chore!: Reserve `enum` and `match` keywords (noir-lang/noir#6961)
  • Loading branch information
AztecBot committed Jan 11, 2025
2 parents 65969cb + 0ea85f5 commit d10c025
Show file tree
Hide file tree
Showing 30 changed files with 385 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f6ed6aa9f469c719ca47d01e3118f37cedde0f9e
db28cb9ffb710c286b54dbfcf57292ae3dffb03d
4 changes: 2 additions & 2 deletions noir/noir-repo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1892,6 +1892,9 @@ impl<'a> Context<'a> {
Value::Instruction { .. } | Value::Param { .. } => {
unreachable!("ICE: Should have been in cache {value_id} {value:?}")
}
Value::Global(_) => {
unreachable!("ICE: All globals should have been inlined");
}
};
self.ssa_values.insert(value_id, acir_value.clone());
acir_value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,10 @@ impl<'block> BrilligBlock<'block> {
}
}
}
Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => {
Value::Instruction { .. }
| Value::Param { .. }
| Value::NumericConstant { .. }
| Value::Global(_) => {
unreachable!("unsupported function call type {:?}", dfg[*func])
}
},
Expand Down Expand Up @@ -795,7 +798,7 @@ impl<'block> BrilligBlock<'block> {
self.brillig_context.deallocate_register(rc_register);
}
Instruction::EnableSideEffectsIf { .. } => {
todo!("enable_side_effects not supported by brillig")
unreachable!("enable_side_effects not supported by brillig")
}
Instruction::IfElse { .. } => {
unreachable!("IfElse instructions should not be possible in brillig")
Expand Down Expand Up @@ -1557,6 +1560,9 @@ impl<'block> BrilligBlock<'block> {
let value = &dfg[value_id];

match value {
Value::Global(_) => {
unreachable!("ICE: All globals should have been inlined");
}
Value::Param { .. } | Value::Instruction { .. } => {
// All block parameters and instruction results should have already been
// converted to registers so we fetch from the cache.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ pub(crate) fn collect_variables_of_value(
let value = &dfg[value_id];

match value {
Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => {
Some(value_id)
}
Value::Instruction { .. }
| Value::Param { .. }
| Value::NumericConstant { .. }
| Value::Global(_) => Some(value_id),
// Functions are not variables in a defunctionalized SSA. Only constant function values should appear.
Value::ForeignFunction(_) | Value::Function(_) | Value::Intrinsic(..) => None,
}
Expand Down
2 changes: 1 addition & 1 deletion noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ impl SsaBuilder {
let ssa_path = emit_ssa.with_extension("ssa.json");
write_to_file(&serde_json::to_vec(&ssa).unwrap(), &ssa_path);
}
Ok(SsaBuilder { ssa_logging, print_codegen_timings, ssa }.print("Initial SSA:"))
Ok(SsaBuilder { ssa_logging, print_codegen_timings, ssa }.print("Initial SSA"))
}

fn finish(self) -> Ssa {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,8 @@ impl DependencyContext {
}
Value::Instruction { .. }
| Value::NumericConstant { .. }
| Value::Param { .. } => {
| Value::Param { .. }
| Value::Global(_) => {
panic!(
"calling non-function value with ID {func_id} in function {}",
function.name()
Expand Down Expand Up @@ -618,7 +619,8 @@ impl Context {
}
Value::Instruction { .. }
| Value::NumericConstant { .. }
| Value::Param { .. } => {
| Value::Param { .. }
| Value::Global(_) => {
panic!("At the point we are running disconnect there shouldn't be any other values as arguments")
}
}
Expand Down
22 changes: 20 additions & 2 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use iter_extended::vecmap;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde_with::DisplayFromStr;
use tracing::warn;

/// The DataFlowGraph contains most of the actual data in a function including
/// its blocks, instructions, and values. This struct is largely responsible for
Expand Down Expand Up @@ -181,7 +182,16 @@ impl DataFlowGraph {

/// Check if the function runtime would simply ignore this instruction.
pub(crate) fn is_handled_by_runtime(&self, instruction: &Instruction) -> bool {
!(self.runtime().is_acir() && instruction.is_brillig_only())
match self.runtime() {
RuntimeType::Acir(_) => !matches!(
instruction,
Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. }
),
RuntimeType::Brillig(_) => !matches!(
instruction,
Instruction::EnableSideEffectsIf { .. } | Instruction::IfElse { .. }
),
}
}

fn insert_instruction_without_simplification(
Expand All @@ -205,7 +215,7 @@ impl DataFlowGraph {
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction_data) {
return InsertInstructionResult::InstructionRemoved;
panic!("Attempted to insert instruction not handled by runtime: {instruction_data:?}");
}

let id = self.insert_instruction_without_simplification(
Expand All @@ -228,6 +238,7 @@ impl DataFlowGraph {
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction) {
warn!("Attempted to insert instruction not handled by runtime: {instruction:?}");
return InsertInstructionResult::InstructionRemoved;
}

Expand Down Expand Up @@ -326,6 +337,10 @@ impl DataFlowGraph {
id
}

pub(crate) fn make_global(&mut self, typ: Type) -> ValueId {
self.values.insert(Value::Global(typ))
}

/// Gets or creates a ValueId for the given FunctionId.
pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId {
if let Some(existing) = self.functions.get(&function) {
Expand Down Expand Up @@ -614,6 +629,9 @@ impl DataFlowGraph {
}
_ => false,
},
// TODO: Make this true and handle instruction simplifications with globals.
// Currently all globals are inlined as a temporary measure so this is fine to have as false.
Value::Global(_) => false,
_ => true,
}
}
Expand Down
22 changes: 12 additions & 10 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub(crate) struct Function {
/// Name of the function for debugging only
name: String,

id: FunctionId,
id: Option<FunctionId>,

/// The DataFlowGraph holds the majority of data pertaining to the function
/// including its blocks, instructions, and values.
Expand All @@ -90,14 +90,22 @@ impl Function {
pub(crate) fn new(name: String, id: FunctionId) -> Self {
let mut dfg = DataFlowGraph::default();
let entry_block = dfg.make_block();
Self { name, id, entry_block, dfg }
Self { name, id: Some(id), entry_block, dfg }
}

/// Globals are generated using the same codegen process as functions.
/// To avoid a recursive global context we should create a pseudo function to mock a globals context.
pub(crate) fn new_for_globals() -> Self {
let mut dfg = DataFlowGraph::default();
let entry_block = dfg.make_block();
Self { name: "globals".to_owned(), id: None, entry_block, dfg }
}

/// Creates a new function as a clone of the one passed in with the passed in id.
pub(crate) fn clone_with_id(id: FunctionId, another: &Function) -> Self {
let dfg = another.dfg.clone();
let entry_block = another.entry_block;
Self { name: another.name.clone(), id, entry_block, dfg }
Self { name: another.name.clone(), id: Some(id), entry_block, dfg }
}

/// Takes the signature (function name & runtime) from a function but does not copy the body.
Expand All @@ -115,7 +123,7 @@ impl Function {

/// The id of the function.
pub(crate) fn id(&self) -> FunctionId {
self.id
self.id.expect("FunctionId should be initialized")
}

/// Runtime type of the function.
Expand Down Expand Up @@ -228,12 +236,6 @@ 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
71 changes: 57 additions & 14 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! This file is for pretty-printing the SSA IR in a human-readable form for debugging.
use std::fmt::{Formatter, Result};
use std::fmt::{Display, Formatter, Result};

use acvm::acir::AcirField;
use im::Vector;
use iter_extended::vecmap;

use crate::ssa::ir::types::{NumericType, Type};
use crate::ssa::{
ir::types::{NumericType, Type},
Ssa,
};

use super::{
basic_block::BasicBlockId,
Expand All @@ -15,8 +18,42 @@ use super::{
value::{Value, ValueId},
};

impl Display for Ssa {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for (id, global_value) in self.globals.dfg.values_iter() {
match global_value {
Value::NumericConstant { constant, typ } => {
writeln!(f, "g{} = {typ} {constant}", id.to_u32())?;
}
Value::Instruction { instruction, .. } => {
display_instruction(&self.globals.dfg, *instruction, true, f)?;
}
Value::Global(_) => {
panic!("Value::Global should only be in the function dfg");
}
_ => panic!("Expected only numeric constant or instruction"),
};
}

if self.globals.dfg.values_iter().len() > 0 {
writeln!(f)?;
}

for function in self.functions.values() {
writeln!(f, "{function}")?;
}
Ok(())
}
}

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

/// Helper function for Function's Display impl to pretty-print the function with the given formatter.
pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result {
fn display_function(function: &Function, f: &mut Formatter) -> Result {
writeln!(f, "{} fn {} {} {{", function.runtime(), function.name(), function.id())?;
for block_id in function.reachable_blocks() {
display_block(&function.dfg, block_id, f)?;
Expand All @@ -25,17 +62,13 @@ pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result
}

/// Display a single block. This will not display the block's successors.
pub(crate) fn display_block(
dfg: &DataFlowGraph,
block_id: BasicBlockId,
f: &mut Formatter,
) -> Result {
fn display_block(dfg: &DataFlowGraph, block_id: BasicBlockId, f: &mut Formatter) -> Result {
let block = &dfg[block_id];

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

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

display_terminator(dfg, block.terminator(), f)
Expand All @@ -53,6 +86,9 @@ fn value(dfg: &DataFlowGraph, id: ValueId) -> String {
Value::Intrinsic(intrinsic) => intrinsic.to_string(),
Value::ForeignFunction(function) => function.clone(),
Value::Param { .. } | Value::Instruction { .. } => id.to_string(),
Value::Global(_) => {
format!("g{}", id.to_u32())
}
}
}

Expand All @@ -72,7 +108,7 @@ fn value_list(dfg: &DataFlowGraph, values: &[ValueId]) -> String {
}

/// Display a terminator instruction
pub(crate) fn display_terminator(
fn display_terminator(
dfg: &DataFlowGraph,
terminator: Option<&TerminatorInstruction>,
f: &mut Formatter,
Expand Down Expand Up @@ -107,17 +143,24 @@ pub(crate) fn display_terminator(
}

/// Display an arbitrary instruction
pub(crate) fn display_instruction(
fn display_instruction(
dfg: &DataFlowGraph,
instruction: InstructionId,
in_global_space: bool,
f: &mut Formatter,
) -> Result {
// instructions are always indented within a function
write!(f, " ")?;
if !in_global_space {
// instructions are always indented within a function
write!(f, " ")?;
}

let results = dfg.instruction_results(instruction);
if !results.is_empty() {
write!(f, "{} = ", value_list(dfg, results))?;
let mut value_list = value_list(dfg, results);
if in_global_space {
value_list = value_list.replace('v', "g");
}
write!(f, "{} = ", value_list)?;
}

display_instruction_inner(dfg, &dfg[instruction], results, f)
Expand Down
4 changes: 4 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub(crate) enum Value {
/// ForeignFunction's always have the type Type::Function and have similar semantics to Function,
/// other than generating different backend operations and being only accessible through Brillig.
ForeignFunction(String),

/// This Value indicates we have a reserved slot that needs to be accessed in a separate global context
Global(Type),
}

impl Value {
Expand All @@ -64,6 +67,7 @@ impl Value {
Value::Function { .. } | Value::Intrinsic { .. } | Value::ForeignFunction { .. } => {
Cow::Owned(Type::Function)
}
Value::Global(typ) => Cow::Borrowed(typ),
}
}
}
Loading

0 comments on commit d10c025

Please sign in to comment.