优化器能否消除从 64 位到 32 位整数类型的赋值?

Can the optimizer elide assignment from 64bit to 32bit integer types?

鉴于此:

uint64_t truncate(uint64_t num) {
    uint32_t lessBits = static_cast<uint32_t>(num);
    return static_cast<uint64_t>(lessBits);
}

是否允许编译器优化静态转换并保留完整的 64 位值,还是必须保留 32 位“截断”?

快速 godbold check, it would appear a mov eax 使用 , ...,这意味着 32 位“截断”未被优化。

语言如何涵盖这种情况?我目前的猜测是,这只是 Integer Conversions 被完全定义并且编译器观察到这一点的情况,因此它不能省略 assignment/32bit 转换。

在C语言中,整数类型之间的转换是强制性的(as-if规则除外,这意味着如果没有任何区别,编译器可以做任何它喜欢的事情。所以如果每个return函数的值被忽略或赋值给一个32位整数,那么编译器将被允许不转换。)

浮点类型之间的转换可能不是强制性的;编译器可以自由使用比要求更高的精度。但是,由于强制转换或由于分配给变量而导致的转换是强制性的。此外,还有一些预定义的宏,可让您检测编译器的作用。

所以

float x = 3.1;
float y = 3.2;
double d = x * y;
double e = (float) (x * y);
float z = x * y;

必须将 3.1f 和 3.2f 分配给 x 和 y。 x * y 可以用浮点数或双精度计算,所以 d 可能有不同的结果。 e 和 z 不能。 (这是因为 double 的尾数位多于 float 的两倍,因此以无限精度计算 x*y,先四舍五入到 double 再到 float,产生与仅四舍五入到 float 相同的结果。如果涉及 long double,事情可能会有所不同)。

由于优化绝不能影响定义的程序行为的原则,以及为了允许优化的必然结果,标准必须将其行为可能影响其行为的所有行为描述为未定义行为,因此没有定义的情况该标准将允许实现进行您描述的那种替换。如果一个实现作为“一致语言扩展”的一种形式,指定除 divide/remainder 以外的整数计算将永远不会产生任何副作用,除了产生可能截断的值,这些值可能在正常范围内,也可能不在正常范围内他们的类型,然后是 32 位 int 和 64 位 long 的实现,给出类似的东西:

long test(int *arr, int count)
{
  long total = 0;
  int subtotal = 0;
  for (int i=0; i<count; i++)
  {
    subtotal += arr[i];
    total += subtotal;
  }
  return total;
}

可能会在闲暇时将小计保持为 32 位 int 并在循环的每次传递中将其符号扩展为 64 位,然后再将其添加到 total,或者它可以签名- 在从内存加载时扩展 arr[i] 的每个项目并将 subtotal 保持为 64 位值,并且在溢出的情况下可能会观察到这种行为差异,因为实现的“符合语言扩展”。

请注意,不幸的是,无法邀请编译器对小于 int 的类型执行此类优化,因为实现需要记录 consistent 规则将 intunsigned 转换为较小的有符号类型,因此不允许在不截断的情况下简单地将较小的类型存储在 32 位寄存器中,并允许读出它们的全部值,即使这样做会比截断更有效。