Skip to content

Commit 41ee1aa

Browse files
fix: SSA typing for right shifts (#4302)
# Description ## Problem\* Partial work towards #4275 ## Summary\* Previously, SSA was issuing (lhs_type, Field) DIV for right shifts. However this is wrong if we support shifts by the bit size (`u1 >> 1`) This is interpreted in acir_gen as an euclidean division with bit_size = lhs_type.bit_size(). However, u1 >> 1 worked because of a + 1 that is in euclidean division https://github.com/noir-lang/noir/blob/0e073037b00212fd17fc8ca9c531b567614eb4c5/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs#L703 (and it failed in unconstrained functions since they don't codegen that extraneous + 1) ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # 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. --------- Co-authored-by: TomAFrench <[email protected]>
1 parent 7420dbb commit 41ee1aa

5 files changed

Lines changed: 37 additions & 1 deletion

File tree

compiler/noirc_evaluator/src/ssa/function_builder/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,18 @@ impl FunctionBuilder {
336336
rhs: ValueId,
337337
bit_size: u32,
338338
) -> ValueId {
339+
let lhs_typ = self.type_of_value(lhs);
339340
let base = self.field_constant(FieldElement::from(2_u128));
340341
// we can safely cast to unsigned because overflow_checks prevent bit-shift with a negative value
341342
let rhs_unsigned = self.insert_cast(rhs, Type::unsigned(bit_size));
342343
let pow = self.pow(base, rhs_unsigned);
343-
self.insert_binary(lhs, BinaryOp::Div, pow)
344+
// We need at least one more bit for the case where rhs == bit_size
345+
let div_type = Type::unsigned(bit_size + 1);
346+
let casted_lhs = self.insert_cast(lhs, div_type.clone());
347+
let casted_pow = self.insert_cast(pow, div_type);
348+
let div_result = self.insert_binary(casted_lhs, BinaryOp::Div, casted_pow);
349+
// We have to cast back to the original type
350+
self.insert_cast(div_result, lhs_typ)
344351
}
345352

346353
/// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs

test_programs/execution_success/bit_shifts_runtime/src/main.nr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ fn main(x: u64, y: u64) {
1616
assert(a << 7 == -128);
1717
assert(a << -a == -2);
1818

19+
assert(x >> x == 0);
1920
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "brillig_bit_shifts_runtime"
3+
type = "bin"
4+
authors = [""]
5+
6+
[dependencies]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
x = 64
2+
y = 1
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
unconstrained fn main(x: u64, y: u64) {
2+
// runtime shifts on compile-time known values
3+
assert(64 << y == 128);
4+
assert(64 >> y == 32);
5+
// runtime shifts on runtime values
6+
assert(x << y == 128);
7+
assert(x >> y == 32);
8+
9+
// Bit-shift with signed integers
10+
let mut a :i8 = y as i8;
11+
let mut b: i8 = x as i8;
12+
assert(b << 1 == -128);
13+
assert(b >> 2 == 16);
14+
assert(b >> a == 32);
15+
a = -a;
16+
assert(a << 7 == -128);
17+
assert(a << -a == -2);
18+
19+
assert(x >> x == 0);
20+
}

0 commit comments

Comments
 (0)