为什么转换不同的指针类型会导致 TBAA(基于类型的别名分析)违规?

Why does casting different pointer types cause TBAA (Type-Based Alias Analysis) violation?

我正在读这个 blog,然后我发现了一些代码 我真的不明白。为什么这是错误的代码?

float *P;

void zero_array() {
  int i;
  for (i = 0; i < 10000; ++i)
  P[i] = 0.0f;
}

int main() {
  P = (float*)&P;  // cast causes TBAA violation in zero_array.
  zero_array();
}

希望有人能解释一下。

如博客中所述,这:

for (i = 0; i < 10000; ++i)
    P[i] = 0.0f;
}

可以优化成这样:

memset(P, 0, 40000);

这是因为 P 应该指向一个 float 的数组,其中一个浮点数是 4 个字节(在这个例子中)。

但是如果你这样做:

P = (float*)&P;

那么P实际上指向了一个指针数组指向float。如果 float * 的大小为 8 个字节,优化将失败。

这是一个更具体的例子:

int main() {
    int i;

    P = malloc(10000 * sizeof(float));
    zero_array();   // this properly sets an array of 10000 floats to 0.
    free(P);

    float **PP = malloc(10000 * sizeof(float *));
    P = (float *)PP;
    zero_array();    // if sizeof(float *) == 8, the first 5000 pointers will be NULL, 
                     // and the next 5000 will contain garbage.
    free(PP);
}

以下代码:

float *P;
P = (float*)&P; 
P[0] = 0.0f;

违反了严格的别名规则。

对象 P 具有 有效类型 float *,因为这是其声明的类型。 C11 6.5/6:

The effective type of an object for an access to its stored value is the declared type of the object, if any.

第二行执行后,表达式P[0]表示与P相同的内存位置。 (注意:为了解释的目的,假设 sizeof(float) == sizeof(float *)。显然,如果这些尺寸不同,那么情况会更糟!)

执行 P[0] = 0.0f 使用类型 float 的左值来访问类型 float * 的对象。这违反了 6.5/7:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types

在这些引用中,"access" 表示读或写。 "following types" 的列表不包括任何涵盖使用 float 表达式读取 float *

的异常

这个例子有点难以理解,因为它是自我参照的。然而,它与这个更简单的例子的原理完全相同:

float *Q;
float *P = &Q;
*P = 0.0f;

在这种情况下 Q 具有类型 float *,但它是通过类型 float.

的左值写入的

此示例演示了该规则的基本原理。假设不存在严格的别名规则,并且允许所有别名。然后评估 P[0] = 0.0f 更改 P。在一个常见的实现中,这会导致 P 现在成为一个空指针,但我们可以很容易地想象一些其他的值被赋值,这使得 P 成为指向其他变量的有效指针。

在那种情况下,P[1] = 0.0f 行必须 成功写入另一个变量。因此,编译器将无法用 memset 替换循环;这样的 memset 会更新 P[1] 的原始位置,但不会在 P[0] = ....; 执行后更新 P[1] 的新位置。不允许优化改变程序的可观察行为。

严格别名规则的存在意味着编译器可以执行优化,例如将此循环更改为 memset,而不必担心循环的内容可能会即时更改指针。


注意。 P = (float *)&P 行也可能是对齐冲突。对齐规则和严格别名规则是完全独立的规则,不应相互混淆。 (一些写得不好的页面试图 'explain' 严格别名作为某种对齐要求)。