I am trying to understand the possible advantages of the “masking shift operators” (
&>>) over the “normal shift operators” (
>>). In SR-6749 it is said that
The goal of the operator, however, is never to have this wrapping behavior happen — it's what you use when you have static knowledge that your shift amount is <= the bitWidth. By masking, you and the compiler can agree that there's no branch needed, so you get slightly faster code without zero branching and zero risk of undefined behavior (which a negative or too large shift would be).
and I can see that they are used a lot in the Swift standard library, for example in UTF8.swift:
let bits = source._biasedBits &- 0x01010101 var value = (bits & 0b0_11_1111__0000_0000__0000_0000__0000_0000) &>> 24 value |= (bits & 0b0____________11_1111__0000_0000__0000_0000) &>> 10 value |= (bits & 0b0_______________________11_1111__0000_0000) &<< 4 value |= (bits & 0b0________________________________0000_0111) &<< 18
I have two questions:
What is the “risk of undefined behavior” when using the normal shift operators?
If I understand
BinaryInteger.>>(_:_:)correctly, the behavior of
a >> bis defined for all possible values of (signed or unsigned) operands, therefore I do not see where undefined behavior could occur.
Why is it advantageous to use the masking shift operator if the shift amount is a literal constant (less than the bit width)?
In the above example, the left operand is an
UInt32and the shift amounts are literal constants less than 32, so it is known at compile time that the shift amounts are less than the bit width. Why is it advantageous here to use the masking shift operators? Would the compiler (possibly) create branching code if, for example,
&>>24is replaced by
Thanks for any insight,