Skip to content

Commit 77b06d9

Browse files
authored
Merge 04b34ad into 223e860
2 parents 223e860 + 04b34ad commit 77b06d9

1 file changed

Lines changed: 56 additions & 23 deletions

File tree

acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,51 @@ impl RangeOptimizer {
4848
/// only store the fact that we have constrained it to
4949
/// be 16 bits.
5050
fn collect_ranges(circuit: &Circuit) -> BTreeMap<Witness, u32> {
51-
let mut witness_to_bit_sizes = BTreeMap::new();
51+
let mut witness_to_bit_sizes: BTreeMap<Witness, u32> = BTreeMap::new();
5252

5353
for opcode in &circuit.opcodes {
54-
// Extract the witness index and number of bits,
55-
// if it is a range constraint
56-
let (witness, num_bits) = match extract_range_opcode(opcode) {
57-
Some(func_inputs) => func_inputs,
58-
None => continue,
54+
let Some((witness, num_bits)) = (match opcode {
55+
Opcode::AssertZero(expr) => {
56+
// If the opcode is constraining a witness to be equal to a value then it can be considered
57+
// as a range opcode for the number of bits required to hold that value.
58+
if expr.is_degree_one_univariate() {
59+
let (k, witness) = expr.linear_combinations[0];
60+
let constant = expr.q_c;
61+
let witness_value = -constant / k;
62+
63+
if witness_value.is_zero() {
64+
Some((witness, 0))
65+
} else {
66+
// We subtract off 1 bit from the implied witness value to give the weakest range constraint
67+
// which would be stricter than the constraint imposed by this opcode.
68+
let implied_range_constraint_bits = witness_value.num_bits() - 1;
69+
Some((witness, implied_range_constraint_bits))
70+
}
71+
} else {
72+
None
73+
}
74+
}
75+
76+
77+
Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE {
78+
input: FunctionInput { witness, num_bits },
79+
}) => {
80+
Some((*witness, *num_bits))
81+
}
82+
83+
_ => None,
84+
}) else {
85+
continue;
5986
};
6087

6188
// Check if the witness has already been recorded and if the witness
6289
// size is more than the current one, we replace it
63-
let should_replace = match witness_to_bit_sizes.get(&witness).copied() {
64-
Some(old_range_bits) => old_range_bits > num_bits,
65-
None => true,
66-
};
67-
if should_replace {
68-
witness_to_bit_sizes.insert(witness, num_bits);
69-
}
90+
witness_to_bit_sizes
91+
.entry(witness)
92+
.and_modify(|old_range_bits| {
93+
*old_range_bits = std::cmp::min(*old_range_bits, num_bits);
94+
})
95+
.or_insert(num_bits);
7096
}
7197
witness_to_bit_sizes
7298
}
@@ -116,16 +142,10 @@ impl RangeOptimizer {
116142
/// Extract the range opcode from the `Opcode` enum
117143
/// Returns None, if `Opcode` is not the range opcode.
118144
fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> {
119-
// Range constraints are blackbox function calls
120-
// so we first extract the function call
121-
let func_call = match opcode {
122-
acir::circuit::Opcode::BlackBoxFuncCall(func_call) => func_call,
123-
_ => return None,
124-
};
125-
126-
// Skip if it is not a range constraint
127-
match func_call {
128-
BlackBoxFuncCall::RANGE { input } => Some((input.witness, input.num_bits)),
145+
match opcode {
146+
Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => {
147+
Some((input.witness, input.num_bits))
148+
}
129149
_ => None,
130150
}
131151
}
@@ -246,4 +266,17 @@ mod tests {
246266
let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions);
247267
assert_eq!(optimized_circuit.opcodes.len(), 5);
248268
}
269+
270+
#[test]
271+
fn constant_implied_ranges() {
272+
// The optimizer should use knowledge about constant witness assignments to remove range opcodes.
273+
let mut circuit = test_circuit(vec![(Witness(1), 16)]);
274+
275+
circuit.opcodes.push(Opcode::AssertZero(Witness(1).into()));
276+
let acir_opcode_positions = circuit.opcodes.iter().enumerate().map(|(i, _)| i).collect();
277+
let optimizer = RangeOptimizer::new(circuit);
278+
let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions);
279+
assert_eq!(optimized_circuit.opcodes.len(), 1);
280+
assert_eq!(optimized_circuit.opcodes[0], Opcode::AssertZero(Witness(1).into()));
281+
}
249282
}

0 commit comments

Comments
 (0)