Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions .github/workflows/test-rust-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,45 +109,6 @@ jobs:
- name: Check for pending snapshots
run: just check-pending-snapshots

incremental-mutation-test:
name: Incremental Mutation Test
if: github.event_name == 'pull_request'
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Fetch base commit
run: |
git fetch --depth=1 origin ${{ github.base_ref }}

- name: Setup toolchain
uses: dtolnay/rust-toolchain@1.85.0
with:
targets: x86_64-unknown-linux-gnu

- uses: Swatinem/rust-cache@v2
with:
key: x86_64-unknown-linux-gnu-debug
save-if: false

- name: Install just
uses: taiki-e/install-action@just

- name: Run incremental mutation test
run: just mutation-test ${{ github.base_ref }}

- name: Archive mutants.out
uses: actions/upload-artifact@v4
if: always()
with:
name: mutants-incremental.out
path: mutants.out

# This is a job which depends on all test jobs and reports the overall status.
# This allows us to add/remove test jobs without having to update the required workflows.
tests-end:
Expand Down
4 changes: 2 additions & 2 deletions acvm-repo/acir/src/circuit/brillig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ pub enum BrilligInputs<F> {
impl<F: AcirField> std::fmt::Display for BrilligInputs<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BrilligInputs::Single(expr) => write!(f, "{expr}"),
BrilligInputs::Single(expr) => expr.fmt(f),
BrilligInputs::Array(exprs) => {
let joined = exprs.iter().map(|e| format!("{e}")).collect::<Vec<_>>().join(", ");
write!(f, "[{joined}]")
}
BrilligInputs::MemoryArray(block_id) => write!(f, "MemoryArray({})", block_id.0),
BrilligInputs::MemoryArray(block_id) => block_id.fmt(f),
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions acvm-repo/acir/src/circuit/opcodes/memory_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
pub struct BlockId(pub u32);

impl std::fmt::Display for BlockId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "b{}", self.0)
}
}

