显示 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 似乎可以推测性地加载两个结构成员,以消除分支。但是,下面的代码是否被视为未定义行为,或者上面的 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”中的代码怎么会变成未定义的行为呢?