Oracle Solaris Studio 12.2: C ユーザーガイド

5.4 メモリー参照の制限の例

ここでは、実際のソースファイルに登場する可能性の高いコードの例を説明します。それぞれの例のあとに、コンパイラの仮定について説明します。これらの仮定は、適用レベルの型に基づいた解析によって作成されます。

5.4.1 例 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;

この例が -xalias_level=any オプションでコンパイルされる場合、コンパイラは次の間接アクセスを相互の別名とみなします。

*ip*sp*fp*bpfp->f1fp->f2fp->f3fp->f4bp->b1bp->b2bp->b3

この例が -xalias_level=basic オプションでコンパイルされる場合、コンパイラは次の間接アクセスを相互の別名とみなします。

*ip*bpfp->f1 fp->f4bp->b1bp->b2 bp->b3

また、*spfp->f2、および fp->f3 は相互に別名設定でき、*sp および *fp も相互に別名設定できます。

しかし、-xalias_level=basic を指定した場合、コンパイラは次のように仮定します。

2 つの間接アクセスの基本型が異なるため、コンパイラはこれらの仮定を作成します。

この例が -xalias_level=weak オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

コンパイラは、fp->fp1bp->b2 を別名設定しないと仮定します。これは、f1 が構造体に 0 のオフセットを保持するフィールドである一方で、b2 が構造体に 4 バイトのオフセットを保持するフィールドであるからです。同様に、コンパイラは、fp->f1bp->b3 を別名設定せず、fp->f4bp->b1 または bp->b2 を別名設定しないと仮定します。

この例が -xalias_level=layout オプションでコンパイルされる場合、コンパイラは、次の情報を仮定します。

fp->f4 は、bp->b3 を別名設定しません。これは、f4b3foo および bar の共通初期シーケンスで対応するフィールドではないからです。

この例が -xalias_level=strict オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

-xalias_level=strict を指定すると、コンパイラは、*fp*bpfp->f1fp->f2fp->f3fp->f4bp->b1bp->b2、および bp->b3 が相互に別名設定しないと仮定します。これは、フィールド名が無視されるときに、foo および bar が同じではないからです。ただし、fpfp->f1 を別名設定し、bpbp->b1 を別名設定します。

この例が -xalias_level=std オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

ただし、fp->f1 は、bp->b1bp->b2、または bp->b3 を別名設定しません。これは、フィールド名が注視されるときに、foo および bar が同じではないからです。

この例が -xalias_level=strong オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

5.4.2 例 2

次の例のソースコードを考えてみましょう。さまざまなレベルの別名設定でコンパイルすることにより、それぞれの型の別名設定の関係性を理解できます。

struct foo {
    int f1;
    int f2;
    int f3;
} *fp;

struct bar {
    int b1;
    int b2;
    int b3;
} *bp;

この例が -xalias_level=any オプションでコンパイルされる場合、コンパイラでは、次の別名情報を仮定します。

*fp*bpfp->f1fp->f2fp->f3bp->b1bp->b2、および bp->b3 はすべて相互に別名設定できます。2 つのメモリーアクセスが -xalias_level=any レベルで相互に別名設定するからです。

この例が -xalias_level=basic オプションでコンパイルされる場合、コンパイラでは、次の別名情報を仮定します。

*fp*bpfp->f1fp->f2fp->f3bp->b1bp->b2、および bp->b3 はすべて相互に別名設定できます。すべての構造体フィールドが同じ基本型であるため、ポインタ *fp および *bp を使用する 2 つのフィールドアクセスは、この例において相互に別名設定できます。

この例が -xalias_level=weak オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

ただし、-xalias_level=weak を指定すると、次の制限が課されます。

この例が -xalias_level=layout オプションでコンパイルされる場合、コンパイラでは、次の別名情報を仮定します。

ただし、-xalias_level=layout を指定すると、次の制限が課されます。

この例が -xalias_level=strict オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

ただし、-xalias_level=strict を指定すると、次の制限が課されます。

この例が -xalias_level=std オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

fp->f1fp->f2fp->f3 bp->b1bp->b2、および bp->b3 は相互に別名設定しません。

この例が -xalias_level=strong オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

fp->f1fp->f2fp->f3 bp->b1bp->b2、および bp->b3 は相互に別名設定しません。

5.4.3 例 3

次の例のソースコードは、特定レベルの別名設定が内部ポインタを処理できないことを示します。内部ポインタの定義については、表 B–13 を参照してください。

struct foo {
        int f1;
        struct bar *f2;
        struct bar *f3;
        int f4;
        int f5;
        struct bar fb[10];
} *fp;

