2016-08-18 36 views
1

32ビットオペレーティングシステムで128ビットレジスタXMM0を使用して、charポインタ配列との間でメモリをロード/ストアしようとしています。私が試した何アドレス位置からのXMMレジスタのロード

は非常に簡単です:

int main() { 
    char *data = new char[33]; 
    for (int i = 0; i < 32; i++) 
     data[i] = 'a'; 
    data[32] = 0; 
    ASM 
    { 
     movdqu xmm0,[data] 
    } 

    delete[] data; 
} 

問題は、これが動作するようには思えないということです。初めて私は私が得たWin32アプリケーションをデバッグ:

XMM0 = 0024F8380000000000F818E30055F158

私が得た私はそれをデバッグ二回目:

XMM0 = 0043FD6800000000002C18E3008CF158

だから、ラインで何かがあるはずです:

movdqu xmm0,[data] 

私の代わりにこれを使用してみました:

movdqu xmm0,data 

が、私は同じ結果を得ました。

問題は、アドレスのデータの代わりにアドレスをコピーすることだと思いました。しかし、xmm0レジスタに表示されている値は32ビットのアドレスでは大きすぎますので、別のアドレスからメモリをコピーする必要があります。

私はインターネットで見つけたいくつかの他の指示も試しましたが、同じ結果がありました。

私はポインタを渡しているのでしょうか、xmmの基本について何か誤解していますか?

説明が有効な解決策が理解できます。

私は(最終的には3時間後)解決策を見つけたにもかかわらず、私はまだ説明のようになります

ASM 
    { 
     push eax 
     mov eax,data 
     movdqu xmm0,[eax] 
     pop eax 
    } 

なぜ私は32ビットのレジスタへのポインタを渡す必要がありますか?

+0

'data'はポインタです。 –

+0

'[data]'の元の投稿のように、新しい/ delete with pointerの代わりにローカル変数 'char data [33];を直接使うことができますか?今はデバッグできませんが、コンパイルされたソースを想像できるので、これがうまくいくと思います。現時点で困惑していることは、 'char * data'とのC++の違いは何ですか? C++の観点からは、それらは等価であるように見えます。私はおそらく何かを見落としているでしょう。 (そしてその2番目のバージョンでは 'mov eax、data'は' mov eax、[data] 'にコンパイルされます) – Ped7g

+4

x86には"メモリ間接 "アドレッシングモードはありません。ポインタを 'xmm0'にロードしています。 'xmm0'はポインタよりも大きいので、ポインタが格納されている場所の終わりを超えてメモリ内のガベージバイトも読み込んでいます。 –

答えて

1
#include <iostream> 

int main() 
{ 
    char *dataptr = new char[33]; 
    char datalocal[33]; 
    dataptr[0] = 'a'; dataptr[1] = 0; 
    datalocal[0] = 'a'; datalocal[1] = 0; 
    printf("%p %p %c\n", dataptr, &dataptr, dataptr[0]); 
    printf("%p %p %c\n", datalocal, &datalocal, datalocal[0]); 
    delete[] dataptr; 
} 

出力:

0xd38050 0x7635bd709448 a 
0x7635bd709450 0x7635bd709450 a 

我々が見ることができるように、動的ポインタdata本当にヒープ、0xD38050へのポインタを含むポインタ変数(32ビット又は0x7635BD709448で64ビット)です。

ローカル変数は、アドレス0x7635BD709450に割り当てられた33文字の長さのバッファです。

しかし、datalocalは、char *値としても機能します。

私はちょっと混乱していますが、これは正式なC++の説明です。C++コードを書いている間、これは非常に自然な感じで、dataptr [0]はヒープメモリの最初の要素です(つまり、dataptrを2回デリファレンスする)が、アセンブラではポインタ変数のアドレスであるdataptrの真の性質がわかります。したがって、ヒープポインタを最初にロードしてmov eax,[data] = eax0xD38050にロードしてから0xD38050の内容を[eax]を使用してXMM0にロードすることができます。

ローカル変数には変数のアドレスはありません。 datalocalのシンボルは既に最初の要素のアドレスなので、movdqu xmm0,[data]が動作します。

「間違っている」ケースではまだ実行できますmovdqu xmm0,[data]; CPUが32ビットの変数から128ビットをロードすることは問題ではありません。それは単純に32ビットを超えて読み取りを続け、他の変数/コードに属する別の96ビットを読み取るだけである。メモリ境界の周りにあり、これがアプリケーションの最後のメモリページである場合、無効なアクセスでクラッシュします。


コメントは数回コメントに記載されています。それは有効なポイントです。 movdquを介してメモリにアクセスするには、それを整列させる必要があります。 C++コンパイラ組み込み関数をチェックしてください。 Visual Studioのために、この動作するはずです:

