2012年6月28日 星期四

gcc strict-aliasing是什麼?

  • 前言 
思考下面這一段code
void foo(int *i1, int *i2, int *res)
{
    for(int i=0;i<10;i++){
        *res += *i1 + *i2;
    }
}
如果想要讓這段code變快,我們可能會想要這樣作
void foo(int *i1, int *i2, int *res)
{
    int tmp=*i1 + *i2;
    for(int i=0;i<10;i++)
        *res += tmp;
    }
}
但考慮到當呼叫端是如下就會發生問題:
foo(&v1,&v3,&v3);
我們永遠不知道caller 到底會丟什麼進來。因此,這樣的code是無法最佳化的。那如果是下面這個例子呢?
void foo(int32_t *i1, int32_t *i2, int64_t *res)
{
    for(int i=0;i<10;i++){
        *res += *i1 + *i2;
    }
}
res的type是int64_t理論上應該就不會有問題了吧?如果呼叫端是:
foo(&v1,&v2,(int64_t *)&v3);
就會發生我們預期外的錯誤了。
  • 什麼是strict-aliasing?
為了讓上面這種狀況不會發生最佳化後的結果是我們預期外的,我們就啟用strict-aliasing來避免"不同型態"的指標指向相同的address除非兩種型態是類似的。例如:unsigned int可以alias int但不可以alias double。這麼我們就可以避免上述的情況發生。 
  • 如何啟用strict-aliasing?
在compile時如果我們開啟-O2,-O3,-Os或直接加上-fstrict-aliasing就會啟動fstrict-aliasing的選項。
  • 簡單的方法避免Werror=strict-aliasing
假設我們的程式中需要作轉型例如
int foo() {
    short a[2];
    a[0]=1;
    a[1]=0;
    int *i=(int*)&a;
}
我們可以用union來表達
union union_a {
    int i;
    short s[2];
};
int foo() {
    union union_a u;
    u.i=1;
    u.s[0]=1;
    u.s[1]=0;
}
這樣就不會有warning: dereferencing type-punned pointer will break strict-aliasing rules的warning了。

沒有留言: