noir
noir copied to clipboard
Implementation of unsigned overflow checks is ACIR specific
Problem
Overflow checks are added on ssa gen. But the overflow checks for signed integers only make sense for the specifics of ACIR, and don't make much sense in SSA. Take this example:
fn main(a: u8, b: u8) -> pub u8 {
a * b
}
This generates the following SSA:
acir fn main f0 {
b0(v0: u8, v1: u8):
v2 = mul v0, v1
range_check v2 to 8 bits
return v2
}
v2 has type of u8. In SSA land, range checking a u8 to just have 8 bits should be a no-op. It only works because it knows that in ACIR additions of u8s are implemented as field additions so adding up two u8s could generate a result larger than a u8 in case of overflow.
This makes the current SSA-gen overflow checks to be no-ops in brillig, and they add overhead because we'd codegen a redundant range check (I'll open a PR to try to catch redundant range checks from generating any bytecode)
Note: this is only for unsigned. Signed overflow checks appear to apply correctly to brillig
Happy Case
We figure out a way of having efficient overflow checks with a clean abstraction. If we did at the SSA gen level the overflow checking in the style that brillig gen does it it'd generate more constraints for acir functions.
Two options that I can think of:
- Do overflow checking in acir_gen/brillig_gen
- Do overflow checking in a specific SSA pass that is aware of the target runtime of each function and codegens an efficient version for each one
Project Impact
None
Impact Context
No response
Workaround
None
Workaround Description
A PR was added to add the unsigned overflow checks into brillig gen directly https://github.com/noir-lang/noir/pull/4445.
Additional Context
No response
Would you like to submit a PR for this Issue?
None
Support Needs
No response