__declspec(align(16)) char datalocal[33]; 
char *dataptr = _aligned_malloc(33, 16); 
_aligned_free(dataptr); 

私のC++の解釈について:たぶん私は初めからこの間違ってました。

dataptrは、dataptrシンボルの値、つまりそのヒープアドレスです。次に、dataptr[0]はヒープアドレスを逆参照し、割り当てられたメモリの最初の要素にアクセスします。 &dataptrは、dataptr値のアドレスです。これは、dataptr = nullptr;のような構文でも意味があります。ここでは、dataptrシンボルアドレスを上書きしないで、dataptr変数にnullptr値を格納します。それは配列変数だとしてdatalocal[]

は、基本的にdatalocal = 'a';でのような純粋なdatalocalにアクセスするには意味がないので、あなたは常に[]インデックスを提供する必要があります。そして&datalocalはそのような配列のアドレスです。純粋なdatalocalは、char *型を持つ配列などを使った簡単なポイント計算のためのエイリアス化されたショートカットですが、純粋なdatalocalが構文エラーを投げるならば、C++コード(ポインタの場合は&datalocal、要素の場合はdatalocal[..])、それは完全にそのdataptr論理に適合します。

結論:アセンブリ言語で[data]dataという値をロードしているので、最初から誤った例がありました。これはnewによって返されたヒープへのポインタです。

あなたのコードに問題がdataである。これは、私自身の説明で、現在はいくつかのC++の専門家は、ビューの正式な観点から作品にそれを来て、涙が... :)))

+0

ほとんどのコンテキスト(例:[関数の引数として渡す](http://stackoverflow.com/questions/38800044/what-kind-of-c11-data-type-is-an-array-according-to-the- amd64-abi#comment64984890_38800044)、 '+'や '[]'のような演算子で使用すると、配列はポインタのように動作します。しかし、アドレスはどこにも格納されません。それはもっと即座の定数のようです。または、スタックポインタからのコンパイル時定数オフセット。しかし、ポインタ変数*は実際にはポインタをメモリまたはレジスタに格納します。BTW、 '&datalocal'は警告を出しますが、'&datalocal [0] 'と同じコードにコンパイルします。 https://godbolt.org/g/05S5XS –

+0

私は 'movdqu'はアライメントのとれていないアクセスだと思いましたか?そうであれば、整列する必要はありません。それが整列することが分かっているなら、私は 'movdqa'を提案します –

3

はポインタです。アセンブリコードmovdqu xmm0,[data]は、アドレスdataの16バイトをレジスタxmm0にロードします。これは、ポインタの値を構成する4バイトまたは8バイトと、メモリ内の後続のバイトを意味します。ポインタアドレスがメモリ内で正しく整列されていることは幸いです。そうしないと、セグメント化エラーが発生します。この整列は保証されません。

ではなく、アライメントの問題(配列からデータをロードしますmovqdu)のアドレッシングの問題を解決する自動配列char data[33];を使用して代わりに、あなたはまだ、コンパイラが自動ストレージを持つ配列を整列する方法に応じて違反を得ることができます。再度、適切なアライメントを保証するものではありません。

あなたが見つけた解決策はおそらく良いアプローチですが、malloc()とは異なり、newで返されたポインタがすべての配置に対して有効かどうかはわかりません。

これはすべてのケースで動作するはずです:ピーター・コルドでコメントしたよう

#include <stdlib.h> 

int main(void) { 
    char *data = malloc(33); 
    for (int i = 0; i < 32; i++) { 
     data[i] = 'a'; 
    } 
    data[32] = 0; 
    __asm { 
     mov eax, data 
     movdqu xmm0, [eax] 
    } 
    free(data); 
    return 0; 
} 

、この種のもの、すなわちmm_loadu_si128のための組み込み関数を使用することがはるかに優れています。主な理由は2つあります。第1に、インラインアセンブリは64ビットビルドではサポートされていないため、組み込み関数を使用することにより、コードの移植性が少し向上します。第2に、コンパイラは、インラインアセンブリを最適化するという比較的貧弱な仕事を行い、特に、無意味なメモリストアと負荷をたくさんする傾向があります。コンパイラはイントリンシングの最適化をはるかに上手に行います。これにより、コードをより高速に実行できます(インラインアセンブリを使用する上でのポイントです)。

+0

あきらめないのは残念ですが、15人の担当者はいません:X – user2377766

+0

@ user2377766:すぐに来るはずです;-) – chqrlie

+2

プッシュ/ inline-asmステートメントMSVCはあなたのasmを読み込み、使用しているレジスタを保存/復元します。もっと重要なのは、MSVCインラインASMをまったく使用しないことです。組み込み関数を使うとより良い結果が得られます。 –

関連する問題