Tuesday, December 29, 2020

Bit masking on x86

In theory it's pretty easy to generate a bitmask for a range of bits:

unsigned long long mask(int bits) {
  return mask = (1ull << bits) - 1;

You need to specify that the 1 being shifted is an unsigned long long otherwise it gets treated as a 32-bit int, and the code only works for the range 0..31.

However, this code fails to work on x86. For example:

#include <math.h>
#include <stdio.h>

unsigned long long shift(int x) {
    return (1ull << x) - 1;

int main() {
    printf("Value %0llx\n", shift(64));

This returns the value 0 for the result of shifting by 64 when run on x86.

The reason for this can be found in the Intel docs (Vol. 2B 4-583):

The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).

The result of this is that the one is shifted by zero - ie remains unchanged - and subtracting 1 from that produces the value zero.

Unfortunately, this means we need a more complex bit of code that handles shifts of greater than 64 correctly:

unsigned long long mask(int bits) {
  return mask = (bits >= 64 ? -1 : (1ull << bits) - 1);

1 comment:

  1. We might determine that if there's a chance of a crash during machining. It ought to be stored in thoughts that the CNC machining process isn't free of errors, and there additionally be} some complications. There additionally be} an opportunity that the workpiece or machine additionally be} instructed in the mistaken course. As a result of such a mishap, the tool or machine parts are broken. The breakage Shower Curtains might embrace a minor reduce or screw breakage and in addition lead to severe destruction of structures.