MIPSでのポインタベースの配列アクセスとはどういう意味ですか?
2 に答える
「ポインターベースの配列アクセス」には、追加の意味または意味があります。
固定アドレスの配列ではなく、配列へのポインタを持つことができます。実際、C/C++ では、「配列へのポインター」は通常、配列の最初の要素へのポインターにすぎません。基本的に、関数へのパラメーターである配列、または構造体またはクラスのメンバーである配列へのポインターがあります。
void Foo(char a[]);
/*or*/ void Foo(char *a);
struct Bar { int offset4bytes; char* a; };
通常、このような配列を使用する場合、配列のベース アドレスがレジスタにロードされます。
このような配列の要素 i にアクセスしたいとします。
char tmp = a[i];
r1 に配列アドレスが含まれているとします。(実際には、呼び出し規約で指定された、関数パラメーター用のおそらく別のレジスターです。コンパイラーが他のコードで使用できると判断したものは何でも。)
私がレジスタr2に住んでいるとしましょう。
そして、適切な測定のために、tmp を r3 とします。
Intel x86 などの一部の命令セットでは、次のようなアドレッシング モードがあります。
MOV r3, (r2,r1)4
つまり、アドレッシング モードは 2 つのレジスタと 1 つのオフセットを追加できます (これを示すために、構造体の例に任意にフィールドを追加しました)。
一体、レジスタの 1 つ、いわゆるインデックス レジスタをスケーリングすることさえできます。
MOV r3, (r2*2,r1)4
または私がそれを書くことを好むように
r3 := load( Memory[r2<<1+r1+4]
ただし、MIPS には、この種の base+_index*scale+offset アドレッシング モードはありません。ほとんどの MIPS メモリ アクセス命令は、レジスタ + オフセットに制限されています。したがって、MIPS の場合、次のことを行う必要がある場合があります。
ADDU r10, r1,r1 ; dest on left. r10 = 2*r1
ADDU r11, r2,r10
LB r3,(r11)4
つまり、x86 が複雑なアドレッシング モードで 1 つの CISC 命令で行うことを達成するために、追加の RISC 命令を追加する必要がある場合があります。ただし、このようなアドレス指定は一般的ではなく、避けられることがよくあります。
さらに、固定アドレスで配列をアドレス指定するだけでも、MIPS で追加の命令が必要になる場合があります。x86 命令は 32 ビットのメモリ オフセットを持つことができます。この場合、これは実際には配列の絶対アドレスである可能性があります。MIPS 命令は 16 ビット オフセットに制限されています。MIPS 命令は固定幅で、32 ビット幅です。したがって、固定アドレスの配列にアクセスする場合でも、通常はアドレスの上位ビットをレジスタにロードするために別の命令が必要になる場合があります。
その他 - MIPS には、reg+reg アドレッシング モードを持つ LUXC1 などの新しい命令があります。ただし、スケーリングされたインデックスではなく、3 番目のオフセット コンポーネントはありません。
このようにアドレッシング モードが制限されているため、単純なコンパイラが次のような lop に対して生成するコードは、
for(int i=0;i<N;i++) {
this->a[i] = 0;
}
ループに上記の複数の命令シーケンスが含まれていると、効率が悪くなります。
のようなループ
for(char *p=this->a;p<&(this->a[N]);p++) {
*p=0;
}
または同等に
for(char *p=this->a;p<this->a+N;p++) {
*p;
}
または時々
for(i=-N,*p=this->a;i<0;i++,p++) {
*p=0;
}
たとえば、最初の 2 つはストアを実行する命令が 1 つしかないためです。(最後は通常、複数の配列をトラバースする場合にのみ勝利します。
さて、簡単な例では、優れたコンパイラがこの最適化を行ってくれます。ただし、コンパイラがそのようなポインターベースのアクセスを好む場合があります。
値の配列の先頭を指すポインターがあります。配列をトラバースするには、ポインター演算 (通常は 1、2、または 4 の加算/減算) を使用して、ポインターを配列内の次/前の要素に進めます。