18

このコードに注意してください:

#include <stdio.h>
void a(int a, int b, int c)
{
    char buffer1[5];
    char buffer2[10];
}

int main()
{
    a(1,2,3); 
}

その後 :

gcc -S a.c

そのコマンドは、アセンブリ内のソース コードを表示します。

メイン関数で確認できるようになりました。「push」コマンドを使用して関数の引数をスタックにプッシュすることはありません。その代わりに「move」を使用しました

main:
 pushl %ebp
 movl %esp, %ebp
 andl $-16, %esp
 subl $16, %esp
 movl $3, 8(%esp)
 movl $2, 4(%esp)
 movl $1, (%esp)
 call a
 leave

なぜそれが起こるのですか?それらの違いは何ですか?

4

4 に答える 4

19

これについて、gcc のマニュアルには次のように書かれています。

-mpush-args
-mno-push-args
    Use PUSH operations to store outgoing parameters. This method is shorter and usually
    equally fast as method using SUB/MOV operations and is enabled by default. 
    In some cases disabling it may improve performance because of improved scheduling
    and reduced dependencies.

 -maccumulate-outgoing-args
    If enabled, the maximum amount of space required for outgoing arguments will be
    computed in the function prologue. This is faster on most modern CPUs because of
    reduced dependencies, improved scheduling and reduced stack usage when preferred
    stack boundary is not equal to 2. The drawback is a notable increase in code size.
    This switch implies -mno-push-args. 

どうやら-maccumulate-outgoing-argsデフォルトで有効になっており、オーバーライドされています-mpush-args。明示的にコンパイルすると、メソッドに-mno-accumulate-outgoing-args戻ります。PUSH


2019 更新: 最新の CPU は、Pentium M の頃から効率的なプッシュ/ポップを備えています(およびプッシュの使用) は、最終的に2014 年 1 月
-mno-accumulate-outgoing-args にデフォルトになりました。-mtune=generic

于 2010-12-27T00:20:16.903 に答える
8

そのコードは、(更新された) スタック ポインター (esp) からのオフセット位置に定数 (1、2、3) を直接配置しているだけです。コンパイラは、同じ結果で手動で「プッシュ」を行うことを選択しています。

「プッシュ」は、データの設定とスタック ポインターの更新の両方を行います。この場合、コンパイラはスタック ポインターの更新を 1 回だけに減らしています (3 回に対して)。興味深い実験として、関数 "a" を引数を 1 つだけ取るように変更して、命令パターンが変化するかどうかを確認します。

于 2010-12-26T17:45:59.210 に答える
6

gcc は、最適化対象の特定の CPU の実行速度に基づいて命令を選択するなど、あらゆる種類の最適化を行います。x *= n特に n が定数の場合は、SHL、ADD、および/または SUB の組み合わせに置き換えられることが多いことに気付くでしょう。一方、MUL は、SHL-ADD-SUB の組み合わせの平均実行時間 (およびキャッシュ/その他のフットプリント) が MUL の平均実行時間を超える場合、またはn定数ではない場合にのみ使用されます (したがって、shl-add-sub でループを使用すると、より高価です)。

関数引数の場合: MOV はハードウェアで並列化できますが、PUSH はできません。(2 番目の PUSH は、esp レジスターの更新のため、最初の PUSH が終了するまで待機する必要があります。) 関数の引数の場合、MOV を並列で実行できます。

于 2010-12-26T19:05:31.833 に答える
2

ひょっとしてこれは OS X にあるのでしょうか? スタック ポインターを 16 バイト境界に揃える必要があることをどこかで読みました。これで、この種のコード生成が説明できる可能性があります。

記事を見つけました: http://blogs.embarcadero.com/eboling/2009/05/20/5607

于 2010-12-26T19:05:39.123 に答える