0

QNX-Momentics (eclipse、g++ 4.6.1 ツールチェーンに基づく) と Visual Studio 2010 の両方でコンパイル可能にする必要があるプロジェクトに取り組んでいます。一部のルーチンについては、組み込み関数でさえうまくいかないため、手動でアセンブリを実装することにしました。最適化されました。最初のコンパイラには、-masm=intel フラグを使用して "intelized" できる ATt&T 構文があり、2 番目のコンパイラは intel 方言のみです。

intel-flag を使用して、記法上の側面を上回ることができます。

#ifdef _WIN32
    #define _cmd(...) __VA_ARGS__
    __asm {
#else
    #define _cmd(...) #__VA_ARGS__
    asm volatile (
#endif
  // constants
  // set loop counter
  _cmd( xor       eax, eax; )
        :
        :
#ifdef _WIN32
   }
#else
   );
#endif

さて、問題の 1 つは、インライン AT&T を使用して名前でローカル変数または関数のパラメーターにアクセスできないことです。次のようなものを使用して、別のスレッドで得たヒント

register __m128i x asm("xmm6");

ローカル変数が機能せず、xmm0 に割り当てられました。組み込み関数によって定義されていないローカル変数またはパラメーターは、AT&T で未定義の参照になるため、次のようなベア スタック処理を使用することにしました。

_cmd( movupd   xmmword ptr [eax], xmm3; )

新しい問題が発生しました:

関数パラメーターとローカル変数の両方が、両方の方言でまったく異なる方法で処理されます。次の例を検討してください。

template<typename T>
void linearRegression2DAsm(unsigned int p_oNumPoints, T *p_pXcoords, T *p_pYcoords, 
double *oX, double *oY, double *oXY,
double p_oAvgX, double p_oAvgY)
{
unsigned int p_rLoopsize = p_oNumPoints - (p_oNumPoints % 2);
double oAvgX[2];

そして、上記の定義ブロックの後のこの単純な計算:

_cmd( xor       eax, eax; )
// p_pXccoords
_cmd( mov       ecx, dword ptr [ebp+12]; )
// p_pYcoords
_cmd( mov       edx, dword ptr [ebp+16]; )
// p_oAvgX
_cmd( movhpd    xmm6, qword ptr [ebp+20]; )
// p_oAvgY
_cmd( movhpd    xmm7, qword ptr [ebp+28]; )
_cmd( movlpd    xmm6, qword ptr [ebp+20]; )
_cmd( movlpd    xmm7, qword ptr [ebp+20]; )
_cmd( addpd     xmm7, xmm6; )
// result into oAvgX
_cmd( mov eax, [ebp-32]; )
_cmd( movupd   xmmword ptr [ebp-32], xmm7; )

結果は oAvgX にあるはずです。これは Intel では問題なく動作しますが、Intel フラグ付きの AT&T asm コンパイラを使用すると成功しません。第二に、追加の O2-Flag が他の変数を最適化する可能性があるため、スタックが異なるコンパイルで同じように構築されることが保証されないことが懸念されます。

とにかくインライン化が必要ですが、二重方言の問題に対処する方法がわかりません。

4

2 に答える 2

0

1つの方法は、変数を構造体にラップし、4、8、または16にクリーンアラインメントを強制するのに十分なダミー変数を使用することです。次に、メンバーの相対位置をoffsetof(struct x、member)と、できればこれらの数値で計算できます。互換性のある文字列としてコンパイル時に注入される可能性があります。

#define LOCAL(a) ((offsetof(struct mystruct,a)==0?"0":offsetof(a)==4?"4":"error"))

asm("   push ebp \n\t"
    "   mov ebp, %0 \n\t"
    "   mov %0, " LOCAL(a) "\n\t"   // this would convert to [ebp + 4]
    "   pop ebp   \n\t"
    :"=0" (&my_struct) :::);

VCバージョンを開始できます:

asm("   push ebp ");
asm("   lea ebp, struct.a ");

その後、同じ(醜い)構文と同じ数のローカル変数。

于 2012-11-12T15:19:56.097 に答える
0

GCC のインライン アセンブリでローカル変数に名前でアクセスできますが、VS2010 で行う方法とは異なる方法で行う必要があるだけです。インライン アセンブリの最後に、入力のリスト、出力のリスト、および「上書きされた」リストを提供する必要があります。ここで、入力のリストと出力のリストの両方にローカル変数を含めることができます。また、「破壊された」のリストが非常に重要であることに注意してください (コンパイラーは、すべてのメモリー内容などを含め、破壊されたものとしてリストされていないもの、または出力としてリストされていないものは変更されないと想定します)。

偶発的なもの (何がどのレジスターにあるのか、または何がどのメモリ位置やスタック位置にあるかなど) に依存することは重大なバグです。テストケース。唯一の正しい方法は、この目的のために提供されている機能 (たとえば、GCC のインライン アセンブリの入力/出力リスト) に依存することです。

インライン アセンブリの重要な部分の場合。インライン アセンブリはどの (C/C++) 標準にも含まれていません。複数のコンパイラで確実に動作させる唯一の正しい方法は、インラインアセンブリを複製することだと思います。

また、異なる OS には異なる慣習があることにも注意してください (たとえば、異なる ABI、異なるカーネル syscall など)。基本的に、(最悪の場合)次のようなことをする必要があるかもしれません:

#ifdef WIN32_VS2010
    /* Inline assembly to suit Visual Studio 2010 for Win32 here */
#elifdef WIN32_ICC
    /* Inline assembly to suit Intel's "ICC" compiler for Win32 here */
#elifdef LINUX_ICC
    /* Inline assembly to suit Intel's "ICC" compiler for Linux here */
#elifdef WIN32_GCC
    /* Inline assembly to suit GCC compiler for Win32 here */
#elifdef LINUX_GCC
    /* Inline assembly to suit GCC compiler for Linux here */
#else
    /* Generate error about unsupported target here */
#endif
于 2012-11-12T11:44:29.910 に答える