2011-06-05
課題
アセンブラの説明をほとんどしてないのに
アセンブラからどのような最適化が行われているのか解析せよとか鬼畜
GCCのオプションから行われていることを書いてやろうと思ったけど、
それだと意味ないし。ぬぬぬ。
アセンブラ云々
命令
leal
Load Effective Address (long?)
leal 4(%esp), %eax
espの値に4足したメモリ位置にあるアドレスをeaxにいれる
http://d.hatena.ne.jp/suu-g/20080505/1210012224ll
sall
sall $2,%eax
eaxを2bit左シフト
xorb
xor。
xorb %al, %al
alに0をセット。
p2align
第一引数はロケーションカウンタの境界を指定。
第二引数はパディングの値を指定。標準は0。
第三引数はロケーションカウンタが進める最大値を指定する。
.p2align 4,,7
ロケーションカウンタを2の4乗の倍数まですすめる。
最大7バイトまで。
これ以上進めなくちゃいけない時は諦める。
.p2align 3
ロケーションカウンタを2の3乗の倍数まですすめる。
http://www.xgc.com/manuals/m1750-ada/xgc-alu/r3300.html
movzbl
move zero extended byte to long
第一引数の下位8bitのアドレスを第二引数の下位8bitにコピーする。
それより上位24bitはゼロで埋める。
http://www.cs.auckland.ac.nz/references/macvax/op-codes/Instructions/movz.html
レジスタ
ecx
ループのカウントとか、カウントの上限とかに使うらしい
eax
計算の途中の値を覚えておくのに使ったり。ループのカウントとかにも利用
http://hp.vector.co.jp/authors/VA014520/asmhsp/chap2.html
al
eaxの下位8bitのレジスタ。ehとかclとかもある。ここ見ろ。
http://codezine.jp/article/detail/393?p=2
esi
メモリのアドレスを覚えるのに使ったり
コードの最適化
C, C++, Programming
関数
#include <stdio.h> void func1 (int x, int a[]) { int i, j; for (i = 0; i < 10; i ++) { a[i] = i; } } int main (void) { int x[10]; func1 (5, x); return 0; }
gcc -O0 -S でこんぱいる
.file "ex4.c" .text .globl func1 .type func1, @function func1: pushl %ebp movl %esp, %ebp subl $16, %esp movl $0, -4(%ebp) jmp .L2 .L3: movl -4(%ebp), %eax sall $2, %eax addl 12(%ebp), %eax movl -4(%ebp), %edx movl %edx, (%eax) addl $1, -4(%ebp) .L2: cmpl $9, -4(%ebp) jle .L3 leave ret .size func1, .-func1 .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $56, %esp leal -40(%ebp), %eax movl %eax, 4(%esp) movl $5, (%esp) call func1 movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
gcc -O1 -Sでこんぱいる
.file "ex4.c" .text .globl func1 .type func1, @function func1: pushl %ebp movl %esp, %ebp movl 12(%ebp), %edx movl $0, %eax .L2: movl %eax, (%edx,%eax,4) addl $1, %eax cmpl $10, %eax jne .L2 popl %ebp ret .size func1, .-func1 .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $56, %esp leal -40(%ebp), %eax movl %eax, 4(%esp) movl $5, (%esp) call func1 movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
mainは変わらず。
func1のfor文で変わってる。
俺がわかる範囲だと、
配列のインデックスに使ってるeaxの計算を、
sall云々からmovlで一行にしてる。
movl %eax, (%edx,%eax,4)
右の(%edx,%eax,4)は、(edx+eax*4)みたいな計算をする。
あとは、ジャンプの回数を少なくしたり。
gcc -O2 -Sでこんぱいる
.file "ex4.c" .text .p2align 4,,15 .globl func1 .type func1, @function func1: pushl %ebp xorl %eax, %eax movl %esp, %ebp movl 12(%ebp), %edx .p2align 4,,7 .p2align 3 .L2: movl %eax, (%edx,%eax,4) addl $1, %eax cmpl $10, %eax jne .L2 popl %ebp ret .size func1, .-func1 .p2align 4,,15 .globl main .type main, @function main: pushl %ebp xorl %eax, %eax movl %esp, %ebp subl $48, %esp leal -40(%ebp), %edx .p2align 4,,7 .p2align 3 .L7: movl %eax, (%edx,%eax,4) addl $1, %eax cmpl $10, %eax jne .L7 xorb %al, %al leave ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
もはやfunc1は呼んでいない。
そのかわり同様の構造がmainに組み込まれている。
それと頻繁にalign命令を使ってロケーションカウンタを調整してる。
他はeaxを0にするために、xorで0にしてる。
alを使ってるのは、レジスタの操作を最小限にしようとしているからだと思う。
gcc -O3 -s でこんぱいる
.file "ex4.c" .text .p2align 4,,15 .globl func1 .type func1, @function func1: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax movl $0, (%eax) movl $1, 4(%eax) movl $2, 8(%eax) movl $3, 12(%eax) movl $4, 16(%eax) movl $5, 20(%eax) movl $6, 24(%eax) movl $7, 28(%eax) movl $8, 32(%eax) movl $9, 36(%eax) popl %ebp ret .size func1, .-func1 .p2align 4,,15 .globl main .type main, @function main: pushl %ebp xorl %eax, %eax movl %esp, %ebp popl %ebp ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
配列の中は0〜9が確定しているので、その値がぶち込まれてしまった。
while(1){LOVE++;}を最適化
C, C++, Programming
最近ツイッターで
"while(1){LOVE++}"と書き込まれてる指輪があって、
そのソースは最適化でLOVEが消えてただの無限ループになる云々という話があったので検証。
用意したソースはこちら。
int main() { int LOVE = 0; while(1){LOVE++;} }
それじゃアセンブラを見てみましょ。
まずは最適化抜き。
gcc -O0 -s hoge.c
.file "hoge.c" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $16, %esp movl $0, -4(%ebp) ;LOVE=0 .L2: addl $1, -4(%ebp) ;LOVE++ jmp .L2 ;JUMP TO L2 .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
LOVEは残ってますね。
gcc -O1 -s hoge.c
.file "hoge.c" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp .L2: jmp .L2 ;JUMP TO L2 .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" .section .note.GNU-stack,"",@progbits
愛は最適化によって消滅しました。
追記
なんかvolatileで最適化抑制すんじゃねっつー話らしいです。
あーvolatileね。あーうん、あれね。あれでしょ。あれ。
知ってる知ってる。えーとね。あれでしょ。
マルチスレッド云々書いてると最適化されたら困る時があるらしくって、そーいうのにつかうんだそーです。
ここが詳しかった。
