"int64var = int32var*int32var"中的计算不会按预期溢出.为什么?

Joh*_*itb 3 gcc posix clock integer-overflow icc

我坚信有一些奇怪的事情发生,所以我想提出这个问题.

#include <time.h>
#include <stdint.h>

// shall return a monotonically increasing time in microseconds
int64_t getMonotonicTime() {
   struct timespec ts;
   clock_gettime(CLOCK_MONOTONIC, &ts);

   int64_t ret;
   ret = ts.tv_sec * 1000000; // #1 HERE
   ret += ts.tv_nsec / 1000;
   return ret;
}
Run Code Online (Sandbox Code Playgroud)

有问题的行ts.tv_sec * 1000000导致系统溢出,其中time_tint32位大(在我的系统上恰好是这种情况),每次ts.tv_sec大于2 147.

当我为这个bug编写测试用例时,我发现英特尔icc编译器没有溢出,即使我确信它time_t实际上也32有点宽,即使我禁用了优化.GCC行为确实导致了预期的溢出.

除了未定义的行为,英特尔不会溢出的原因是什么?英特尔开发人员的理由是什么?或者这只是我的误解?

Mar*_*sse 5

看看这个简单的代码:

long f(int a,int b){
  return a*b;
}
Run Code Online (Sandbox Code Playgroud)

我们看到gcc生成的3个不同的asm:

movl    %edi, %eax
imull   %esi, %eax
cltq
Run Code Online (Sandbox Code Playgroud)

铛:

imull   %esi, %edi
movslq  %edi, %rax
Run Code Online (Sandbox Code Playgroud)

英特尔:

movslq    %esi, %rsi
movslq    %edi, %rdi
imulq     %rsi, %rdi
movq      %rdi, %rax
Run Code Online (Sandbox Code Playgroud)

基本上,您可以将2个32位数字相乘(imull)然后对结果进行符号扩展.或者你可以对操作数进行符号扩展,然后将它们乘以64位数字(imulq),那么原则上你应该只保留低32位并对它们进行符号扩展,但这是不必要的,因为重要的情况是那些存在的情况.溢出(未定义的行为),这种优化(删除最终的符号扩展)正是您所观察到的.