编译器是如何实现位域运算的

How does the compiler implement bit field arithmetics?

本文关键字:位域 运算 实现 何实现 编译器      更新时间:2023-10-16

当问一个关于如何做N位符号减法的问题时,我得到了以下答案:

template<int bits>
int
sub_wrap( int v, int s )
{
    struct Bits { signed int r: bits; } tmp;
    tmp.r = v - s;
    return tmp.r;
}

这很简洁,但是编译器如何实现呢?从这个问题中我得出结论,访问位字段或多或少与手工操作相同,但是如果像本例中那样与算术结合使用呢?它会和手动操作一样快吗?

在"编译器"的角色中回答"gcc"将是伟大的,如果有人想要得到具体的。我已经尝试阅读生成的程序集,但它目前超出了我。

正如在另一个问题中所写的,无符号包装数学可以这样做:

int tmp = (a - b) & 0xFFF;  /* 12 bit mask.  */

写入(12位)位域将完全执行该操作,无论带符号还是无符号。唯一的区别是你可能会从编译器得到一个警告消息。

对于读取,您需要做一些不同的事情。

对于无符号数学,这样做就足够了:

int result = tmp;  /* whatever bit count, we know tmp contains nothing else.  */

int result = tmp & 0xFFF;  /* 12bit, again, if we have other junk in tmp.  */

对于符号数学,额外的魔力是符号扩展:

int result = (tmp << (32-12)) >> (32-12); /* asssuming 32bit int, and 12bit value. */

所做的就是在更宽的int上复制位域的顶部(第11位)。

这正是编译器对位域所做的。无论你是手工编码还是作为位域,都取决于你,但只要确保你得到了正确的神奇数字。

(我没有读过标准,但我怀疑依靠位域在溢出时做正确的事情可能不安全?)

编译器知道示例中r的大小和确切位置。假设它是

[xxxxrrrr]
然后

tmp.r = X;
例如,

可以展开为(b后缀表示二进制字面值,&是位的,|是位的或)

tmp = (tmp & 11110000b)   // <-- get the remainder which is not tmp.r
    | (X   & 00001111b);  // <-- put X into tmp.r and filter away unwanted bits

假设你的布局是

[xxrrrrxx]   // 4 bits, 2 left-shifts

展开可以是

tmp = (tmp    & 11000011b)   // <-- get the remainder which is not tmp.r
    | ((X<<2) & 00111100b);  // <-- filter 4 relevant bits, then shift left 2

X实际上是什么样子的,是一个复杂的公式还是只是一个文字,实际上是无关紧要的。

如果您的体系结构不支持这样的按位操作,仍然有乘法和除以2的幂来模拟移位,并且可能这些也可以用来过滤掉不需要的位。