attribute malloc

関連する話として、アロケータに、__attribute__((malloc))を付けましょうという話。


まず、

extern void * alloc_buffer( );
extern void free_buffer( int * );

int 
nanika( int *a )
{
	int x;
	int ret;
	int * buf = alloc_buffer( );

	x = a[0];

	buf[0] = 0;

	ret = x - a[0];

	free_buffer( buf );

	return ret;
}

こういうコードを考える。さて、このコードの戻り値は?
0 … のように見える(x = a[0]; ret = x-a[0]; return 0;)けど、ひょっとすると、0ではないかもしれない。
なぜか?alloc_bufferの返してくる値が、int *aが指しているバッファと重なってるかもしれないから(何を言ってるかわからない人は、もう一度上の話を読み直そう。それでもわからない場合は、抗議のメールを出そう!そんなことされたら困る)。

上のをコンパイルすると、

nanika:
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%esi
	pushl	%ebx
	movl	8(%ebp), %ebx
	call	alloc_buffer
	movl	(%ebx), %esi
	subl	$12, %esp
	movl	$0, (%eax)
	subl	(%ebx), %esi  ; 減算
	pushl	%eax
	call	free_buffer
	leal	-8(%ebp), %esp
	popl	%ebx
	movl	%esi, %eax
	popl	%esi
	leave
	ret

減算してるのがわかると思う。最適化してくれない。


ところが…!このコードを、

int 
nanika2( int *a )
{
	int x;
	int ret;
	int * buf = malloc( 10 );

	x = a[0];
	buf[0] = 0;
	ret = x - a[0];

	free( buf );

	return ret;
}

こんなふうに、malloc、freeを使うように修正すると、

nanika2:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$20, %esp
	pushl	$10
	call	malloc
	movl	$0, (%eax)
	movl	%eax, (%esp)
	call	free
	xorl	%eax, %eax
	leave
	ret

アラ不思議!減算処理が消えてしまいました!xorl eax, eax!(GCC4.1だとうまくいかない?GCC3.2だとうまくいった。)


実は、GCC拡張のattributeに、'malloc'というのがあって、

GCC info より
`malloc'
The `malloc' attribute is used to tell the compiler that a function
may be treated as if any non-`NULL' pointer it returns cannot
alias any other pointer valid when the function returns. This
will often improve optimization. Standard functions with this
property include `malloc' and `calloc'. `realloc'-like functions
have this property as long as the old pointer is never referred to
(including comparing it to the new pointer) after the function
returns a non-`NULL' value.


適当訳: `malloc' attribute は 関数が返すNULL以外の戻りポインタが他の有効なポインタとエイリアスしないものと示すのに使います。
これは、たまに最適化を良くします。
(以下略)

まあ、なんかそんな。戻したポインタがエイリアスしませんよ。と。そういう話。

extern void * alloc_buffer( ) __attribute__((malloc));
extern void free_buffer( int * );

int 
nanika( int *a )
{
	int x;
	int ret;
	int * buf = alloc_buffer( );

	x = a[0];
	buf[0] = 0;
	ret = x - a[0];
	free_buffer( buf );

	return ret;
}

こうすると、ほら不思議。


というわけで、自分でアロケータ書いた場合は、__attribute__((malloc))を付けましょう