TODO: Definitely add tests to make sure this impl is right.
(&mut self, value: SpirvValue, shift: SpirvValue, is_left: bool)
| 216 | |
| 217 | // TODO: Definitely add tests to make sure this impl is right. |
| 218 | fn rotate(&mut self, value: SpirvValue, shift: SpirvValue, is_left: bool) -> SpirvValue { |
| 219 | let width = match self.lookup_type(shift.ty) { |
| 220 | SpirvType::Integer(width, _) => width, |
| 221 | other => self.fatal(&format!( |
| 222 | "cannot rotate non-integer type: {}", |
| 223 | other.debug(shift.ty, self) |
| 224 | )), |
| 225 | }; |
| 226 | let int_size = self.constant_int(shift.ty, width as u64); |
| 227 | let mask = self.constant_int(shift.ty, (width - 1) as u64); |
| 228 | let zero = self.constant_int(shift.ty, 0); |
| 229 | let bool = SpirvType::Bool.def(self.span(), self); |
| 230 | // https://stackoverflow.com/a/10134877 |
| 231 | let mask_shift = self.and(shift, mask); |
| 232 | let sub = self.sub(int_size, mask_shift); |
| 233 | let (lhs, rhs) = if is_left { |
| 234 | (self.shl(value, mask_shift), self.lshr(value, sub)) |
| 235 | } else { |
| 236 | (self.lshr(value, mask_shift), self.shl(value, sub)) |
| 237 | }; |
| 238 | let or = self.or(lhs, rhs); |
| 239 | // "The result is undefined if Shift is greater than or equal to the bit width of the components of Base." |
| 240 | // So we need to check for zero shift, and don't use the shift result if it is. |
| 241 | let mask_is_zero = self |
| 242 | .emit() |
| 243 | .i_not_equal(bool, None, mask_shift.def(self), zero.def(self)) |
| 244 | .unwrap() |
| 245 | .with_type(bool); |
| 246 | self.select(mask_is_zero, value, or) |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | // Important: This lets us use CodegenCx methods on Builder |
no test coverage detected