Skip to content

Instantly share code, notes, and snippets.

@takeutch-kemeco
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takeutch-kemeco/9846313 to your computer and use it in GitHub Desktop.
Save takeutch-kemeco/9846313 to your computer and use it in GitHub Desktop.
osecpu078d-gcc4.8.x.patch
GCC-4.7.x と GCC-4.8.x とでは、後置インクリメントの際のインクリメント実行タイミングが異なるので、
この実行タイミングに暗黙に依存したコードでは動作に違いが生じてしまう場合があるようです。
具体的には以下のようなコードの場合に違いが生じるようです:
int a[] = {10, 11, 12};
int *p = a;
*p++ = p[1];
GCC-4.7.x では結果は a[] = {11, 11, 12} となりました。
GCC-4.8.x では結果は a[] = {12, 11, 12} となりました。
(テストは Ubuntu(32bit)版の GCC-4.8.1, Arch(32bit)版の GCC-4.8.2 いずれでも同様の結果となったので、
 原因はディストリビューション依存では無く、GCC-4.8.x 自体に原因があると予想してます)
*p++ = a[1] の挙動について、
推測ですが、おそらく以下のような挙動が行われているのだと予想してます:
GCC-4.7.x の場合:
1. p++ を評価して、変数 p がインクリメントされる予定であるフラグを立てる(まだ実行はされない)
2. *p を評価して、左辺値(代入先アドレス)に &p[0] が設定される。
! 3. この時点で左辺値の評価が終了。(何もしない)
4. p[1] を右辺値として得る。(この時点で p は p + 0 なので、p[1] は *(p + 1) となるので、値は11)
5. 右辺値の結果を、左辺値(代入先アドレス)へ代入する。
! 6. ステートメントの終了に到達した時点で、インクリメントフラグが立っている場合は p += 1 を実際に行う。
GCC-4.8.xの場合:
1. 同様
2. 同様
! 3. この時点で左辺値の評価が終了し、インクリメントフラグが立っている場合は p += 1 を実際に行う。
4. p[1] を右辺値として得る。(この時点で p は p + 1 なので p[1] は *(p + 1 + 1) となるので、値は12)
5. 同様
! 6. ステートメントの終了。(何もしない)
以下は d.c をコンパイルし、それを逆アセンブルして得たオブジェクトコードの検証です:
/* d.c */
void f(void)
{
int a[] = {10, 11, 12};
int *p = a;
*p++ = p[1];
}
GCC-4.7.2 (Debian7.4 32bit) の場合:
d47.o: file format elf32-i386
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
6: c7 45 f0 0a 00 00 00 movl $0xa,-0x10(%ebp) # a[0] = 10 |
d: c7 45 f4 0b 00 00 00 movl $0xb,-0xc(%ebp) # a[1] = 11 |
14: c7 45 f8 0c 00 00 00 movl $0xc,-0x8(%ebp) # a[2] = 12 => a[] = {10, 11, 12}
1b: 8d 45 f0 lea -0x10(%ebp),%eax # t = &a[0] |
1e: 89 45 fc mov %eax,-0x4(%ebp) # p = t => p = a
21: 8b 45 fc mov -0x4(%ebp),%eax # t = p | #左辺値アドレス確定
24: 8b 50 04 mov 0x4(%eax),%edx # u = t[1] |
27: 8b 45 fc mov -0x4(%ebp),%eax # t = p | #右辺値確定
2a: 89 10 mov %edx,(%eax) # *t = u => *p = p[1] #左辺値先への右辺値代入
2c: 83 45 fc 04 addl $0x4,-0x4(%ebp) # p += 1 => p += 1 #インクリメント実行
30: c9 leave
31: c3 ret
GCC-4.8.2 (Arch_Linux 32bit) の場合:
d48.o: file format elf32-i386
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
6: c7 45 f0 0a 00 00 00 movl $0xa,-0x10(%ebp) # a[0] = 10 |
d: c7 45 f4 0b 00 00 00 movl $0xb,-0xc(%ebp) # a[1] = 11 |
14: c7 45 f8 0c 00 00 00 movl $0xc,-0x8(%ebp) # a[2] = 12 => a[] = {10, 11, 12}
1b: 8d 45 f0 lea -0x10(%ebp),%eax # t = &a[0] |
1e: 89 45 fc mov %eax,-0x4(%ebp) # p = t => p = a
21: 8b 45 fc mov -0x4(%ebp),%eax # t = p | #左辺値アドレス確定
24: 8d 50 04 lea 0x4(%eax),%edx # u = &t[1] |
27: 89 55 fc mov %edx,-0x4(%ebp) # p = u => p += 1 #インクリメント実行
2a: 8b 55 fc mov -0x4(%ebp),%edx # u = p |
2d: 8b 52 04 mov 0x4(%edx),%edx # u = u[1] | #右辺値確定
30: 89 10 mov %edx,(%eax) # *p = u => *p = p[2] #左辺値先への右辺値代入
32: c9 leave
33: c3 ret
diff --git a/osecpu.c b/osecpu.c
index edce223..4d127d3 100644
--- a/osecpu.c
+++ b/osecpu.c
@@ -1779,7 +1779,8 @@ prefix_continue:
} else {
j += 4;
w.dst -= 6;
- jitCompPutByte1(w.dst, w.dst[1] ^ 0xf0);
+ w.dst[0] = w.dst[1] ^ 0xf0;
+ w.dst += 1;
}
jitCompPutByte1(w.dst, j & 0xff);
}
@@ -2442,7 +2443,9 @@ cmpcc1:
if (-128 - 4 <= j && j < 0) {
j += 4;
w.dst -= 6;
- jitCompPutByte2(w.dst, w.dst[1] ^ 0xf0, j & 0xff);
+ w.dst[0] = w.dst[1] ^ 0xf0;
+ w.dst[1] = j & 0xff;
+ w.dst += 2;
}
#endif
src += 6;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment