Skip to content

Commit

Permalink
chore: add regression test for #6530 (noir-lang/noir#7089)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
AztecBot committed Jan 16, 2025
2 parents 580e142 + aec9fe9 commit 53e9136
Show file tree
Hide file tree
Showing 42 changed files with 922 additions and 155 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f2a6d1038ecb922367ffb7280c4b0781043ade02
c172880ae47ec4906cda662801bd4b7866c9586b
Empty file.
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
40 changes: 36 additions & 4 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ pub(crate) enum Instruction {
/// Constrains two values to be equal to one another.
Constrain(ValueId, ValueId, Option<ConstrainError>),

/// Constrains two values to not be equal to one another.
ConstrainNotEqual(ValueId, ValueId, Option<ConstrainError>),

/// Range constrain `value` to `max_bit_size`
RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option<String> },

Expand Down Expand Up @@ -364,6 +367,7 @@ impl Instruction {
InstructionResultType::Operand(*value)
}
Instruction::Constrain(..)
| Instruction::ConstrainNotEqual(..)
| Instruction::Store { .. }
| Instruction::IncrementRc { .. }
| Instruction::DecrementRc { .. }
Expand Down Expand Up @@ -405,7 +409,7 @@ impl Instruction {
},

// These can fail.
Constrain(..) | RangeCheck { .. } => true,
Constrain(..) | ConstrainNotEqual(..) | RangeCheck { .. } => true,

// This should never be side-effectful
MakeArray { .. } | Noop => false,
Expand Down Expand Up @@ -472,7 +476,14 @@ impl Instruction {
},

// We can deduplicate these instructions if we know the predicate is also the same.
Constrain(..) | RangeCheck { .. } => deduplicate_with_predicate,
Constrain(..) | ConstrainNotEqual(..) | RangeCheck { .. } => deduplicate_with_predicate,

// Noop instructions can always be deduplicated, although they're more likely to be
// removed entirely.
Noop => true,

// Cast instructions can always be deduplicated
Cast(_, _) => true,

// Noop instructions can always be deduplicated, although they're more likely to be
// removed entirely.
Expand Down Expand Up @@ -540,6 +551,7 @@ impl Instruction {
}

Constrain(..)
| ConstrainNotEqual(..)
| EnableSideEffectsIf { .. }
| IncrementRc { .. }
| DecrementRc { .. }
Expand Down Expand Up @@ -610,6 +622,7 @@ impl Instruction {
Instruction::Cast(_, _)
| Instruction::Not(_)
| Instruction::Truncate { .. }
| Instruction::ConstrainNotEqual(..)
| Instruction::Constrain(_, _, _)
| Instruction::RangeCheck { .. }
| Instruction::Allocate
Expand Down Expand Up @@ -656,6 +669,22 @@ impl Instruction {
});
Instruction::Constrain(lhs, rhs, assert_message)
}
Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => {
// Must map the `lhs` and `rhs` first as the value `f` is moved with the closure
let lhs = f(*lhs);
let rhs = f(*rhs);
let assert_message = assert_message.as_ref().map(|error| match error {
ConstrainError::Dynamic(selector, is_string, payload_values) => {
ConstrainError::Dynamic(
*selector,
*is_string,
payload_values.iter().map(|&value| f(value)).collect(),
)
}
_ => error.clone(),
});
Instruction::ConstrainNotEqual(lhs, rhs, assert_message)
}
Instruction::Call { func, arguments } => Instruction::Call {
func: f(*func),
arguments: vecmap(arguments.iter().copied(), f),
Expand Down Expand Up @@ -714,7 +743,8 @@ impl Instruction {
Instruction::Truncate { value, bit_size: _, max_bit_size: _ } => {
*value = f(*value);
}
Instruction::Constrain(lhs, rhs, assert_message) => {
Instruction::Constrain(lhs, rhs, assert_message)
| Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => {
*lhs = f(*lhs);
*rhs = f(*rhs);
if let Some(ConstrainError::Dynamic(_, _, payload_values)) = assert_message {
Expand Down Expand Up @@ -786,7 +816,8 @@ impl Instruction {
| Instruction::Load { address: value } => {
f(*value);
}
Instruction::Constrain(lhs, rhs, assert_error) => {
Instruction::Constrain(lhs, rhs, assert_error)
| Instruction::ConstrainNotEqual(lhs, rhs, assert_error) => {
f(*lhs);
f(*rhs);
if let Some(ConstrainError::Dynamic(_, _, values)) = assert_error.as_ref() {
Expand Down Expand Up @@ -878,6 +909,7 @@ impl Instruction {
SimplifiedToInstructionMultiple(constraints)
}
}
Instruction::ConstrainNotEqual(..) => None,
Instruction::ArrayGet { array, index } => {
if let Some(index) = dfg.get_numeric_constant(*index) {
try_optimize_array_get_from_previous_set(dfg, *array, index)
Expand Down
8 changes: 8 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ fn display_instruction_inner(
writeln!(f)
}
}
Instruction::ConstrainNotEqual(lhs, rhs, error) => {
write!(f, "constrain {} != {}", show(*lhs), show(*rhs))?;
if let Some(error) = error {
display_constrain_error(dfg, error, f)
} else {
writeln!(f)
}
}
Instruction::Call { func, arguments } => {
let arguments = value_list(dfg, arguments);
writeln!(f, "call {}({}){}", show(*func), arguments, result_types(dfg, results))
Expand Down
Loading

0 comments on commit 53e9136

Please sign in to comment.