strict-aliasingを体験してみた。

gcc47 -O3 -c でオブジェクトコードを生成して、objdump -dで逆アセンブルして調べた。

型がちがえばオブジェクトは別ものなのでエイリアスを仮定しないコードが出る。

int
foo_intp_longp(int *a, long *b)                 
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //mov    $0x1,%eax        <-- no aliasing
    *b = 2;                                     //movq   $0x2,(%rsi)
    return *a;                                  //retq
}                                               

型が同じだと同じオブジェクトを指しているかもしれないのでエイリアスを仮定したコードが出る。

int
foo_intp_intp(int *a, int *b)                   
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //movl   $0x2,(%rsi)
    *b = 2;                                     //mov    (%rdi),%eax      <-- aliasing
    return *a;                                  //retq
}                                               

引数の型はちがってもキャストして同じになるとエイリアスを仮定したコードが出る。

int
foo_intp_longp_cast_int(int *a, long *b)        
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //movl   $0x2,(%rsi)
    *(int*)b = 2;                               //mov    (%rdi),%eax      <-- aliasing
    return *a;                                  //retq
}                                               

違う型へのキャストならエイリアスを仮定しないコードが出る。

int
foo_intp_longp_cast_short(int *a, long *b)      
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //mov    $0x1,%eax        <-- no aliasing
    *(short*)b = 2;                             //movw   $0x2,(%rsi)
    return *a;                                  //retq
}                                               

違う型でもchar*は特別扱いなのでエイリアスを仮定したコードが出る。

int
foo_intp_longp_cast_charp(int *a, long *b)      
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //movb   $0x2,(%rsi)
    *(char*)b = 2;                              //mov    (%rdi),%eax      <-- aliasing
    return *a;                                  //retq
}                                               

char*/void*を経由したキャストをしてもエイリアスは仮定されない。

int                                             
foo_intp_longp_cast_voidp(int *a, long *b)      
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //mov    $0x1,%eax        <- no aliasing
    *(short*)(void*)b = 2;                      //movw   $0x2,(%rsi)
    return *a;                                  //retq              
}
int
foo_intp_longp_cast_charp_shortp(int *a, long *b)
{                                               //movl   $0x1,(%rdi)
    *a = 1;                                     //mov    $0x1,%eax
    *(short*)(char*)b = 2;                      //movw   $0x2,(%rsi)
    return *a;                                  //retq
}

-fno-strict-aliasingをつけると、すべての場合でaliasingを仮定したコード生成になった。

C99で導入された restrict をつかうと(-fno-strict-aliasingをつけてコンパイルしても)エイリアスが仮定されないコードが出る。

int
foo_intp_intp_restrict(int * restrict a, int * restrict b)
{                       //movl   $0x1,(%rdi)
    *a = 1;             //movl   $0x2,(%rsi)
    *b = 2;             //mov    $0x1,%eax
    return *a;          //retq
}                       

出てきたコードが他のものとはちょっとちがってmov $0x1,%eaxが後ろの方になっている。そっちの方が効率がいいのかな?

両方のパラメータにrestrictをつけないといけないようだ。片方だけだとエイリアスを仮定したコードが出る。

int
foo_intp_intp_restrict1(int * restrict a, int *b)
{                       //movl   $0x1,(%rdi)
    *a = 1;             //movl   $0x2,(%rsi)
    *b = 2;             //mov    (%rdi),%eax
    return *a;          //retq
}
int
foo_intp_intp_restrict2(int *a, int * restrict b)
{                       //movl   $0x1,(%rdi)
    *a = 1;             //movl   $0x2,(%rsi)
    *b = 2;             //mov    (%rdi),%eax
    return *a;          //retq
}