Skip to content

Commit

Permalink
feat: Sync from noir (#11279)
Browse files Browse the repository at this point in the history
Automated pull of development from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore: add regression test for #6530
(noir-lang/noir#7089)
feat: add `ConstrainNotEqual` instruction
(noir-lang/noir#7032)
feat!: `loop` statements (only frontend)
(noir-lang/noir#7092)
fix!: Include kind in `StructDefinition::generics` and fix derivation of
Eq in structs with numeric generics
(noir-lang/noir#7076)
chore: add test for isuee #7090
(noir-lang/noir#7091)
chore: mark `noir-edwards` as expected to compile
(noir-lang/noir#7085)
chore: allow passing custom conditions to inlining pass
(noir-lang/noir#7083)
feat(LSP): auto-import trait reexport if trait is not visible
(noir-lang/noir#7079)
fix: Allow implicit associated types on integer type kinds
(noir-lang/noir#7078)
feat(ssa): Treat globals as constant in a function's DFG
(noir-lang/noir#7040)
chore: Do not make new instruction if it hasn't changed
(noir-lang/noir#7069)
fix: do not remove memory blocks used as brillig input
(noir-lang/noir#7073)
END_COMMIT_OVERRIDE

---------

Co-authored-by: Tom French <[email protected]>
  • Loading branch information
AztecBot and TomAFrench authored Jan 17, 2025
1 parent 07fabe8 commit a964cd0
Show file tree
Hide file tree
Showing 58 changed files with 1,197 additions and 299 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9471e28ad6f02bf2fae3782c3db68106b615595f
c172880ae47ec4906cda662801bd4b7866c9586b
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use acir::circuit::{opcodes::BlockId, Circuit, Opcode};
use acir::circuit::{brillig::BrilligInputs, opcodes::BlockId, Circuit, Opcode};
use std::collections::HashSet;

/// `UnusedMemoryOptimizer` will remove initializations of memory blocks which are unused.
Expand Down Expand Up @@ -29,6 +29,13 @@ impl<F> UnusedMemoryOptimizer<F> {
Opcode::MemoryOp { block_id, .. } => {
unused_memory_initialization.remove(block_id);
}
Opcode::BrilligCall { inputs, .. } => {
for input in inputs {
if let BrilligInputs::MemoryArray(block) = input {
unused_memory_initialization.remove(block);
}
}
}
_ => (),
}
}
Expand Down
23 changes: 23 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,29 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
Ok(())
}

/// Constrains the `lhs` and `rhs` to be non-equal.
///
/// This is done by asserting the existence of an inverse for the value `lhs - rhs`.
/// The constraint `(lhs - rhs) * inverse == 1` will only be satisfiable if `lhs` and `rhs` are non-equal.
pub(crate) fn assert_neq_var(
&mut self,
lhs: AcirVar,
rhs: AcirVar,
assert_message: Option<AssertionPayload<F>>,
) -> Result<(), RuntimeError> {
let diff_var = self.sub_var(lhs, rhs)?;

let one = self.add_constant(F::one());
let _ = self.inv_var(diff_var, one)?;
if let Some(payload) = assert_message {
self.acir_ir
.assertion_payloads
.insert(self.acir_ir.last_acir_opcode_location(), payload);
}

Ok(())
}

pub(crate) fn vars_to_expressions_or_memory(
&self,
values: &[AcirValue],
Expand Down
41 changes: 41 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 @@ -723,6 +723,47 @@ impl<'a> Context<'a> {

self.acir_context.assert_eq_var(lhs, rhs, assert_payload)?;
}
Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => {
let lhs = self.convert_numeric_value(*lhs, dfg)?;
let rhs = self.convert_numeric_value(*rhs, dfg)?;

let assert_payload = if let Some(error) = assert_message {
match error {
ConstrainError::StaticString(string) => Some(
self.acir_context.generate_assertion_message_payload(string.clone()),
),
ConstrainError::Dynamic(error_selector, is_string_type, values) => {
if let Some(constant_string) = try_to_extract_string_from_error_payload(
*is_string_type,
values,
dfg,
) {
Some(
self.acir_context
.generate_assertion_message_payload(constant_string),
)
} else {
let acir_vars: Vec<_> = values
.iter()
.map(|value| self.convert_value(*value, dfg))
.collect();

let expressions_or_memory =
self.acir_context.vars_to_expressions_or_memory(&acir_vars)?;

Some(AssertionPayload {
error_selector: error_selector.as_u64(),
payload: expressions_or_memory,
})
}
}
}
} else {
None
};

self.acir_context.assert_neq_var(lhs, rhs, assert_payload)?;
}
Instruction::Cast(value_id, _) => {
let acir_var = self.convert_numeric_value(*value_id, dfg)?;
self.define_result_var(dfg, instruction_id, acir_var);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ impl<'block> BrilligBlock<'block> {
self.brillig_context.deallocate_single_addr(condition);
}
}
Instruction::ConstrainNotEqual(..) => {
unreachable!("only implemented in ACIR")
}

Instruction::Allocate => {
let result_value = dfg.instruction_results(instruction_id)[0];
let pointer = self.variables.define_single_addr_variable(
Expand Down
1 change: 1 addition & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result<Ss
.run_pass(Ssa::fold_constants, "Constant Folding")
.run_pass(Ssa::remove_enable_side_effects, "EnableSideEffectsIf removal")
.run_pass(Ssa::fold_constants_using_constraints, "Constraint Folding")
.run_pass(Ssa::make_constrain_not_equal_instructions, "Adding constrain not equal")
.run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (1st)")
.run_pass(Ssa::simplify_cfg, "Simplifying:")
.run_pass(Ssa::array_set_optimization, "Array Set Optimizations")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ impl DependencyContext {
}
// Check the constrain instruction arguments against those
// involved in Brillig calls, remove covered calls
Instruction::Constrain(value_id1, value_id2, _) => {
Instruction::Constrain(value_id1, value_id2, _)
| Instruction::ConstrainNotEqual(value_id1, value_id2, _) => {
self.clear_constrained(
&[function.dfg.resolve(*value_id1), function.dfg.resolve(*value_id2)],
function,
Expand Down Expand Up @@ -555,6 +556,7 @@ impl Context {
| Instruction::Binary(..)
| Instruction::Cast(..)
| Instruction::Constrain(..)
| Instruction::ConstrainNotEqual(..)
| Instruction::IfElse { .. }
| Instruction::Load { .. }
| Instruction::Not(..)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use super::{
ir::{
basic_block::BasicBlock,
call_stack::{CallStack, CallStackId},
dfg::InsertInstructionResult,
dfg::{GlobalsGraph, InsertInstructionResult},
function::RuntimeType,
instruction::{ConstrainError, InstructionId, Intrinsic},
types::NumericType,
Expand Down Expand Up @@ -73,6 +73,13 @@ impl FunctionBuilder {
self.current_function.set_runtime(runtime);
}

pub(crate) fn set_globals(&mut self, globals: Arc<GlobalsGraph>) {
for (_, value) in globals.values_iter() {
self.current_function.dfg.make_global(value.get_type().into_owned());
}
self.current_function.set_globals(globals);
}

/// Finish the current function and create a new function.
///
/// A FunctionBuilder can always only work on one function at a time, so care
Expand Down
139 changes: 122 additions & 17 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, sync::Arc};

use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyResult};

Expand Down Expand Up @@ -102,6 +102,31 @@ pub(crate) struct DataFlowGraph {

#[serde(skip)]
pub(crate) data_bus: DataBus,

pub(crate) globals: Arc<GlobalsGraph>,
}

/// The GlobalsGraph contains the actual global data.
/// Global data is expected to only be numeric constants or array constants (which are represented by Instruction::MakeArray).
/// The global's data will shared across functions and should be accessible inside of a function's DataFlowGraph.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub(crate) struct GlobalsGraph {
/// Storage for all of the global values
values: DenseMap<Value>,
/// All of the instructions in the global value space.
/// These are expected to all be Instruction::MakeArray
instructions: DenseMap<Instruction>,
}

impl GlobalsGraph {
pub(crate) fn from_dfg(dfg: DataFlowGraph) -> Self {
Self { values: dfg.values, instructions: dfg.instructions }
}

/// Iterate over every Value in this DFG in no particular order, including unused Values
pub(crate) fn values_iter(&self) -> impl ExactSizeIterator<Item = (ValueId, &Value)> {
self.values.iter()
}
}

impl DataFlowGraph {
Expand Down Expand Up @@ -235,6 +260,24 @@ impl DataFlowGraph {
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
) -> InsertInstructionResult {
self.insert_instruction_and_results_if_simplified(
instruction,
block,
ctrl_typevars,
call_stack,
None,
)
}

/// Simplifies a potentially existing instruction and inserts it only if it changed.
pub(crate) fn insert_instruction_and_results_if_simplified(
&mut self,
instruction: Instruction,
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
existing_id: Option<InstructionId>,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction) {
panic!("Attempted to insert instruction not handled by runtime: {instruction:?}");
Expand All @@ -251,7 +294,20 @@ impl DataFlowGraph {
result @ (SimplifyResult::SimplifiedToInstruction(_)
| SimplifyResult::SimplifiedToInstructionMultiple(_)
| SimplifyResult::None) => {
let mut instructions = result.instructions().unwrap_or(vec![instruction]);
let instructions = result.instructions();
if instructions.is_none() {
if let Some(id) = existing_id {
if self[id] == instruction {
// Just (re)insert into the block, no need to redefine.
self.blocks[block].insert_instruction(id);
return InsertInstructionResult::Results(
id,
self.instruction_results(id),
);
}
}
}
let mut instructions = instructions.unwrap_or(vec![instruction]);
assert!(!instructions.is_empty(), "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector");

if instructions.len() > 1 {
Expand Down Expand Up @@ -511,7 +567,7 @@ impl DataFlowGraph {
&self,
value: ValueId,
) -> Option<(FieldElement, NumericType)> {
match &self.values[self.resolve(value)] {
match &self[self.resolve(value)] {
Value::NumericConstant { constant, typ } => Some((*constant, *typ)),
_ => None,
}
Expand All @@ -520,13 +576,15 @@ impl DataFlowGraph {
/// Returns the Value::Array associated with this ValueId if it refers to an array constant.
/// Otherwise, this returns None.
pub(crate) fn get_array_constant(&self, value: ValueId) -> Option<(im::Vector<ValueId>, Type)> {
match &self.values[self.resolve(value)] {
Value::Instruction { instruction, .. } => match &self.instructions[*instruction] {
let value = self.resolve(value);
if let Some(instruction) = self.get_local_or_global_instruction(value) {
match instruction {
Instruction::MakeArray { elements, typ } => Some((elements.clone(), typ.clone())),
_ => None,
},
}
} else {
// Arrays are shared, so cloning them is cheap
_ => None,
None
}
}

Expand Down Expand Up @@ -619,17 +677,23 @@ impl DataFlowGraph {

/// True if the given ValueId refers to a (recursively) constant value
pub(crate) fn is_constant(&self, argument: ValueId) -> bool {
match &self[self.resolve(argument)] {
let argument = self.resolve(argument);
match &self[argument] {
Value::Param { .. } => false,
Value::Instruction { instruction, .. } => match &self[*instruction] {
Instruction::MakeArray { elements, .. } => {
elements.iter().all(|element| self.is_constant(*element))
Value::Instruction { .. } => {
let Some(instruction) = self.get_local_or_global_instruction(argument) else {
return false;
};
match &instruction {
Instruction::MakeArray { elements, .. } => {
elements.iter().all(|element| self.is_constant(*element))
}
_ => false,
}
_ => 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,
}
Value::Global(_) => {
unreachable!("The global value should have been indexed from the global space");
}
_ => true,
}
}
Expand All @@ -642,6 +706,29 @@ impl DataFlowGraph {
false
}
}

pub(crate) fn is_global(&self, value: ValueId) -> bool {
matches!(self.values[value], Value::Global(_))
}

/// Uses value information to determine whether an instruction is from
/// this function's DFG or the global space's DFG.
pub(crate) fn get_local_or_global_instruction(&self, value: ValueId) -> Option<&Instruction> {
match &self[value] {
Value::Instruction { instruction, .. } => {
let instruction = if self.is_global(value) {
let instruction = &self.globals[*instruction];
// We expect to only have MakeArray instructions in the global space
assert!(matches!(instruction, Instruction::MakeArray { .. }));
instruction
} else {
&self[*instruction]
};
Some(instruction)
}
_ => None,
}
}
}

impl std::ops::Index<InstructionId> for DataFlowGraph {
Expand All @@ -660,7 +747,11 @@ impl std::ops::IndexMut<InstructionId> for DataFlowGraph {
impl std::ops::Index<ValueId> for DataFlowGraph {
type Output = Value;
fn index(&self, id: ValueId) -> &Self::Output {
&self.values[id]
let value = &self.values[id];
if matches!(value, Value::Global(_)) {
return &self.globals[id];
}
value
}
}

Expand All @@ -678,6 +769,20 @@ impl std::ops::IndexMut<BasicBlockId> for DataFlowGraph {
}
}

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

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

// The result of calling DataFlowGraph::insert_instruction can
// be a list of results or a single ValueId if the instruction was simplified
// to an existing value.
Expand Down
Loading

0 comments on commit a964cd0

Please sign in to comment.