代码之家  ›  专栏  ›  技术社区  ›  ruach

stricit别名冲突:为什么gcc和clang生成不同的输出?

  •  -2
  • ruach  · 技术社区  · 7 年前

    当类型转换违反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函数生成几乎相同的指令序列,两种情况下返回值相同; 酒吧 违反规则,并传播常量;以及 汽车 不会违反,从内存中读取正确的值。

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

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

    1 回复  |  直到 7 年前
        1
  •  1
  •   Nick ODell    7 年前

    为什么我们可以说这个条不违反严格的别名规则?

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

    让我举个例子。

    假设我们这样称呼酒吧:

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

    严格的别名要求不同类型的两个指针不引用同一内存。&X和&Y是不同的类型,它们表示不同的内存。这不会违反严格的别名。

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

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

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