4

組み立ての質問を投稿するとは思ってもみませんでした。:-)

GCCには、asm関数の拡張バージョンがあります。この関数は、アセンブリコード、出力リスト、入力リスト、上書きリストの4つのパラメーターを取ることができます。

私の質問は、上書きリストのレジスタがゼロになっているのですか?以前にそこにあった値(実行中の他のコードから)はどうなりますか。

更新:これまでの私の答えを検討する際に(ありがとう!)、レジスターはclobber-listにリストされていますが、(私のインスタンスでは)pop(popl)コマンドで使用されていることを追加したいと思います。他の参照はありません。

4

3 に答える 3

7

いいえ、ゼロ化されていません。上書きリスト(より一般的にはクローバーリストと呼ばれます)の目的は、asm命令の結果として、クローバーリストにリストされているレジスタが変更されることをGCCに通知することです。したがって、コンパイラは次のいずれかを保持する必要があります。現在ライブです。

たとえば、x86では、命令は、の入力値に基づいて、4cpuidつの固定レジスタ、、、およびを使用して4つの部分で情報を返します。との結果のみに関心がある場合は、(素朴に)次のように記述します。%eax%ebx%ecx%edx%eax%eax%ebx

int input_res1 = 0; // also used for first part of result 
int res2;
__asm__("cpuid" : "+a"(input_res1), "=b"(res2) ); 

これにより、結果の最初と2番目の部分がC変数input_res1res2;になります。ただし GCCが他のデータを使用%ecxして保持していた場合。それらは、 gccが知らないうちに命令%edxによって上書きされます。これを防ぐために; clobberリストを使用します:cpuid

int input_res1 = 0; // also used for first part of result 
int res2;
__asm__("cpuid" : "+a"(input_res1), "=b"(res2)
                : : "%ecx", "%edx" );

GCCに、このasm呼び出しによって上書きされることを伝えたよう%ecx%edx、またはを使用しないか、関数の前にスタックに値を保存して後で復元することにより、状況を正しく処理%ecxでき%edxますasm

アップデート:

2番目の質問(命令のクローバーリストにレジスターがリストされているのはなぜですか)に関して-あなたが次のように見えるとpopl仮定します:asm

__asm__("popl %eax" : : : "%eax" );

次に、ここのコードはスタックからアイテムをポップしますが、実際の値は関係ありません。おそらく、スタックのバランスを維持しているだけであるか、このコードパスに値は必要ありません。これとは対照的に、このように書くことによって:

int trash // don't ever use this.
__asm__("popl %0" : "=r"(trash));

不要な値を保持するために一時変数を明示的に作成する必要はありません。確かに、この場合、2つの間に大きな違いはありませんが、クローバーを備えたバージョンでは、スタックからの値を気にしないことが明確になっています。

于 2009-06-30T22:26:12.360 に答える
5

「ゼロ化」とは、「他の関数が何をしていたかわからないように、レジスターの値が0に置き換えられる」ことを意味する場合、いいえ、レジスターは使用前にゼロ化されません。ただし、GCCに情報を保存するように指示しているので、現在そこにある情報を読みたいということではなく、問題ではありません。

この情報をGCCに提供して、(ドキュメントを読んで)アセンブリコードを使い終わったときに「使用するデータが含まれるレジスタまたはメモリの場所を推測する必要がない」ようにします(たとえば、データがスタックレジスタまたは他のレジスタにあるかどうかを覚えておいてください)。

「コンパイラは...アセンブラ命令テンプレートを解析せず、それが何を意味するのか、あるいはそれが有効なアセンブラ入力であるかどうかさえも知らないため、GCCはアセンブリコードに多くの助けを必要とします。拡張asm機能はマシン命令に最もよく使用されますコンパイラ自体は存在を知りません。」

アップデート

GCCはマルチパスコンパイラとして設計されています。パスの多くは、実際にはまったく異なるプログラムです。「コンパイラ」を形成する一連のプログラムは、ソースをC、C ++、Ada、Javaなどからアセンブリコードに変換します。次に、別のプログラム(gasGNUアセンブラの場合)がそのアセンブリコードを取得してバイナリに変換します(その後ldcollect2バイナリに対してさらに多くのことを実行します)。にテキストを直接渡すためのアセンブリブロックが存在しgas、C、C ++、Ada、Javaなどの側との間で情報を渡すために必要なセットアップをコンパイラが実行できるようにclobber-list(および入力リスト)が存在します。 thegas物事の側面、および現在レジスタにある重要な情報を、アセンブリブロックの実行前にメモリにコピーする(および後でメモリからコピーする)ことにより、アセンブリブロックから保護できることを保証します。

別の方法は、すべてのアセンブリコードブロックのすべてのレジスタを保存して復元することです。高価になる可能性のある多数のレジスタを備えたRISCマシン(たとえば、Itaniumには128個の汎用レジスタ、別の128個の浮動小数点レジスタおよび64個の1ビットレジスタがあります)。

アセンブリコードを書いてからしばらく経ちました。また、特定のレジスタを使用するよりも、GCCの名前付きレジスタ機能を使用した経験が豊富です。だから、例を見て:

#include <stdio.h>

long foo(long l)
{
    long result;
    asm (
        "movl %[l], %[reg];"
        "incl %[reg];"
        : [reg] "=r" (result)
        : [l] "r" (l)
    );
    return result;
}

int main(int argc, char** argv)
{
    printf("%ld\n", foo(5L));
}

regアセンブリコード内で呼び出す出力レジスタを要求しました。GCCはresult完了時に変数に自動的にコピーします。この変数にCコードとアセンブリコードで異なる名前を付ける必要はありません。私はそれが可能であることを示すためだけにそれをしました。%%eaxGCCが使用することを決定した物理レジスタ( %%ebx、、、%%ecxなど)は、アセンブリブロックに入るときに、GCCがそのレジスタからメモリに重要なデータをコピーして、最後までそのレジスタを完全に使用できるようにします。アセンブリブロックの。

lまた、Cとアセンブリの両方で呼び出す入力レジスタを要求しました。GCCは、私が与えることを決定した物理レジスターがl、アセンブリー・ブロックに入るときに現在C変数にある値を持つことを約束します。GCCは、アセンブリブロックに入る前に、そのレジスタにたまたま存在するデータを保護するために必要な記録管理も行います。

アセンブリコードに行を追加するとどうなりますか?言う:

"addl %[reg], %%ecx;"

GCCのコンパイラ部分はアセンブリコードをチェックしないため、のデータは保護されません%%ecx。運%%ecxが良ければ、GCCが%[reg]またはに使用することを決定したレジスタの1つである可能性があります%[l]。運が悪ければ、プログラムの他の部分で「不思議なことに」値を変更してしまいます。

于 2009-06-30T19:08:51.573 に答える
4

上書きリストは、ASM呼び出し全体でこれらのレジスタに値を格納しないようにGCCにヒントを与えるためだけのものだと思います。GCCは提供しているASMを分析せず、特定の命令には、コードで明示的に指定されていない他のレジスタに影響を与える副作用があるため、これはGCCにそのことを伝える方法です。

于 2009-06-30T02:20:33.253 に答える