x86_64 ABI では、関数に可変引数がある場合AL
(の一部EAX
) は、その関数への引数を保持するために使用されるベクトル レジスタの数を保持することが期待されます。
あなたの例では:
printf("%d", 1);
には整数の引数があるため、ベクトル レジスタは必要ないため、AL
0 に設定されます。
一方、例を次のように変更すると:
printf("%f", 1.0f);
次に、浮動小数点リテラルがベクトル レジスタに格納され、それに応じて次のAL
ように設定され1
ます。
movsd LC1(%rip), %xmm0
leaq LC0(%rip), %rdi
movl $1, %eax
call _printf
予想通り:
printf("%f %f", 1.0f, 2.0f);
2 つの浮動小数点引数があるため、コンパイラは次のように設定さAL
れます。2
movsd LC0(%rip), %xmm0
movapd %xmm0, %xmm1
movsd LC2(%rip), %xmm0
leaq LC1(%rip), %rdi
movl $2, %eax
call _printf
他の質問について:
puts
%eax
また、単一のポインターしか使用しませんが、呼び出しの直前にゼロに設定されています。どうしてこれなの?
すべきではありません。例えば:
#include <stdio.h>
void test(void) {
puts("foo");
}
でコンパイルするとgcc -c -O0 -S
、次のように出力されます。
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
call _puts
leave
ret
ゼロに%eax
はなりません。ただし、削除する#include <stdio.h>
と、結果のアセンブリは%eax
を呼び出す直前にゼロになりputs()
ます。
pushq %rbp
movq %rsp, %rbp
leaq LC0(%rip), %rdi
movl $0, %eax
call _puts
leave
ret
その理由は、2 番目の質問に関連しています。
これは、自分の void proc() 関数の呼び出しの前にも発生します (-O2 が設定されていても) が、void proc2(int param) 関数を呼び出したときにゼロにはなりません。
コンパイラが関数の宣言を認識しない場合、コンパイラはそのパラメータについて何も仮定せず、関数は可変引数を十分に受け入れることができます。空のパラメーター リストを指定した場合も同じことが当てはまります (これはすべきではなく、ISO/IEC によって廃止予定の C 機能としてマークされています)。コンパイラーは関数パラメーターに関する十分な情報を持っていない%eax
ため、関数が可変引数を持つように定義されている可能性があるため、関数を呼び出す前にゼロに設定します。
例えば:
#include <stdio.h>
void function() {
puts("foo");
}
void test(void) {
function();
}
wherefunction()
には空のパラメーター リストがあり、結果は次のようになります。
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
call _function
leave
ret
void
ただし、次のように、関数がパラメーターを受け入れない場合を指定する推奨プラクティスに従う場合:
#include <stdio.h>
void function(void) {
puts("foo");
}
void test(void) {
function();
}
次に、コンパイラfunction()
は が引数を受け入れないことを認識します。特に、可変引数を受け入れないため、%eax
その関数を呼び出す前にクリアしません。
pushq %rbp
movq %rsp, %rbp
call _function
leave
ret