严格混叠违规:为什么gcc和clang生成不同的输出?

时间:2019-01-17 07:27:55

标签: c gcc clang compiler-optimization strict-aliasing

当类型转换违反C和C ++中严格的别名规则时,编译器可能会进行优化,以使错误的常量值可以传播并允许未对齐的访问,从而导致性能下降或总线错误。

我写了一个简单的例子,看看当我违反GCC和Clang中严格的别名规则时,编译器如何优化常量。

这是我得到的代码和说明。

#include <stdio.h>
#include <stdlib.h>

int
foo () //different result in C and C++
{
    int x = 1;
    long *fp = (long *)&x;
    *fp = 1234L;

    return x;
}

//int and long are not compatible 
//Wrong constant propagation as a result of strict aliasing violation
long
bar(int *ip, long *lp)
{
    *lp = 20L;
    *ip = 10;

    return *lp;
}

//char is always compatible with others
//constant is not propagated and memory is read
char
car(char *cp, long *lp)
{
    *cp = 'a';
    *lp = 10L;
    return *cp;
}

When I compile the code with the GCC 8.2 with -std=c11 -O3 option.

foo:
  movl $1234, %eax
  ret
bar:
  movq $20, (%rsi)
  movl $20, %eax
  movl $10, (%rdi)
  ret
car:
  movb $97, (%rdi)
  movq $10, (%rsi)
  movzbl (%rdi), %eax
  ret

When I compile the code with the clang 7.0 with -std=c11 -O3 option.

foo: # @foo
  movl $1, %eax
  retq
bar: # @bar
  movq $20, (%rsi)
  movl $10, (%rdi)
  movl $20, %eax
  retq
car: # @car
  movb $97, (%rdi)
  movq $10, (%rsi)
  movb (%rdi), %al
  retq

bar和car函数生成几乎相同的指令序列,并且两种情况下的返回值都相同; bar 违反规则,并且传播常数;并且汽车没有违反,并且从内存中读取了正确的值。

但是,对于违反严格别名规则的foo函数,在GCC和Clang中生成不同的输出输出; gcc传播存储在内存中的正确值(但不随内存引用一起传播),而clang传播错误的值。似乎两个编译器都将常量传播作为其优化,但是为什么两个编译器生成不同的结果呢?这是否意味着GCC会在foo函数中自动找出严格的别名违规并传播正确的值?

为什么它们显示不同的指令流和结果?

1 个答案:

答案 0 :(得分:1)

  

为什么我们要说酒吧不违反严格的别名规则?

如果调用bar的代码没有违反严格的别名,bar也不会违反严格的别名。

让我举个例子。

假设我们这样调用bar:

int x;
long y;
bar(&x, &y);

严格的别名要求不同类型的两个指针不能引用相同的内存。 &x和&y是不同的类型,它们引用不同的内存。这不违反严格的别名。

另一方面,假设我们这样称呼它:

long y;
bar((int *) &y, &y);

现在,我们违反了严格的别名。但是,违规是呼叫者的错。