Пример Отображение gcc Оптимизация и код пользователя, который может быть неисправен
Функция 'foo' в нижеприведенном фрагменте будет загружать только один из элементов структуры A или B; ну, по крайней мере, это намерение неоптимизированного кода.
typedef struct {
int A;
int B;
} Pair;
int foo(const Pair *P, int c) {
int x;
if (c)
x = P->A;
else
x = P->B;
return c/102 + x;
}
Вот что gcc -O3 дает:
mov eax, esi
mov edx, -1600085855
test esi, esi
mov ecx, DWORD PTR [rdi+4] <-- ***load P->B**
cmovne ecx, DWORD PTR [rdi] <-- ***load P->A***
imul edx
lea eax, [rdx+rsi]
sar esi, 31
sar eax, 6
sub eax, esi
add eax, ecx
ret
Итак, похоже, что gcc разрешено спекулятивно загружать оба элемента структуры, чтобы исключить ветвление. Но тогда, следующий код считается undefined поведением или является ли gcc-оптимизацией выше незаконным?
#include <stdlib.h>
int naughty_caller(int c) {
Pair *P = (Pair*)malloc(sizeof(Pair)-1); // *** Allocation is enough for A but not for B ***
if (!P) return -1;
P->A = 0x42; // *** Initializing allocation only where it is guaranteed to be allocated ***
int res = foo(P, 1); // *** Passing c=1 to foo should ensure only P->A is accessed? ***
free(P);
return res;
}
Если предположение загрузки произойдет в описанном выше сценарии, вероятность того, что загрузка P- > B вызовет исключение, потому что последний байт P- > B может лежать в нераспределенной памяти. Это исключение не произойдет, если оптимизация отключена.
Вопрос
Является ли gcc-оптимизация, показанная выше, правильной? Где спецификация говорит или подразумевает, что это нормально? Если оптимизация законна, как код в 'naughtly_caller' оказывается undefined поведение?