struct bar
        struct bar *b2;
        struct bar *b3;
        int b4;
} *bp;

bp=(struct bar*)(&fp->f2);

この例の参照解除は、weaklayoutstrict、または std でサポートされません。ポインタ割り当て bp=(struct bar*)(&fp->f2) の実行後、対になった 次のメモリーアクセスは同じメモリー位置に接触します。

ただし、オプション weaklayoutstrict、および std を指定する場合、コンパイラは、fp->f2 および bp->b2 が別名設定しないことを仮定します。コンパイラがこのように仮定する理由は、b2 のオフセットが 0 である一方で f2 が 4 バイトのオフセットを保持することと、foo および bar が共通の初期シーケンスを保持しないことにあります。同様に、コンパイラは、bp->b3fp->f3 を別名設定しないこと、および bp->b4fp->f4 を別名設定しないことも仮定します。

そのため、ポインタ割り当てbp=(struct bar*)(&fp->f2) により、別名情報に関するコンパイラの仮定が正しくない状況が作成されます。これにより、最適化が正しく行われない可能性があります。

次の例に示されている変更を行なったあとで、コンパイルを実行してください。


struct foo {
        int f1;
        struct bar fb;   /* Modified line */
#define f2 fb.b2         /* Modified line */
#define f3 fb.b3         /* Modified line */
#define f4 fb.b4         /* Modified line */
        int f5;
        struct bar fb[10];
} *fp;

struct bar
        struct bar *b2;
        struct bar *b3;
        int b4;
} *bp;

bp=(struct bar*)(&fp->f2);

ポインタ割り当て bp=(struct bar*)(&fp->f2) の実行後、対になった 次のメモリーアクセスは同じメモリー位置に接触します。

前述のコード例に示された変更内容を調べることにより、式 fp->f2 が式 fp->fb.b2 のもう 1 つの書式であることが理解できます。fp->fb の型が bar であるため、fp->f2barb2 フィールドにアクセスします。さらに、bp->b2barb2 フィールドにアクセスします。そのため、コンパイラは、fp->f2bp->b2 を別名設定することを仮定します。同様に、コンパイラは、fp->f3bp->b3 を別名設定し、fp->f4bp->b4 を別名設定することも仮定します。その結果、コンパイラの仮定する別名設定は、ポインタ割り当てで設定された実際の別名と一致します。

5.4.4 例 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;

この例が -xalias_level=weak オプションでコンパイルされる場合、コンパイラは、次の別名情報を仮定します。

fp->f2 は、cp->c2 を別名設定できます。*dp*cp を別名設定でき、*fpdp->db を別名設定できるからです。

cp->cf.f1 は、dp->db.b1 を別名設定しません。

cp->c2dp->db.b1 を別名設定せず、 cp->c2dp->d3 を別名設定しません。

オフセットに関連して、*dpcp->cf を別名設定する場合にかぎり、cp->c2db->db.b1 を別名設定できます。ただし、*dpcp->cf を別名設定する場合、dp->db.b1foo cf の末尾を超えて別名設定する必要がありますが、これはオブジェクトの制限事項で禁じられています。そのため、コンパイラは、cp->c2db->db.b1 を別名設定できないと仮定します。

cp->c3 は、dp->d3 を別名設定できます。

cp->c3 は、dp->db.b2 を別名設定しません。参照解除に関連する型のフィールドのオフセットが異なり、重複することがないため、これらのメモリー参照は別名設定を行いません。この事実に基づき、コンパイラは、それらのメモリー参照が別名設定できないと仮定します。

dp->d3 は、cp->cf.f2 を別名設定しません。参照解除に関連する型のフィールドのオフセットが異なり、重複することがないため、これらのメモリー参照は別名設定を行いません。この事実に基づき、コンパイラは、それらのメモリー参照が別名設定できないと仮定します。

この例が -xalias_level=layout オプションでコンパイルされる場合、コンパイラでは、次の別名情報だけを想定します。

この例が -xalias_level=strict オプションでコンパイルされる場合、コンパイラでは、次の別名情報だけを想定します。

この例が -xalias_level=std オプションでコンパイルされる場合、コンパイラでは、次の別名情報だけを想定します。

5.4.5 例 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.4.6 例 6

次の例のソースコードを考えてみましょう。

struct bar;

struct foo {
        struct foo *ffp;
        struct bar *fbp;
} *fp;

struct bar {
        struct bar *bbp;
        long        b2;
} *bp;

それぞれの別名レベルに基づいて、コンパイラは次のように仮定します。

5.4.7 例 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 が相互に別名設定できることがコンパイラに伝えられます。コンパイラは、別名情報について次のように仮定します。