/// Operation on a block of memory
/// We can either write or read at an index in memory
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
Expand Down
197 changes: 76 additions & 121 deletions acvm-repo/acir/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ impl<'a> Parser<'a> {
let mut functions: Vec<Circuit<FieldElement>> = Vec::new();

// We expect top-level "func" keywords for each circuit
while let Some(Keyword::Function) = self.peek_keyword() {
self.bump()?;

while self.eat_keyword(Keyword::Function)? {
let func_id = self.eat_u32_or_error()?;
let expected_id = functions.len() as u32;
if func_id != expected_id {
Expand Down Expand Up @@ -197,26 +195,7 @@ impl<'a> Parser<'a> {
}

fn parse_witness_vector(&mut self) -> ParseResult<Vec<Witness>> {
self.eat_or_error(Token::LeftBracket)?;

let mut witnesses = Vec::new();

while !self.eat(Token::RightBracket)? {
let witness = self.eat_witness_or_error()?;
witnesses.push(witness);

// Eat optional comma
if self.eat(Token::Comma)? {
continue;
}

// If no comma, expect closing bracket next
if self.token.token() != &Token::RightBracket {
return self.expected_token(Token::RightBracket);
}
}

Ok(witnesses)
self.parse_bracketed_list(|parser| parser.eat_witness_or_error())
}

fn parse_witness_ordered_set(&mut self) -> ParseResult<BTreeSet<Witness>> {
Expand Down Expand Up @@ -583,20 +562,7 @@ impl<'a> Parser<'a> {
) -> ParseResult<Vec<FunctionInput<FieldElement>>> {
self.eat_keyword_or_error(keyword)?;
self.eat_or_error(Token::Colon)?;
self.eat_or_error(Token::LeftBracket)?;

let mut inputs = Vec::new();

while !self.eat(Token::RightBracket)? {
let input = self.parse_blackbox_input_no_keyword()?;
inputs.push(input);

// Eat a comma if there is another input, but do not error if there is no comma
// as this means we have reached the end of the inputs.
self.eat(Token::Comma)?;
}

Ok(inputs)
self.parse_bracketed_list(|parser| parser.parse_blackbox_input_no_keyword())
}

fn parse_blackbox_input(
Expand All @@ -611,25 +577,17 @@ impl<'a> Parser<'a> {
fn parse_blackbox_input_no_keyword(
&mut self,
) -> Result<FunctionInput<FieldElement>, ParserError> {
Ok(match self.token.token() {
Token::Int(value) => {
let value = *value;
self.bump()?;
FunctionInput::Constant(value)
}
Token::Witness(index) => {
let witness = *index;
self.bump()?;
FunctionInput::Witness(Witness(witness))
}
other => {
return Err(ParserError::ExpectedOneOfTokens {
tokens: vec![Token::Int(FieldElement::zero()), Token::Witness(0)],
found: other.clone(),
span: self.token.span(),
});
}
})
if let Some(value) = self.eat_field_element()? {
Ok(FunctionInput::Constant(value))
} else if let Some(witness) = self.eat_witness()? {
Ok(FunctionInput::Witness(witness))
} else {
Err(ParserError::ExpectedOneOfTokens {
tokens: vec![Token::Int(FieldElement::zero()), Token::Witness(0)],
found: self.token.token().clone(),
span: self.token.span(),
})
}
}

fn parse_blackbox_output(&mut self) -> ParseResult<Witness> {
Expand Down Expand Up @@ -661,17 +619,7 @@ impl<'a> Parser<'a> {
fn parse_memory_init(&mut self) -> ParseResult<Opcode<FieldElement>> {
self.eat_keyword_or_error(Keyword::MemoryInit)?;

let block_type = match self.peek_keyword() {
Some(Keyword::CallData) => {
self.bump()?;
BlockType::CallData(self.eat_u32_or_error()?)
}
Some(Keyword::ReturnData) => {
self.bump()?;
BlockType::ReturnData
}
_ => BlockType::Memory,
};
let block_type = self.parse_block_type()?;

// blockId = [witness1, witness2, ...]
let block_id = self.eat_block_id_or_error()?;
Expand All @@ -681,6 +629,16 @@ impl<'a> Parser<'a> {
Ok(Opcode::MemoryInit { block_id, init, block_type })
}

fn parse_block_type(&mut self) -> Result<BlockType, ParserError> {
if self.eat_keyword(Keyword::CallData)? {
Ok(BlockType::CallData(self.eat_u32_or_error()?))
} else if self.eat_keyword(Keyword::ReturnData)? {
Ok(BlockType::ReturnData)
} else {
Ok(BlockType::Memory)
}
}

fn parse_memory_read(&mut self) -> ParseResult<Opcode<FieldElement>> {
self.eat_keyword_or_error(Keyword::MemoryRead)?;

Expand Down Expand Up @@ -739,67 +697,35 @@ impl<'a> Parser<'a> {
}

fn parse_brillig_inputs(&mut self) -> ParseResult<Vec<BrilligInputs<FieldElement>>> {
self.eat_or_error(Token::LeftBracket)?;

let mut inputs = Vec::new();
while !self.eat(Token::RightBracket)? {
let input = match self.token.token() {
Token::LeftBracket => {
// It's an array of expressions
self.bump()?; // eat [
let mut exprs = Vec::new();
while !self.eat(Token::RightBracket)? {
exprs.push(self.parse_arithmetic_expression()?);
self.eat(Token::Comma)?; // allow trailing comma
}
BrilligInputs::Array(exprs)
}
Token::Ident(s) if s == "MemoryArray" => {
self.bump()?; // eat "MemoryArray"
self.eat_or_error(Token::LeftParen)?;
let block_id = self.eat_u32_or_error()?;
self.eat_or_error(Token::RightParen)?;
BrilligInputs::MemoryArray(BlockId(block_id))
}
_ => {
let expr = self.parse_arithmetic_expression()?;
BrilligInputs::Single(expr)
}
};
self.parse_bracketed_list(|parser| parser.parse_brillig_input())
}

inputs.push(input);
self.eat(Token::Comma)?; // optional trailing comma
fn parse_brillig_input(&mut self) -> Result<BrilligInputs<FieldElement>, ParserError> {
if self.at(Token::LeftBracket) {
// It's an array of expressions
let exprs = self.parse_bracketed_list(|parser| parser.parse_arithmetic_expression())?;
Ok(BrilligInputs::Array(exprs))
} else if let Some(block_id) = self.eat_block_id()? {
Ok(BrilligInputs::MemoryArray(block_id))
} else {
let expr = self.parse_arithmetic_expression()?;
Ok(BrilligInputs::Single(expr))
}

Ok(inputs)
}

fn parse_brillig_outputs(&mut self) -> ParseResult<Vec<BrilligOutputs>> {
self.eat_or_error(Token::LeftBracket)?;

let mut outputs = Vec::new();
while !self.eat(Token::RightBracket)? {
let output = match self.token.token() {
Token::LeftBracket => {
self.bump()?; // eat [
let mut witnesses = Vec::new();
while !self.eat(Token::RightBracket)? {
witnesses.push(self.eat_witness_or_error()?);
self.eat(Token::Comma)?; // optional trailing comma
}
BrilligOutputs::Array(witnesses)
}
Token::Witness(_) => BrilligOutputs::Simple(self.eat_witness_or_error()?),
_ => {
return self.expected_one_of_tokens(&[Token::LeftBracket, Token::Witness(0)]);
}
};
self.parse_bracketed_list(|parser| parser.parse_brillig_output())
}

outputs.push(output);
self.eat(Token::Comma)?; // optional trailing comma
fn parse_brillig_output(&mut self) -> Result<BrilligOutputs, ParserError> {
if self.at(Token::LeftBracket) {
let witnesses = self.parse_witness_vector()?;
Ok(BrilligOutputs::Array(witnesses))
} else if let Some(witness) = self.eat_witness()? {
Ok(BrilligOutputs::Simple(witness))
} else {
self.expected_one_of_tokens(&[Token::LeftBracket, Token::Witness(0)])
}

Ok(outputs)
}

fn parse_call(&mut self) -> ParseResult<Opcode<FieldElement>> {
Expand Down Expand Up @@ -832,6 +758,31 @@ impl<'a> Parser<'a> {
Ok(predicate)
}

fn parse_bracketed_list<T, F>(&mut self, parser: F) -> ParseResult<Vec<T>>
where
F: Fn(&mut Parser<'a>) -> ParseResult<T>,
{
self.eat_or_error(Token::LeftBracket)?;

let mut values = Vec::new();

while !self.eat(Token::RightBracket)? {
let value = parser(self)?;
values.push(value);

// Eat optional comma
if self.eat(Token::Comma)? {
continue;
}

// If no comma, expect closing bracket next
self.eat_or_error(Token::RightBracket)?;
break;
}

Ok(values)
}

fn eat_ident_or_error(&mut self) -> ParseResult<String> {
if let Some(identifier) = self.eat_ident()? {
Ok(identifier)
Expand Down Expand Up @@ -962,6 +913,10 @@ impl<'a> Parser<'a> {
if self.eat(token.clone())? { Ok(()) } else { self.expected_token(token) }
}

fn at(&mut self, token: Token) -> bool {
self.token.token() == &token
}

/// Returns true if the token is eaten and bumps to the next token.
/// Otherwise will return false and no bump will occur.
fn eat(&mut self, token: Token) -> ParseResult<bool> {
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acir/src/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ fn brillig_call_with_memory_array_input() {
private parameters: [w0, w1, w2]
public parameters: []
return values: []
BRILLIG CALL func: 0, inputs: [2, MemoryArray(0)], outputs: []
BRILLIG CALL func: 0, inputs: [2, b0], outputs: []
";
assert_circuit_roundtrip(src);
}
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl<'a, F: AcirField> RangeOptimizer<'a, F> {
/// a minimal number of times that still allows us to avoid executing
/// any new side effects due to their removal.
///
/// The idea is to keep only the RANGE opcodes that have stricly smaller bit-size requirements
/// The idea is to keep only the RANGE opcodes that have strictly smaller bit-size requirements
/// than before, i.e the ones that are at a 'switch point'.
/// Furthermore, we only keep the switch points that are last before
/// a 'side-effect' opcode (i.e a Brillig call).
Expand Down
20 changes: 10 additions & 10 deletions compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2127,22 +2127,22 @@ mod tests {
brillig(inline_always) fn apply f6 {
b0(v0: Field):
v2 = eq v0, Field 1
jmpif v2 then: b3, else: b2
jmpif v2 then: b2, else: b1
b1():
return
b2():
v5 = eq v0, Field 2
jmpif v5 then: b5, else: b4
b3():
jmpif v5 then: b4, else: b3
b2():
call f1()
jmp b1()
b4():
jmp b5()
b3():
constrain v0 == Field 5
call f5()
jmp b1()
b5():
jmp b5()
b4():
call f2()
jmp b1()
jmp b5()
b5():
return
}
");
}
Expand Down
Loading
Loading