MIPSのポインターベースの配列アクセス
-
30-09-2019 - |
質問
MIPSのポインターベースの配列アクセスとはどういう意味ですか?
解決
「ポインターベースの配列アクセス」には、追加の可能な意味や含意があります。
固定アドレスの配列ではなく、配列へのポインターがある場合があります。実際、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
IEアドレス指定モードは、2つのレジスタとオフセットを追加できます(これを表示できるように、structの例にフィールドを任意に追加しました)。
ヘック、彼らはレジスタの1つ、いわゆるインデックスレジスタを拡大することもできます。
MOV r3, (r2*2,r1)4
または私がそれを書きたいと思っているように
r3 := load( Memory[r2<<1+r1+4]
ただし、MIPには、この種のベース+_index*スケール+オフセットアドレス指定モードがありません。ほとんどのMIPSメモリアクセス手順は、レジスタ+オフセットに限定されています。したがって、MIPの場合、あなたはしなければならないかもしれません
ADDU r10, r1,r1 ; dest on left. r10 = 2*r1
ADDU r11, r2,r10
LB r3,(r11)4
つまり、複雑なアドレス指定モードで1つのCISC命令でX86が行うことを達成するために、追加のRISC命令を追加する必要がある場合があります。ただし、そのようなアドレス指定は一般的ではなく、しばしば避けられます。
さらに、固定アドレスで配列をアドレス指定するだけでも、MIPに追加の指示が必要になる場合があります。 X86命令は32ビットメモリオフセットを持つことができます - この場合、実際には配列の絶対アドレスである可能性があります。 MIPSの命令は16ビットオフセットに制限されています - MIPSの指示は固定幅、幅32ビットです。したがって、固定アドレスで配列にアクセスするために、通常はアドレスの上部ビットをレジスタにロードするために、別の命令が必要になる場合があります。
その他 - MIPSには、REG+REGアドレス指定モードを備えたLuxC1のような新しい手順があります。スケーリングされたインデックスではなく、3番目のオフセットコンポーネントはありません。
この制限されたアドレス指定モードのため、ナイーブコンパイラがLOPのような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、2、または4を追加/減算する)を使用して、配列の次の要素/前の要素へのポインターを進めます。