Когда приведение типов нарушает строгое правило псевдонимов в 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 и передает правильное значение?
Почему они показывают разные потоки команд и результат?