Skip to content

Commit

Permalink
feat: add as_witness builtin function in order to constrain a witne…
Browse files Browse the repository at this point in the history
…ss to be equal to a variable (#4641)

# Description

## Problem\*

Quick fix to #4629 

## Summary\*

This PR adds a `as_witness` function which takes a `Field` and instructs
the compiler to lay down an opcode which constrains a witness to be
equal to this value.

This prevents the issue in #4629 where we collect ever growing
expressions which need to be broken down multiple times while reshaping
the circuit. Doing this during acirgen instead allows us to avoid this
repetition.

## Additional Context



## Documentation\*

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [x] **[For Experimental Features]** Documentation to be submitted in a
separate PR.
Feature is experimental

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
TomAFrench authored May 21, 2024
1 parent aea60c0 commit faf5bd8
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,10 @@ impl AcirContext {
self.acir_ir.call_stack = call_stack;
}

fn get_or_create_witness_var(&mut self, var: AcirVar) -> Result<AcirVar, InternalError> {
pub(crate) fn get_or_create_witness_var(
&mut self,
var: AcirVar,
) -> Result<AcirVar, InternalError> {
if self.var_to_expression(var)?.to_witness().is_some() {
// If called with a variable which is already a witness then return the same variable.
return Ok(var);
Expand Down
9 changes: 9 additions & 0 deletions compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2605,6 +2605,15 @@ impl<'a> Context<'a> {

Ok(result)
}

Intrinsic::AsWitness => {
let arg = arguments[0];
let input = self.convert_value(arg, dfg).into_var()?;
Ok(self
.acir_context
.get_or_create_witness_var(input)
.map(|val| self.convert_vars_to_values(vec![val], dfg, result_ids))?)
}
_ => todo!("expected a black box function"),
}
}
Expand Down
7 changes: 6 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub(crate) enum Intrinsic {
BlackBox(BlackBoxFunc),
FromField,
AsField,
AsWitness,
}

impl std::fmt::Display for Intrinsic {
Expand All @@ -87,6 +88,7 @@ impl std::fmt::Display for Intrinsic {
Intrinsic::BlackBox(function) => write!(f, "{function}"),
Intrinsic::FromField => write!(f, "from_field"),
Intrinsic::AsField => write!(f, "as_field"),
Intrinsic::AsWitness => write!(f, "as_witness"),
}
}
}
Expand All @@ -97,7 +99,9 @@ impl Intrinsic {
/// If there are no side effects then the `Intrinsic` can be removed if the result is unused.
pub(crate) fn has_side_effects(&self) -> bool {
match self {
Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint => true,
Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint | Intrinsic::AsWitness => {
true
}

// These apply a constraint that the input must fit into a specified number of limbs.
Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true,
Expand Down Expand Up @@ -140,6 +144,7 @@ impl Intrinsic {
"to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)),
"from_field" => Some(Intrinsic::FromField),
"as_field" => Some(Intrinsic::AsField),
"as_witness" => Some(Intrinsic::AsWitness),
other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox),
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ pub(super) fn simplify_call(
let instruction = Instruction::Cast(truncated_value, target_type);
SimplifyResult::SimplifiedToInstruction(instruction)
}
Intrinsic::AsWitness => SimplifyResult::None,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ impl Context {
| Intrinsic::BlackBox(_)
| Intrinsic::FromField
| Intrinsic::AsField
| Intrinsic::AsSlice => false,
| Intrinsic::AsSlice
| Intrinsic::AsWitness => false,
},

// We must assume that functions contain a side effect as we cannot inspect more deeply.
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ fn slice_capacity_change(
| Intrinsic::StrAsBytes
| Intrinsic::BlackBox(_)
| Intrinsic::FromField
| Intrinsic::AsField => SizeChange::None,
| Intrinsic::AsField
| Intrinsic::AsWitness => SizeChange::None,
}
}
3 changes: 3 additions & 0 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ pub fn wrapping_sub<T>(x: T, y: T) -> T {
pub fn wrapping_mul<T>(x: T, y: T) -> T {
crate::from_field(crate::as_field(x) * crate::as_field(y))
}

#[builtin(as_witness)]
pub fn as_witness(x: Field) {}

0 comments on commit faf5bd8

Please sign in to comment.