3

コンパイル時に引数の数が不明な関数を呼び出す必要があるプロジェクトでインラインアセンブリを大量に使用し、Linuxで動作させるために自分自身を管理している間(Windowsではその問題が発生したことを思い出せません)このような奇妙なことが起こります:

私が何かを持っているなら

for(int i = 1; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

できます。

私が持っている場合

for(int i = this->someVar; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

そして私は、someVarがセグメンテーション違反をスローする値1を保持していることを私の人生で保証します。

また、私が持っている場合

int x = 1;
for(int i = x; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

それは動作しますが

int x = this->someVar;
for(int i = x; i >= 0; i--)
    asm("push %0"::"m"(someArray[i]));

ではない。

また、不思議なことに、一部の関数では問題がない一方で、他の関数ではすべて同じオブジェクトで問題が発生していると言えます。

誰かが私にそこの問題を解決することができるいくつかの情報を指摘することができれば、私は感謝します。

私は実際にforループで引数をプッシュする必要があるので、それを回避することはオプションではないことに注意してください。

インラインアセンブリワード「volatile」も使用してみましたが、何も変わりませんでした。

4

5 に答える 5

4

何が問題なのか理解できませんが、次のようにクリアな asm コードを使用してコードを記述してみてください

asm{
   loop1:
     mov ax, this->var
     ...
     dec ax
     cmp ax, 0
     je exit
     jmp loop1
}

...

出口:

また、「var」値を静的にすることも役立つ場合があります。

于 2010-01-13T10:21:43.183 に答える
4

分解を調べます。最も可能性の高い原因は、および/または終了値を保持する変数が、ループiの各反復でスタック上の固定オフセットから再フェッチされていることです。間違った値がフェッチされます。forpush

さまざまな回避策 (ローカル変数の宣言など) を試すことができますが、残念ながら、この場合、C/C++ で正しい動作を保証するregister良い方法はありません。この問題を回避するには、oivoodoo が示唆するように、ループを自分で実装してください。

于 2010-01-13T10:25:36.623 に答える
3

ここに私の精神的なデバッグの努力があります:

iスタックに格納される可能性thisが最も高く、386 以降では、マシンコードはesp相対メモリ位置を直接参照できるため、コンパイラは次のような命令を生成する可能性があります。

mov eax,[esp+8]

の値をレジスタに取得しthisますeax。問題は、push操作がスタック ポインターを混乱させることです。そのため、これらのハード コーディングされたアクセスは、最初の反復後に (ますます) 間違ったメモリ位置にアクセスします。

ほとんどの場合、単純なループ形式this->someVarは、コンパイラによってより完全に最適化され、レジスタのみを使用し、esp相対アクセスを使用しないマシン コードになります。つまり、引き続き正常に動作します。

昔々、ローカル変数と引数へのすべてのメモリ アクセスはebp、インライン アセンブリ コードによって変更されないレジスタを介して行われていました。ebpの代わりにを強制的に使用するコンパイラ スイッチが見つかった場合esp、これで問題が解決する可能性があります。

警告:コンパイラは、スタックをいじることを想定していません。スタックの最上位がどこにあるかを常に認識していることをコンパイラは想定しています。本当に動的にスタックにプッシュしたい場合は、oivoodooが行ったように、ループ自体をアセンブリ言語で完全に記述することをお勧めします。

于 2010-01-13T10:26:35.793 に答える
1

まず、おそらく起こっていることは、Linux での gcc が、スタック フレーム ポインターを使用するのではなく、スタック ポインターを使用してローカル変数のインデックスを作成していることです。これは、gcc がフレーム ポインター (x86 での BP) を別の汎用レジスタとして使用し、フレームを設定する多くのコードを回避できるようにする最適化です。フレームは基本的に、ローカル関数に属する SP と BP の間の領域です。この関数に渡したサイズで alloca への呼び出しを含めると、コンパイラがこの最適化を行わないように強制されるため、すべてが改善されると確信しています。

そうは言っても、バグは実際にはあなたのコードにあります。自分が何をしているのかを本当に理解している場合を除き、インライン asm に入ったときとは異なるスタック ポインターでインライン asm を終了しないでください。コンパイラは、ほとんどの場合、スタック ポインタを排他的に所有していると考えています。彼らは、変数を保存した場所を見つけるためにそれを使用できるように、それが同じままであることに依存しています。また、フレーム ポインター (BP) にも近づかないでください。

それらをいじっても大丈夫な場合はまれであり、通常はコンテキスト切り替えコード (あるスレッドまたはプロセスから別のスレッドまたはプロセスへの変更) などの場合です。

于 2010-03-26T19:14:07.960 に答える
0

引数の数の制限がわかっている場合は、その数の引数を使用して単一の関数呼び出しで呼び出すだけで、実際の引数を最後まで揃えることができます。

警告: x86_64 abi はいくつかのパラメーターに register を使用しているため、これとコードが壊れます。

于 2010-01-13T10:40:23.447 に答える