2

C++で関数を呼び出すときにrdiをプッシュしてrdiをポップする目的は何ですか? VS2010、x64、デバッグ、最適化なし

C++

int calc()
{
    return 8 + 7;
}

分解:

int calc()
{
000000013F0B1020  push        rdi  
    return 8 + 7;
000000013F0B1022  mov         eax,0Fh  
}
000000013F0B1027  pop         rdi  
000000013F0B1028  ret 
4

2 に答える 2

7

目的はありません。これは、最適化されていないコードの一般的なアーティファクトです。コードジェネレータはpush edi、加算を実行する必要があることを見越して命令を発行します。EDIレジスタは、関数呼び出し間で保持する必要があります。しかし、後で、コンパイル時に追加を実行できることがわかります。

このような無関係なコードを取り除くには、「のぞき穴最適化」が必要です。ただし、その最適化はデバッグビルドでは有効になっていません。実際のコードがどのように見えるかを知るには、オプティマイザーをオンにする必要があります。これは、リリースビルドをビルドすることによって行うのが最適です。それは実際には機能を完全に排除します、あなたはそれがそうするのを防ぐことができます:

__declspec(noline) int calc()
{
    return 8 + 7;
}

リリースビルドで生成されるもの:

    return 8 + 7;
000007F7038E1000  mov         eax,0Fh  
000007F7038E1005  ret  
于 2013-02-03T22:21:14.267 に答える
3

「caller-save」および「callee-save」レジスターについて聞いたことがありますか?

CPU には少数の限られた数のレジスタしかないため、通常、呼び出し元/呼び出された関数が常に異なるレジスタを使用することは不可能です。呼び出し元の関数と呼び出された関数の両方が同じレジスタを使用したい場合は、呼び出しの前後に呼び出し元の値を保存/復元する必要があることを意味します。

レジスタ値の保存/復元は、呼び出し元または呼び出し先のいずれかによって行うことができます。どちらがそうするかは慣習の問題です。「caller-save」レジスターの利点は、呼び出し後にレジスター XYZ の値が必要ないことを呼び出し元が知っている場合、保存/復元操作を省略できることです。「呼び出し先保存」レジスタの利点は、呼び出し先がレジスタ XYZ の値を変更しないことがわかっている場合、保存/復元操作を省略できることです。

あなたのコンパイラはRDIを呼び出し先保存レジスタとして扱いますが、コンパイラの最適化をオンにしない限り、不要な保存/復元操作を省略しないと思います。(誰かがこれが間違っていることを知っている場合は、別の回答を投稿してください!)

更新: x86 呼び出し規約に関する記事を見つけました: http://en.wikipedia.org/wiki/X86_calling_conventions

ほとんどの呼び出し規約では、RDI が呼び出し先保存になることが確認されているようです。これは、他のすべての呼び出し先保存レジスタをプッシュおよびポップしない理由を説明していません。たぶん、ここで何か他のことが起こっています。

于 2013-02-03T21:58:25.797 に答える