PPC-gccのオプティマイズについて

PPC-gccのオプティマイズの様を観察してみたメモです。参考になるかどうかは 不明です(^^;

目次

  1. 関数の引数
  2. 条件判定&ブランチ
  3. bitシフト操作

関数の引数

ppc-gccでは、関数の引数はレジスタで渡されます。最初の引数はr3で、以降r4,r5 ...で渡され、r3に返ってきます。doubleの場合はf1,f2....で渡され、戻り値は f1レジスタに返ってきます。

int add(int i,int j)
{
  return( i+j ) ;
}


の様な関数の場合、

add:
    add 3,3,4
    blr


という命令列になりました。

double add(double i,double j)
{
  return( i+j ) ;
}


の様な関数の場合、

add:
    fadd 1,1,2
    blr


の様な命令列になりました。そのまんまで判りやすいと感じるのは私だけ でしょうか?(^^;

巨大な配列や構造体を渡す場合などはスタック(スタックポインタはr1に 保存されています)を介して受け渡される様ですが、基本はレジスタ渡しとなっています。 必ずスタックを介して関数呼び出しを行うm68kとは随分様子が違うと 思います。レジスタ数が多い事を有効に活用していると言えるのではないで しょうか。

条件判定&ブランチ

次の様なCコードをコンパイルしてみました。


000001: for( i=0 ; i<=100 ; i++){
000002:   if( i==10 || i==86 ){
000003:     printf( "%d\n",i ) ;
000004:   }else{
000005:     printf( "%f\n",sqrt(i) ) ;
000006:   }
000007: }


2行目の条件に注目すると、-O0では以下の様なコードが出力されました。

.L6:
    lwz 0,16(31)
    cmpwi 0,0,10  ← 10と比較
    bc 12,2,.L8
    lwz 0,16(31)
    cmpwi 0,0,86  ← 86と比較
    bc 12,2,.L8
    b .L7


そのまんまといった感じでしょうか。ところが、-O2では以下の様になりました。

.L6:
    xori 9,31,10
    subfic 11,9,0
    adde 9,11,9
    xori 0,31,86
    subfic 11,0,0
    adde 0,11,0
    or. 11,9,0
    bc 12,2,.L7
    la 3,.LC0@l(28)
    mr 4,31
    crxor 6,6,6
    bl printf
    b .L5


いきなり判りにくくなりますが、i==10とi==86の比較を行った後、どちらかが 満たされていれば、3行目のprintf()を実行する というコードになっていま す。

全ての条件のチェックを行い、最後にブランチするかしないかを決定するのと、 比較を2回行うのとでは、実行する命令数的には後者の方が速い様に思えるのですが、 ブランチヒストリをできるだけ壊さない様な要素が働いている様に思え ます。
因みに、私は、最初この論理が理解できなかったので、コンパイラのバグだ と思っていたのですが、結果はシミュレータのバグだった(*1)(^^;という事が ありました。

(*1) 0-0は、キャリーは上がらないと思いこんでいたのですが、 正しくは、2の補数で演算すると0-0はCarry=1,結果=0とならなければいけません。 これが判っていなかったので、オプティマイズ時の複数条件比較の論理が理解 できませんでした(^^;

bitシフト操作

PPCのbitシフト命令は部分シフトやマスク付きシフトを行えるなど、非常に 強力なものになっています。


unsigned int swap1(unsigned int i)
{
  return ( (i&0xff000000)>>24 |
           (i&0x00ff0000)>>8  |
           (i&0x0000ff00)<<8  |
           (i&0x000000ff)<<24 ) ;
}


の様な関数は、

swap1:
    srwi 0,3,24
    rlwinm 9,3,24,1
    rlwinm 11,3,8,8
    or 0,0,9
    or 0,0,11
    slwi 3,3,24
    or 3,0,3
    blr


の様になりました。シフトとビットマスクを使用できるので、シフトした 値をorしていくだけという様なコードになっています。最初にまとめて シフトして、続いてまとめてorしているのは、パイプラインの連結度合い を上げる為の命令列の様な感じがしますが、真相のほどは定かではあり ません(^^;

unsigned int swap2(unsigned int i,unsigned int j)
{
  return ( (i&0xffff0000) | (j&0x0000ffff) ) ;
}


の様なコードは

swap2:
    rlwinm 4,4,0,0xffff
    rlwimi 3,4,0,16,31
    blr
    

の様になりました。この例はかなり特異な感じがしますが、イイ感じの コードになっていると思います(^^;

全体的に感じるのですが、シフト命令を征する事ができれば、PPCを 征したも同然という気がしなくもない様に思えます(<結構本気です)。


TOP PREV