| C ユーザーズガイドの追補 |
型に基づいた別名解析
このマニュアルでは、型に基づいた別名解析を使って最適化を行うための Sun WorkShopTM Compilers C の新しいオプションおよびいくつかのプラグマについて説明しています。この機能を使用すると、ポインタの使用方法に関する、型に基づいた情報を C プログラム中で表現することができます。C コンパイラにこの情報を使用させることで、プログラム中のポインタに基づいたメモリー参照の別名解析を、より効果的かつ効率的に行うことができます。
はじめに
新しいオプション
-xalias_levelには、7 つある別名解析のレベルのうちの 1 つを指定できます。7 つのレベルごとに、C プログラム中でのポインタの使用方法に関する属性が定められています。
-xalias_levelオプションで指定するレベルが高いほど、コンパイラはコード中でのポインタの使用方法に関する「仮定」の範囲を広げます。コンパイラの仮定が少ないほど、プログラミングの自由度は上がります。しかし、仮定が少ないと、最適化による実行時パフォーマンスの改善はあまり期待できません。-xalias_levelオプションを使ってコンパイラに広範囲に渡る仮定を行わせ、その仮定を踏まえてコーディングすることで、最適化によって実行時パフォーマンスを飛躍的に向上させることができます。
-xalias_levelオプションは、各翻訳単位で適用される別名解析のレベルを指定します。より細かい単位でレベルを設定したい場合は、新しいプラグマを使用してください。このプラグマを使用すると、翻訳単位で現在有効になっている別名解析のレベルが何であれ、特定の型またはポインタ変数の関係の別名解析レベルを個別に指定できます。このプラグマは、翻訳単位全体には-xalias_levelオプションのレベルが適用できるが、特定のポインタ変数が通常とは異なる方法で使用されているため、そのポインタ変数だけがオプションのレベルに準拠していない、といった場合に便利です。
-xalias_levelオプションコンパイラは、型に基づいた別名解析による最適化に設定できる仮定の内容を、
-xalias_levelオプションで指定されたレベルをもとに決定します。オプションで指定されたレベルは、コンパイル対象の翻訳単位に対して適用されます。第 1 のデフォルトは-xalias_level=anyで、型に基づいた別名解析は行われないことを意味しています。値を指定しないで、つまり、-xalias_levelとだけ指定した場合は、デフォルトとして-xalias_level=layoutが使用されます。
-xalias_levelオプションで指定したレベルに定義されている、別名解析に関する仮定または制限事項のすべてに準拠しなかった場合は、プログラムの動作は未定義になるため注意が必要です。
-xalias_level=listlist には、次の表にあるいずれかのレベル名を指定します。
プラグマ
型に基づいた別名解析をさらに細かく制御することで、より大きな効果が得られる場合には、以降で説明するプラグマを使用してください。このプラグマに翻訳単位中の個々の型またはポインタ変数に別名関係を指定すると、そのレベルはオプションで指定されたレベルより優先して適用されます。このプラグマは、翻訳単位全体には
-xalias_levelオプションのレベルが適用できるが、特定のポインタ変数が通常とは異なる方法で使用されているため、そのポインタ変数だけがオプションのレベルに準拠していない、といった場合に便利です。
注 - プラグマに指定する型または変数の名前は、そのプラグマの手前で宣言しなければなりません。宣言をしないと警告メッセージが発行され、プラグマは無視されます。プラグマの指定に当てはまる最初のメモリー参照の後でプラグマが指定されている場合は、プログラムの結果は未定義になります。
level 「-xalias_level オプション」に列挙したレベル名のいずれかを指します。 type 次のいずれかを指します。 char、short、int、long、longlong、float、double、longdoublevoid(すべてのポインタ型を指す)typedefname (typedef宣言で定義済みの型の名前が続く)structname (キーワードstructの後ろに構造体タグ名が続く)unionname (キーワードunionの後ろに共用体タグ名が続く)pointer_name 翻訳単位中のポインタ型を持つ任意の変数の名前
#pragmaalias_levellevellistlevel には、
any、basic、weak、layout、strict、std、およびstrongの 7 つのレベルのうちの 1 つを指定します。list には型名またはポインタ名を指定できます。指定する型名またはポインタ名が複数ある場合は、コンマで区切って指定してください。 たとえば、次のように指定できます。このプラグマは、指定された別名解析のレベルが、翻訳単位中で指定された型を持つすべてのメモリー参照に対して、または、指定されたすべてのポインタ変数が間接参照されている翻訳単位中のすべての間接参照に対して適用されます。
特定の間接参照に対して複数の別名解析レベルを指定した場合は、ポインタ名で指定されたレベルが (存在する場合は) その他のすべてのレベルより優先的に適用されます。同様に、型名で指定されたレベルが存在する場合は、そのレベルがオプションで指定されたレベルより優先的に適用されます。次の例では、
#pragmaalias_levelがanyより高いレベルでプログラムがコンパイルされている場合、pにはstdレベルが適用されます。
typedef int*int_ptr;int_ptr p;#pragma alias_level strong (int_ptr)#pragma alias_level std (p)
#pragmaalias (type,type[,type]...)このプラグマは「指定された型は互いに別名とみなす」と指定します。次の例では、コンパイラは「間接アクセス
*ptは間接アクセス*pfの別名である」と指定します。
#pragma alias (int, float)int*pt;float*pf;
#pragmaalias(pointer,pointer[,pointer]...)このプラグマは「指定されたポインタ変数を間接参照する時点で、間接参照されているポインタ値は指定されたその他のポインタ変数の間接参照を指すことできる」と指定します。このプラグマは、現在適用されている別名解析レベルの別名に関する仮定を無効にします。次の例では、コンパイラは「双方の型に関係なく、間接アクセス
*pは間接アクセス*qの別名である」と仮定します。
#pragma alias(p, q)
#pragmamay_point_to (pointer,variable[,variable]...)このプラグマは「指定されたポインタ変数を間接参照する時点で、間接参照されているポインタ値は指定された変数に含まれているオブジェクトを指すことができる」と指定します。このプラグマは、現在適用されている別名解析レベルの別名に関する仮定を無効にします。次の例では、コンパイラは「間接アクセス
*pは間接アクセスa、b、cの別名である」と仮定します。
#pragma alias may_point_to(p, a, b, c)
#pragmanoalias (type,type[,type]...)このプラグマは「指定された型は互いに別名とはみなさない」と指定します。次の例では、コンパイラは「間接アクセス
*pは間接アクセス*psを別名とはみなさない」と仮定します。
struct S {float f;...} *ps;#pragma noalias(int, struct S)int*p;
#pragmanoalias (pointer,pointer[,pointer]...)このプラグマは、指定されたポインタ変数を間接参照する時点では、間接参照されているポインタ値は指定されたその他のポインタ変数と同じオブジェクトを指さない」と指定します。このプラグマは、現在適用されている別名解析レベルの別名に関する仮定を無効にします。次の例では、コンパイラは「双方の型に関係なく、間接アクセス
*pは間接アクセス*qの別名ではない」と仮定します。
#pragma noalias(p, q)
#pragma may_not_point_to (pointer,variable[,variable]...)このプラグマは「指定されたポインタ変数を間接参照する時点で、間接参照されているポインタ値は指定された変数に含まれているオブジェクトを指さない」と指定します。このプラグマは、現在適用されている別名解析レベルの別名に関する仮定を無効にします。次の例では、コンパイラは「間接アクセス
*pは間接アクセスa、b、cの別名ではない」と仮定します。
#pragma may_not_point_to(p, a, b, c)例題
この節では、
-xalias_levelオプションを使用した場合に、プログラム中のメモリー参照が受ける制約について例題を使って説明します。例題 1
次のコード例をさまざまな別名解析レベルでコンパイルして、各型の別名解析の違いを比較してみましょう。
struct foo {int f1;short f2;short f3;int f4;} *fp;struct bar {int b1;int b2;int b3;} *bp;int*ip;short*sp;例題 1 を
-xalias_level=anyオプションでコンパイルすると、コンパイラは、以下の間接アクセスは互いに別名であると仮定します。
*ip、*sp、*fp、*bp、fp->f1、fp->f2、fp->f3、fp->f4、bp->b1、bp->b2、bp->b3例題 1 を
-xalias_level=basicオプションでコンパイルすると、コンパイラは、以下の間接アクセスは互いに別名であると仮定します。
*ip、*bp、fp->f1、fp->f4、bp->b1、bp->b2、bp->b3さらに、
*spおよびfp->f2とfp->f3、*spと*fpは、互いに別名とみなせます。ただし
-xalias_level=basicオプションを指定すると、コンパイラは以下の内容を仮定します。
*ipは*spの別名ではない。*ipはfp->f2およびfp->f3の別名ではない。*spとfp->f1、fp->f4、bp->b1、bp->b2、bp->b3は別名ではない。これは、上記の間接アクセスのアクセス型が互いに異なる基本型であるためです。
例題 1 を
-xalias_level=weakオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*ipは*fp、fp->f1、fp->f4、*bp、bp->b1、bp->b2、およびbp->b3と別名とみなせる。*spは*fp、fp->f2、およびfp->f3の別名とみなせる。fp->f1はbp->b1の別名とみなせる。fp->f4はbp->b3の別名とみなせる。コンパイラが
fp->fp1がbp->b2の別名ではないと仮定します。これは、f1が構造体の中で 0 オフセットのフィールドであるのに対して、b2は構造体の中の 4 バイトオフセットのフィールドであるためです。同じ理由からコンパイラは、fp->f1はbp->b3の、fp->f4はbp->b1およびbp->b2の別名ではないと仮定します。例題 1 を
-xalias_level=layoutオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*ipは*fp、*bp、fp->f1、fp->f4、bp->b1、bp->b2、およびbp->b3の別名とみなせる。*spは*fp、fp->f2、およびfp->f3の別名とみなせる。fp->f1はbp->b1および*bpの別名とみなせる。*fpと*bpは、互いに別名とみなせる。
fp->f4はbp->b3の別名とはみなしません。これは、f4とb3が、fooおよびbarの先頭の共通部分であるフィールドに対応していないためです。例題 1 を
-xalias_level=strictオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*ipは*fp、fp->f1、fp->f4、*bp、bp->b1、bp->b2、およびbp->b3の別名とみなせる。*spは*fp、fp->f2、およびfp->f3の別名とみなせる。
-xalias_level=strictを指定すると、コンパイラは「*fp、*bp、fp->f1、fp->f2、fp->f3、fp->f4、bp->b1、bp->b2、およびbp->b3は、互いに別名ではない」と仮定します。これは、フィールド名を無視した場合にfooとbarが同じではないためです。ただし、fpとfp->f1、およびbpとbp->b1は互いに別名とみなせます。例題 1 を
-xalias_level=stdオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*ipは*fp、fp->f1、fp->f4、*bp、bp->b1、bp->b2、およびbp->b3の別名とみなせる。*spは*fp、fp->f2、およびfp->f3の別名とみなせる。ただし、コンパイラは「
fp->f1はbp->b1、bp->b2またはbp->b3の別名ではない」と仮定します。これは、フィールド名を考慮した場合にfooとbarが同じではないためです。例題 1 を
-xalias_level=strongオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*ipはfp->f1、fp->f4、bp->b1、bp->b2、およびbp->b3の別名ではない。
これは、*ipなどのポインタは構造体の内部を指すべきではないという理由からです。*spはfp->f1またはfp->f3の別名ではない。
理由は上記と同じです。*ipは*fp、*bp、および*spの別名ではない。
これは型が異なるためです。*spは*fp、*bp、および*ipの別名ではない。
これは型が異なるためです。例題 2
次のコード例をさまざまな別名解析レベルでコンパイルして、各型の別名解析の違いを比較してみましょう。
struct foo {int f1;int f2;int f3;}*fp;struct bar {int b1;int b2;int b3;}*bp;例題 2 を
-xalias_level=anyオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*fp、*bp、fp->f1、fp->f2、fp->f3、bp->b1、bp->b2、およびbp->b3は、すべて互いに別名とみなせる。
これは、-xalias_level=anyのレベルでは、どのメモリーアクセスも互いに別名とみなせるためです。例題 2 を
-xalias_level=basicオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*fp、*bp、fp->f1、fp->f2、fp->f3、bp->b1、bp->b2、およびbp->b3は、すべて互いに別名とみなせる。ポインタ*fpおよび*bpを使用したフィールドアクセスは、互いに別名とみなせる。
これは、構造体のすべての型が同じ基本型であるためです。例題 2 を
-xalias_level=weakオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*fpと*fpは、互いに別名とみなせる。fp->f1は、bp->b1、*bp、および*fpと別名とみなせる。fp->f2は、bp->b2、*bp、および*fpと別名とみなせる。fp->f3は、bp->b3、*bp、および*fpと別名とみなせる。ただし、
-xalias_level=weakオプションを使用すると以下の制限事項も仮定されます。
fp->f1は、bp->b2またはbp->b3の別名ではない。
これは、f1のオフセット (0 バイト) が、b2のオフセット (4 バイトオフセット) およびb3のオフセット (8 バイトオフセット) と異なるためです。fp->f2は、bp->b1またはbp->b3の別名ではない。
これは、f2のオフセット (4 バイト) がb1のオフセット (0 バイト) およびb3のオフセット (8 バイト) と異なるためです。fp->f3は、bp->b1またはbp->b2の別名ではない。
これは、f3のオフセット (8 バイト) がb1のオフセット (0 バイト) およびb2のオフセット (4 バイト) と異なるためです。例題 2 を
-xalias_level=layoutオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*fpと*bpは、互いに別名とみなせる。fp->f1は、bp->b1、*bp、および*fpと別名とみなせる。fp->f2は、bp->b2、*bp、および*fpと別名とみなせる。fp->f3は、bp->b3、*bp、および*fpと別名とみなせる。ただし、
-xalias_level=layoutオプションを使用すると以下の制限事項も仮定されます。
fp->f1は、bp->b2またはbp->b3の別名ではない。
これは、フィールドf1が、fooおよびbarの先頭の共通部分にあるフィールドb1に対応するためです。fp->f2は、bp->b1またはbp->b3の別名ではない。
これは、フィールドf2が、fooおよびbarの先頭の共通部分にあるフィールドb2に対応するためです。fp->f3は、bp->b1またはbp->b2の別名ではない。
これは、フィールドf3が、fooおよびbarの先頭の共通部分にあるフィールドb3に対応するためです。例題 2 を
-xalias_level=strictオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
*fpと*bpは、互いに別名とみなせる。fp->f1は、bp->b1、*bp、および*fpと別名とみなせる。fp->f2は、bp->b2、*bp、および*fpと別名とみなせる。fp->f3は、bp->b3、*bp、および*fpと別名とみなせる。ただし、
-xalias_level=strictオプションを使用すると以下の制限事項も仮定されます。
fp->f1は、bp->b2またはbp->b3の別名ではない。
これは、フィールドf1が、fooおよびbarの先頭の共通部分にあるフィールドb1に対応しているためです。fp->f2は、bp->b1またはbp->b3の別名ではない。
これは、フィールドf2が、fooおよびbarの先頭の共通部分にあるフィールドb2に対応しているためです。fp->f3は、bp->b1またはbp->b2の別名ではない。
これは、フィールドf3が、fooおよびbarの先頭の共通部分にあるフィールドb3に対応しているためです。例題 2 を
-xalias_level=stdオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1、fp->f2、fp->f3、bp->b1、bp->b2、およびbp->b3は、互いに別名ではない。例題 2 を
-xalias_level=strongオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1、fp->f2、fp->f3、bp->b1、bp->b2、およびbp->b3は、互いに別名ではない。例題 3
次のソースコードを例にして、特定の別名解析レベルでは内部ポインタを扱えないことについて説明します。内部ポインタの定義については 「strong」の項目を参照してください。
struct foo {int f1;struct bar *f2;struct bar *f3;int f4;int f5;struct bar fb[10];} *fp;struct barstruct bar *b2;struct bar *b3;int b4;} *bp;bp=(struct bar*)(&fp->f2);例題 3 の中の間接参照は、
weak、layout、strict、またはstdのレベルでは別名とはみなせません。ポインタ代入bp=(struct bar*)(&fp->f2)の後、次のように 2 つのメモリーアクセスがメモリー中の同じ場所を参照します。
fp->f2およびbp->b2は、メモリー中の同じ場所を参照する。fp->f3およびbp->b3は、メモリー中の同じ場所を参照する。fp->f4およびbp->b4は、メモリー中の同じ場所を参照する。しかし、
weak、layout、strict、およびstdのレベルでは、コンパイラは「fp->f2とbp->b2は別名ではない」と仮定します。これは、b2のオフセット(0 バイト) がf2のオフセット (4 バイト) と異なること、 および、fooとbarが互いに共通の先頭部分を持たないことが理由です。同じ理由から、コンパイラは「bp->b3はfp->f3の別名ではなく、bp->b4はfp->f4の別名ではない」と仮定します。このように、ポインタ代入
bp=(struct bar*)(&fp->f2)によって、コンパイラの別名解析に関する仮定が正しくないものになってしまいます。そのため、最適化が正しく行われない可能性もあります。この問題が発生しないように、次のようにコードの内容を変更してコンパイルしてみてください。
ポインタ代入
bp=(struct bar*)(&fp->f2)の後、次の 2 つのメモリーアクセスがメモリー中の同じ場所を参照します。上のコードの変更部分でわかるように、
fp->f2という式はfp->fb.b2とも表現できます。fp->fbの型はbarなので、fp->f2はbarのフィールドb2にアクセスします。さらに、bp->b2もbarのフィールドb2にアクセスします。従って、コンパイラは「fp->f2はbp->b2の別名である」と仮定します。同様に「fp->f3はbp->b3の、fp->f4はbp->b4の別名である」とも仮定します。結果として、コンパイラによる別名の仮定はポインタ代入による実際の別名と一致します。例題 4
struct foo {int f1;int f2;} *fp;struct bar {int b1;int b2;} *bp;struct cat {int c1;struct foo cf;int c2;int c3;} *cp;struct dog {int d1;int d2;struct bar db;int d3;} *dp;例題 4 を
-xalias_level=weakオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1は、bp->b1、cp->c1、dp->d1、cp->cf.f1、およびdf->db.b1を別名とみなせる。fp->f2は、bp->b2、cp->cf.f1、dp->d2、cp->cf.f2、df->db.b2、およびcp->c2を別名とみなせる。bp->b1は、fp->f1、cp->c1、dp->d1、cp->cf.f1、およびdf->db.b1を別名とみなせる。bp->b2は、fp->f2、cp->cf.f1、dp->d2、cp->cf.f1、およびdf->db.b2を別名とみなせる。
fp->f2がcp->c2を別名とみなせるのは、*dpが*cpを、および*fpがdp->dbを別名とみなせるためです。
cp->c1は、fp->f1、bp->b1、dp->d1、およびdp->db.b1を別名とみなせる。cp->cf.f1は、fp->f1、fp->f2、bp->b1、bp->b2、dp->d2、およびdp->d1を別名とみなせる。
cp->cf.f1はdp->db.b1の別名ではありません。
cp->c2はdp->db.b1の別名ではなく、cp->c2はdp->d3の別名ではありません。オフセットについて考えれば、
*dpがcp->cfの別名である場合にのみ、cp->c2はdb->db.b1を別名とみなせます。しかし、*dpがcp->cfの別名である場合、dp->db.b1はfoocfの末尾を越えた位置にある別名になってしまいます。これは、オブジェクトの制限で禁止されています。したがって、コンパイラは「cp->c2はdb->db.b1の別名とはみなせない」と仮定します。
cp->c3がdp->db.b2の別名でないことに注目してください。これは、間接参照に含まれる型を持つフィールドのオフセットが、2 つのメモリー参照間で異なり、重複していないためです。このため、コンパイラはこれらが別名ではないと仮定します。
dp->d1はfp->f1、bp->b1、およびcp->c1の別名とみなせる。dp->d2は、fp->f2、bp->b2、およびcp->cf.f1の別名とみなせる。dp->db.b1は、fp->f1、bp->b1、およびcp->c1の別名とみなせる。dp->db.b2は、fp->f2、bp->b2、cp->c2、およびcp->cf.f1の別名とみなせる。dp->d3はcp->c3の別名とみなせる。
dp->d3がcp->cf.f2の別名でないことに注目してください。これは、間接参照に含まれる型を持つフィールドのオフセットが、2 つのメモリー参照間で異なり、重複していないためです。このため、コンパイラはこれらが別名ではないと仮定します。例題 4 を
-xalias_level=layoutオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1、bp->b1、cp->c1、およびdp->d1は、すべて互いに別名とみなせる。fp->f2、bp->b2、およびdp->d2は、すべて互いに別名とみなせる。fp->f1は、cp->cf.f1およびdp->db.b1の別名とみなせる。bp->b1は、cp->cf.f1およびdp->db.b1の別名とみなせる。fp->f2は、cp->cf.f2およびdp->db.b2の別名とみなせる。bp->b2は、cp->cf.f2およびdp->db.b2の別名とみなせる。例題 4 を
-xalias_level=strictオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1とbp->b1は、互いに別名とみなせる。fp->f2とbp->b2は、互いに別名とみなせる。fp->f1は、cp->cf.f1およびdp->db.b1の別名とみなせる。bp->b1は、cp->cf.f1およびdp->db.b1の別名とみなせる。fp->f2は、cp->cf.f2およびdp->db.b2の別名とみなせる。bp->b2は、cp->cf.f2およびdp->db.b2の別名とみなせる。例題 4 を
-xalias_level=stdオプションでコンパイルすると、コンパイラは以下の内容を仮定します。
fp->f1はcp->cf.f1の別名とみなせる。bp->b1はdp->db.b1の別名とみなせる。fp->f2はcp->cf.f2の別名とみなせる。bp->b2はdp->db.b2の別名とみなせる。例題 5
struct foo {short f1;short f2;int f3;} *fp;struct bar {int b1;int b2;} *bp;union moo {struct foo u_f;struct bar u_b;} u;別名解析レベルごとのコンパイラの仮定内容は、次のようになります。
- 例題 5 を
-xalias_level=weakオプションでコンパイルした場合、fp->f3とbp->b2は互いに別名とみなせる。- 例題 5 を
-xalias_level=layoutオプションでコンパイルした場合、別名とみなせるフィールドはない。- 例題 5 を
-xalias_level=strictオプションでコンパイルした場合、fp->f3とbp->b2は互いに別名とみなせる。- 例題 5 を
-xalias_level=stdオプションでコンパイルした場合、別名とみなせるフィールドはない。例題 6
struct bar;struct foo {struct foo *ffp;struct bar *fbp;} *fp;struct bar {struct bar *bbp;long b2;} *bp;別名解析レベルごとのコンパイラの仮定内容は、次のようになります。
- 例題 6 を
-xalias_level=weakオプションでコンパイルした場合、fp->ffpとbp->bbpのみが互いに別名とみなせる。- 例題 6 を
-xalias_level=layoutオプションでコンパイルした場合、fp->ffpとbp->bbpは互いに別名とみなせる。- 例題 6 を
-xalias_level=strictオプションでコンパイルした場合、別名とみなせるフィールドはない。
これはタグを取り除いた後も、2 つの構造体の型が同じにならないためです。- 例題 6 を
-xalias_level=stdオプションでコンパイルした場合、別名とみなせるフィールドはない。
これは、2 つのフィールドの型とタグが同じでないためです。例題 7
struct foo;struct bar;#pragma alias (struct foo, struct bar)struct foo {int f1;int f2;} *fp;struct bar {short b1;short b2;int b3;} *bp;この例で使用されているプラグマは、コンパイラに「
fooとbarは、互いに別名とみなせる」と指示します。これによって、コンパイラは次の別名関係を仮定します。
|
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |