Skip to content

nir: Fix undefined behaviour of shifts of negative values

A number of nir instructions in src/compiler/nir/nir_opcodes.py might use shift operators with a negative lhs value. This behavior, however, is undefined for left shifts, and implementation-defined for right shifts.

Section Section 6.5.7 "Bitwise shift operators" of the C99 n1124 spec states:

  1. The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 * 2^E2 , reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 * 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
  2. The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2^E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

Which is different from what is described in Section 5.9 "Expressions" of the GLSL 4.40 (rev 9) spec:

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted by E2 bits.

The value of E1 >> E2 is E1 right-shifted by E2 bit positions. If E1 is a signed integer, the right-shift will extend the sign bit. If E1 is an unsigned integer, the right-shift will zero-extend.

For the left shift, the required behavior can be easily simulated by casting the E1 value to unsigned before performing the shift, and then casting the result back to signed.

The right shift, that preserves the MSB bit, can be implemented as such:

((s ^ lhs) >> rhs) ^ s
   where s is
       0 if lhs >= 0 (all bits of s are 0)
      -1 if lhs <  0 (all bits of s are 1)

s and lhs will always have the same MSB, so s ^ lhs will always have a 0 in MSB. This way, the implementation-defined behavior of the right shift shall be avoided. After performing the right shift, the original MSB of lhs can be retrieved by another ^-comparation against s.

Also adding unit tests for all users of left and right shifts in nir_opcodes.py. With mesa built with -Db_sanitize=undefined (which is the case for Gitlab CI), the tests are expected to result in runtime errors without subsequent commits.

Merge request reports