Строгое нарушение псевдонимов: почему gcc и clang генерируют разные выходные данные? - PullRequest
0 голосов
/ 17 января 2019

Когда приведение типов нарушает строгое правило псевдонимов в 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;
}

Когда я компилирую код с GCC 8.2 с опцией -std = c11 -O3.

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

Когда я компилирую код с помощью clang 7.0 с опцией -std = c11 -O3.

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 голосов
/ 20 января 2019

Почему мы можем сказать, что бар не нарушает строгое правило псевдонимов?

Если код, который вызывает bar, не нарушает строгий псевдоним, bar также не будет нарушать строгий псевдоним.

Позвольте мне привести пример.

Предположим, мы называем bar следующим образом:

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

Строгий псевдоним требует, чтобы два указателя разных типов не ссылались на одну и ту же память. & x и & y - это разные типы, и они относятся к разной памяти. Это не нарушает строгий псевдоним.

С другой стороны, скажем, мы называем это так:

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

Теперь мы нарушили строгий псевдоним. Однако нарушение является ошибкой вызывающего абонента.

...