MSDN Flash 2009/8/11 No.330 の答え - (2)
もう先月の話になるのですね…2つ目のコードの問題について解説したいと思います。
掲載したコードは以下の通り
char s[10];
memset(s, 0, 10);
double *val = (double*) (&s[2]);
printf("%lf\n", *val);
期待される実行結果は x86, x64環境では 0.000000 と表示、Itanium環境では整列違反例外によるプログラムの異常終了です。
これを一度に Itaniumでのアセンブリに展開すると話がややこしくなるので、
char s[10];
double *val;
double val2;
memset(s, 0, 10);
val = (double*) (&s[2]);
val2 = *val; // 問題の部分
printf("%lf\n", val2);
と書き直すことにします。
このコードをコンパイルすると
{ .mib //R-Addr: 0X01e0
adds r29=val$, sp //6 cc:0
adds r27=val2$, sp //6 cc:0
nop.b 0;;
}
{ .mmi //R-Addr: 0X01f0
ld8 r28=[r29];; //6 cc:1
ldfd f6=[r28] //6 cc:3
nop.i 0;;
}
{ .mmi //R-Addr: 0X0200
stfd [r27]=f6;; //6 cc:9
adds r26=val2$, sp //7 cc:0
addl r25=@gprel(__imp_printf#),gp //7 cc:0
}
となります。
val2 = *val; が
ld8 r28=[r29];;
ldfd f6=[r28]
と展開されていることがわかります。しかし、この r29 は 16-bytes aligned を仮定された char s[10] の 2-bytes目を指しているので miss-alignedとなります。
Itaniumでは参照されるアドレスは自然長に整列されていなければなりません。この場合、8-bytes alignmentでなければならないため整列違反例外となるわけです。
このように、渡された型が自然長に整列されているかどうかわからない場合は __unaligned キーワードをつけることで問題を回避することができます。2行目を
__unaligned double *val;
と書き換えると、コンパイルの結果は
{ .mmi //R-Addr: 0X01e0
adds r29=val$, sp;; //6 cc:0
ld8 r28=[r29] //6 cc:1
adds r29=val2$, sp;; //6 cc:1
}
{ .mib //R-Addr: 0X01f0
mov r27=r28 //6 cc:2
adds r26=1, r28 //6 cc:2
nop.b 0;;
}
{ .mmb //R-Addr: 0X0200
ld1 r25=[r27], 2 //6 cc:3
ld1 r22=[r26], 2 //6 cc:3
nop.b 0;;
}
{ .mmi //R-Addr: 0X0210
ld1 r20=[r27], 2 //6 cc:4
ld1 r18=[r26], 2 //6 cc:4
dep r21=r22, r25, 8, 8;; //10 cc:4
}
{ .mmi //R-Addr: 0X0220
ld1 r16=[r27], 2 //6 cc:5
ld1 r11=[r26], 2 //6 cc:5
dep r19=r20, r21, 16, 8;; //6 cc:5
}
{ .mmi //R-Addr: 0X0230
ld1 r9=[r27] //6 cc:6
ld1 r31=[r26] //6 cc:6
dep r17=r18, r19, 24, 8;; //6 cc:6
}
{ .mii //R-Addr: 0X0240
nop.m 0
dep r15=r16, r17, 32, 8;; //6 cc:7
dep r10=r11, r15, 40, 8;; //6 cc:8, 00000028H
}
{ .mii //R-Addr: 0X0250
nop.m 0
dep r8=r9, r10, 48, 8;; //6 cc:9, 00000030H
dep r30=r31, r8, 56, 8;; //6 cc:10, 00000038H
}
{ .mmi //R-Addr: 0X0260
setf.d f6=r30;; //6 cc:11
stfd [r29]=f6 //6 cc:17
nop.i 0;;
}
となり、このコードは問題なく動作するようになります。(しかし、長くて読む気にならないのは気のせいでしょうか…)
違いは ld8 が 8個の ld1 に置き換えられているところです。つまり、double val; を構成する全ての領域を 1-byteずつ読み込んでいることになります。
問題なく動作するようになったとしても、3-cyclesで完了する処理に 9-cyclesもかけることになるわけですから処理は遅くなります。
x86/x64 においても、自然長への整列がなされていない場合はキャッシュヒットに対するペナルティが課せられるため動作が低下します。
このように、自然長に配置されていないスタックや構造体を用いる場合には十分に注意しましょう。
Comments
Anonymous
January 12, 2010
このコードは問題思をいます。このように十分に注意わからなします。Anonymous
January 12, 2010
If the double I8 was 16 bytes of Peer . It is I7=I8 @println I7[16] return ;;To make I8 of 16 bytes double.Anonymous
January 12, 2010
What do I7 and I8 indicate?