Zeiger basierend Array-Zugriff in MIPS
-
30-09-2019 - |
Frage
Was meinen wir durch den Zeiger basierten Array Zugriff in MIPS?
Lösung
Es gibt eine zusätzliche mögliche Bedeutung oder Auswirkung auf „Zeiger basierten Array Zugriff“:
Sie können einen Zeiger auf ein Array, anstatt ein Array mit einer festen Adresse. Eigentlich in C / C ++, eine „Zeiger auf ein Feld“ ist eigentlich in der Regel nur ein Zeiger auf das erste Element des Arrays. Im Wesentlichen hat man eine Anordnung, die ein Parameter für eine Funktion ist, oder ein Zeiger auf ein Feld, das ein Mitglied einer Struktur oder Klasse ist:
void Foo(char a[]);
/*or*/ void Foo(char *a);
struct Bar { int offset4bytes; char* a; };
Normalerweise, wenn Sie ein solches Array verwenden möchten, die Basisadresse des Arrays werden in ein Register geladen werden.
Jetzt sagen Sie zugreifen Element möchte ich eines solchen Arrays,
char tmp = a[i];
Lassen Sie uns sagen, dass r 1 enthält die Array-Adresse. (Eigentlich wahrscheinlich ein anderes Register, durch die Aufrufkonvention festgelegt, für die Funktion Paramater. Was auch immer die Compiler für anderen Code findet.)
Lassen Sie uns sagen, dass ich im Register r2 lebt.
Und für eine gute Maßnahme, lassen r3 tmp sein.
Bei einigen Befehlssätzen, zum Beispiel Intel x86, gibt es eine Adressierungsart das aussieht wie
MOV r3, (r2,r1)4
d. die Adressierungsart zwei Register hinzufügen und ein Offset (I willkürlich ein Feld auf die Struktur Beispiel hinzugefügt, so dass ich dies zeigen konnte).
Heck, sie können sogar eines des Registers skalieren, die so genannte Index-Register:
MOV r3, (r2*2,r1)4
oder, wie ich es vorziehen, es zu schreiben
r3 := load( Memory[r2<<1+r1+4]
MIPS jedoch nicht über diese Art von Basis + _index * scale + Modus versetzt Adressierung. Die meisten MIPS Speicherzugriffsbefehle sind begrenzt + Offset zu registrieren. Daher ist für MIPS, könnten Sie tun müssen,
ADDU r10, r1,r1 ; dest on left. r10 = 2*r1
ADDU r11, r2,r10
LB r3,(r11)4
d. Sie könnten zusätzliche RISC-Befehle hinzufügen müssen, um zu erreichen, was x86 in einem CISC-Befehl funktioniert mit einem komplizierten Adressierungsmodus. Allerdings ist eine solche Adressierung ist nicht üblich, und canm oft vermieden werden.
Ferner kann auch nur an einer festen Adresse ein Array Adressierung kann zusätzliche Anweisungen in MIPS erfordern. x86-Befehle können ein 32-Bit-Speicher-Offset haben, - die in diesem Fall tatsächlich eine absolute Adresse eines Arrays sein. MIPS Anweisungen sind auf eine 16-Bit-Offset - MIPS Befehle mit fester Breite, 32 Bits breit. Daher ist eine separate Anweisung erforderlich sein kann, auch eine Anordnung an einer festen Adresse zuzugreifen, die typischerweise die oberen Bits der Adresse in ein Register zu laden.
Und mehr - MIPS hat neue Befehle wie LUXC1, die haben reg + reg Adressierungsart. Aber nicht skalierten Index und keine dritte Offset-Komponente.
Aufgrund dieser eingeschränkten Adressierungsmodi, dass der Code ein naiver Compiler generiert für eine lop wie
for(int i=0;i<N;i++) {
this->a[i] = 0;
}
wäre ineffizient sein, wenn die Schleife die mehrere Befehlssequenz enthalten ist oben erwähnt.
Eine Schleife wie
for(char *p=this->a;p<&(this->a[N]);p++) {
*p=0;
}
oder äquivalent
for(char *p=this->a;p<this->a+N;p++) {
*p;
}
oder sogar manchmal
for(i=-N,*p=this->a;i<0;i++,p++) {
*p=0;
}
könnte effizienter sein, weil beispiel in den ersten beiden gäbe es nur eine einzige Anweisung, den Speicher zu tun. (Die letzte ist in der Regel nur ein Sieg, wenn mehrere Arrays durchlaufen.
Nun, in einem einfachen Beispiel jeden guter Compiler wird für Dich diese Optimierung tun. Aber manchmal ist die Compiler bevorzugt solche Zeiger basierte Zugriffe.
Andere Tipps
In Kürze erhalten Sie haben einen Zeiger, der auf den Anfang des Arrays von Werten. Um die Anordnung zu durchqueren, werden Sie Zeigerarithmetik verwendet werden (in der Regel Addier / Subtrahier- 1, 2, oder 4), um den Zeiger auf den nächsten / vorherigen Element im Array zu fördern.