Hatena::ブログ(Diary)

duost

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

メモリのアドレスを覚えるのに使ったり

コードの最適化

関数

#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++;}を最適化

最近ツイッターで

"while(1){LOVE++}"と書き込まれてる指輪があって、

そのソースは最適化でLOVEが消えてただの無限ループになる云々という話があったので検証。

http://togetter.com/li/144840


用意したソースはこちら。

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ね。あーうん、あれね。あれでしょ。あれ。

知ってる知ってる。えーとね。あれでしょ。


マルチスレッド云々書いてると最適化されたら困る時があるらしくって、そーいうのにつかうんだそーです。

ここが詳しかった。

http://proger.blog10.fc2.com/blog-entry-20.html

Author:MARISHI
Twitter:@MARISHI