● -ffloat-store
さっそくですが最適化オプション-ffloat-storeを指定すると吐き出したアセンブラコードがどのように変化するかを見てみましょう.
検証用のコードをリスト1に示します.
〔リスト1〕test00.c
|
#include <stdio.h>
main(int argc,char* argv[])
{
register double i=4503599627370496e+11;
printf("%e\n",i);
return;
}
|
|
このコードをオプションなしでコンパイルすると,リスト2のようなアセンブラコードを吐きます.
〔リスト2〕test00.s
|
.file "test00.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC1:
.string "%e\n"
.align 8
.LC0:
.long 0xe8000000,0x45774876
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
fldl .LC0
fstpl -8(%ebp)
addl $-4,%esp
pushl -4(%ebp)
pushl -8(%ebp)
pushl $.LC1
call printf
addl $16,%esp
jmp .L2
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
ここではbpレジスタに浮動小数点値をセットしていますが,セットしたくない場合もあります.その場合,オプションを-ffloat-storeにすると,リスト3のようなアセンブラコードを吐きます.
〔リスト3〕test00.s(-ffloat-store)
|
.file "test00.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC1:
.string "%e\n"
.align 8
.LC0:
.long 0xe8000000,0x45774876
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
fldl .LC0
fstpl -8(%ebp)
addl $-4,%esp
fldl -8(%ebp)
subl $8,%esp
fstpl (%esp)
pushl $.LC1
call printf
addl $16,%esp
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
● -fno-defer-pop
次に-fno-defer-popオプションを付けた場合について検証してみましょう.
検証用のコードをリスト4に示します.
〔リスト4〕test01.c
|
#include <stdio.h>
main(int argc,char* argv[])
{
void func01(int x,int y,int z);
void func02(int x,int y,int z);
void func03(int x,int y,int z);
void func04(int x,int y,int z);
int z1 = 0;
int z2 = 0;
int z3 = 0;
int z4 = 0;
func01(100,150,z1);
func02(300,150,z2);
func03(10,150,z3);
func04(1000,100,z4);
return;
}
int func01(int x,int y,int z)
{
z= x+y;
}
int func02(int x,int y,int z)
{
z= x-y;
}
int func03(int x,int y,int z)
{
z= x*y;
}
int func04(int x,int y,int z)
{
z= x/y;
}
|
|
このコードをオプションなしでコンパイルすると,リスト5のようなアセンブラコードを吐きます.
〔リスト5〕test01.s
|
.file "test01.c"
.version "01.01"
gcc2_compiled.:
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
addl $-4,%esp
pushl $0
pushl $150
pushl $100
call func01
addl $-4,%esp
pushl $0
pushl $150
pushl $300
call func02
addl $32,%esp
addl $-4,%esp
pushl $0
pushl $150
pushl $10
call func03
addl $-4,%esp
pushl $0
pushl $100
pushl $1000
call func04
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.align 4
.globl func01
.type func01,@function
func01:
pushl %ebp
movl %esp,%ebp
movl %ebp,%esp
popl %ebp
ret
.Lfe2:
.size func01,.Lfe2-func01
.align 4
.globl func02
.type func02,@function
func02:
pushl %ebp
movl %esp,%ebp
movl %ebp,%esp
popl %ebp
ret
.Lfe3:
.size func02,.Lfe3-func02
.align 4
.globl func03
.type func03,@function
func03:
pushl %ebp
movl %esp,%ebp
movl %ebp,%esp
popl %ebp
ret
.Lfe4:
.size func03,.Lfe4-func03
.align 4
.globl func04
.type func04,@function
func04:
pushl %ebp
movl %esp,%ebp
movl %ebp,%esp
popl %ebp
ret
.Lfe5:
.size func04,.Lfe5-func04
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
関数の引き数が参照渡しの場合,関数呼び出し後に無条件にPOPされますが,この場合は関数呼び出し後に値をスタックしているのがわかります.
では,-fno-defer-popオプションを付けた場合をリスト6に示します.
〔リスト6〕test01.s(-fno-defer-pop)
|
.file "test01.c"
.version "01.01"
gcc2_compiled.:
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
movl $0,-4(%ebp)
movl $0,-8(%ebp)
movl $0,-12(%ebp)
movl $0,-16(%ebp)
addl $-4,%esp
movl -4(%ebp),%eax
pushl %eax
pushl $150
pushl $100
call func01
addl $16,%esp
addl $-4,%esp
movl -8(%ebp),%eax
pushl %eax
pushl $150
pushl $300
call func02
addl $16,%esp
addl $-4,%esp
movl -12(%ebp),%eax
pushl %eax
pushl $150
pushl $10
call func03
addl $16,%esp
addl $-4,%esp
movl -16(%ebp),%eax
pushl %eax
pushl $100
pushl $1000
call func04
addl $16,%esp
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.align 4
.globl func01
.type func01,@function
func01:
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
movl 12(%ebp),%edx
leal (%edx,%eax),%ecx
movl %ecx,16(%ebp)
.L3:
movl %ebp,%esp
popl %ebp
ret
.Lfe2:
.size func01,.Lfe2-func01
.align 4
.globl func02
.type func02,@function
func02:
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
movl 12(%ebp),%edx
movl %eax,%ecx
subl %edx,%ecx
movl %ecx,16(%ebp)
.L4:
movl %ebp,%esp
popl %ebp
ret
.Lfe3:
.size func02,.Lfe3-func02
.align 4
.globl func03
.type func03,@function
func03:
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
imull 12(%ebp),%eax
movl %eax,16(%ebp)
.L5:
movl %ebp,%esp
popl %ebp
ret
.Lfe4:
.size func03,.Lfe4-func03
.align 4
.globl func04
.type func04,@function
func04:
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
leal 12(%ebp),%ecx
cltd
idivl (%ecx)
movl %eax,16(%ebp)
.L6:
movl %ebp,%esp
popl %ebp
ret
.Lfe5:
.size func04,.Lfe5-func04
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
関数呼び出し後にaレジスタの値をbレジスタに蓄積しています.
● -fforce-mem
次に,-fforce-memオプションを付けた場合について検証してみましょう.
同じく検証用のコードをリスト7に示します.
〔リスト7〕test02.c
|
#include <stdio.h>
main(int argc,char* argv[])
{
int* pt = (long)0xbffffcb8;
int* pt1 = (long)0xbffffcb8;
int* pt2 = (long)0xbffffcb8;
int* pt3 = (long)0xbffffcb8;
*pt = (int)1;
printf("%d\n",pt);
printf("%d\n",pt1);
printf("%d\n",pt2);
printf("%d\n",pt3);
return;
}
|
|
このコードをオプション無しでコンパイルするとリスト8のようなアセンブラコードを吐きます.同一のメモリアドレスでも別のレジスタに格納されています.
〔リスト8〕test02.s
|
.file "test02.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "%dエn"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $24,%esp
movl $-1073742664,-4(%ebp)
movl $-1073742664,-8(%ebp)
movl $-1073742664,-12(%ebp)
movl $-1073742664,-16(%ebp)
movl -4(%ebp),%eax
movl $1,(%eax)
addl $-8,%esp
movl -4(%ebp),%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
addl $-8,%esp
movl -8(%ebp),%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
addl $-8,%esp
movl -12(%ebp),%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
addl $-8,%esp
movl -16(%ebp),%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
では,-fforce-memオプションを付けた場合の出力をリスト9に示します.
〔リスト9〕test02.s(-fforce-mem)
|
.file "test02.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "%dエn"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $20,%esp
pushl %ebx
movl $-1073742664,%ebx
movl $1,-1073742664
addl $-8,%esp
pushl %ebx
pushl $.LC0
call printf
addl $-8,%esp
pushl %ebx
pushl $.LC0
call printf
addl $32,%esp
addl $-8,%esp
pushl %ebx
pushl $.LC0
call printf
addl $-8,%esp
pushl %ebx
pushl $.LC0
call printf
movl -24(%ebp),%ebx
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
同一のメモリアドレスは,同一のレジスタに一度だけ格納されています.問題は固定値で表されたメモリアドレスの内容が外部のハードウェアによって更新される場合などに最適化が意図しないものになってしまうことです.
● -fomit-frame-pointer
今度は-fomit-frame-pointerオプションを付けた場合について検証してみましょう.
同じく検証用のコードをリスト10に示します.
〔リスト10〕test03.c
|
#include <stdio.h>
main(int argc,char* argv[])
{
register int pt0 = 10;
register int pt1 = 20;
register int pt2 = 30;
register int pt3 = 40;
register int pt4 = 50;
register int pt5 = 60;
register int pt6 = 70;
register int pt7 = 80;
register int pt8 = 100;
register int pt9 = 200;
register unsigned long pt10 = 4294967290UL;
register unsigned long pt11 = 4294967290UL;
register unsigned long pt12 = 4294967290UL;
register unsigned long pt13 = 4294967200UL;
register unsigned long pt14 = 4294967290UL;
return;
}
|
|
このコードをオプションなしでコンパイルすると,リスト11のようなアセンブラコードを吐きます.
〔リスト11〕test03.s
|
.file "test03.c"
.version "01.01"
gcc2_compiled.:
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $44,%esp
pushl %edi
pushl %esi
pushl %ebx
movl $10,%eax
movl $20,%edx
movl $30,%ecx
movl $40,%ebx
movl $50,%esi
movl $60,%edi
movl $70,-4(%ebp)
movl $80,-8(%ebp)
movl $100,-12(%ebp)
movl $200,-16(%ebp)
movl $-6,-20(%ebp)
movl $-6,-24(%ebp)
movl $-6,-28(%ebp)
movl $-96,-32(%ebp)
movl $-6,-36(%ebp)
jmp .L2
.L2:
leal -56(%ebp),%esp
popl %ebx
popl %esi
popl %edi
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
フレームポインタであるEBPレジスタに値がセットされています.
では,-fomit-frame-pointerオプションを付けた場合の出力をリスト12に示します.
〔リスト12〕test03.s(-fomit-frame-pointer)
|
.file "test03.c"
.version "01.01"
gcc2_compiled.:
.text
.align 4
.globl main
.type main,@function
main:
subl $48,%esp
pushl %edi
pushl %esi
pushl %ebx
movl $10,%eax
movl $20,%edx
movl $30,%ecx
movl $40,%ebx
movl $50,%esi
movl $60,%edi
movl $70,44(%esp)
movl $80,40(%esp)
movl $100,36(%esp)
movl $200,32(%esp)
movl $-6,28(%esp)
movl $-6,24(%esp)
movl $-6,20(%esp)
movl $-96,16(%esp)
movl $-6,12(%esp)
jmp .L2
.L2:
popl %ebx
popl %esi
popl %edi
addl $48,%esp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.95.3 20010315 (release)"
|
|
フレームポインタであるEBPレジスタを使わずにスタックポインタで指し示される領域に値が設定されています.
ターゲットマシンによっては,このオプションを指定しても何も変化しません.VAXなどはフレームポインタを自動的に扱うので,プログラマが意識する必要がないからです.マクロFRAME_POINTER_REQUIREDの式の値がゼロであれば,このオプション指定に意味はありません.
|