web-dev-qa-db-de.com

Arbeitsweise von __asm__ __volatile__ (""::: "memory")

Was macht __asm__ __volatile__ () und welche Bedeutung hat "memory" für ARM Architektur?

42
vnr1992
asm volatile("" ::: "memory");

erstellt eine Speicherbarriere auf Compilerebene, die das Optimierungsprogramm dazu zwingt, die Speicherzugriffe über die Barriere hinweg nicht neu anzuordnen.

Wenn Sie beispielsweise in einer bestimmten Reihenfolge auf eine Adresse zugreifen müssen (wahrscheinlich, weil dieser Speicherbereich tatsächlich von einem anderen Gerät als von einem Speicher gesichert wird), müssen Sie dies dem Compiler mitteilen, da dies sonst möglicherweise nur Ihre Schritte optimiert Der Effizienz zuliebe.

Angenommen, Sie müssen in diesem Szenario einen Wert in der Adresse erhöhen, etwas lesen und einen anderen Wert in einer benachbarten Adresse erhöhen.

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        d[1] += 1;
        return r;
}

Das Problem ist, dass der Compiler (in diesem Fall gcc) Ihren Speicherzugriff neu ordnen kann, um eine bessere Leistung zu erzielen, wenn Sie danach fragen (-O). Wahrscheinlich führt dies zu einer Abfolge von Anweisungen wie der folgenden:

00000000 <c>:
   0:   4603        mov r3, r0
   2:   c805        ldmia   r0, {r0, r2}
   4:   3001        adds    r0, #1
   6:   3201        adds    r2, #1
   8:   6018        str r0, [r3, #0]
   a:   6808        ldr r0, [r1, #0]
   c:   605a        str r2, [r3, #4]
   e:   4770        bx  lr

Die obigen Werte für d[0] Und d[1] Werden gleichzeitig geladen. Nehmen wir an, dies ist etwas, das Sie vermeiden möchten. Dann müssen Sie den Compiler anweisen, die Speicherzugriffe nicht neu zu ordnen und asm volatile("" ::: "memory") zu verwenden.

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        asm volatile("" ::: "memory");
        d[1] += 1;
        return r;
}

So erhalten Sie Ihre Anweisungssequenz so, wie Sie es möchten:

00000000 <c>:
   0:   6802        ldr r2, [r0, #0]
   2:   4603        mov r3, r0
   4:   3201        adds    r2, #1
   6:   6002        str r2, [r0, #0]
   8:   6808        ldr r0, [r1, #0]
   a:   685a        ldr r2, [r3, #4]
   c:   3201        adds    r2, #1
   e:   605a        str r2, [r3, #4]
  10:   4770        bx  lr
  12:   bf00        nop

Es ist zu beachten, dass dies nur eine Speicherbarriere für die Kompilierungszeit darstellt, um zu vermeiden, dass der Compiler Speicherzugriffe neu anordnet, da keine zusätzlichen Anweisungen auf Hardware-Ebene zum Entleeren von Speichern oder zum Warten auf den Abschluss des Ladens oder Speicherns erforderlich sind. CPUs können Speicherzugriffe weiterhin neu anordnen, wenn sie über die Architekturfunktionen verfügen und die Speicheradressen vom Typ normal anstelle von strongly ordered Oder device ( ref ) sind.

65
auselen

Diese Sequenz ist eine Barriere für die Planung des Compilerspeicherzugriffs, wie in dem von Udo referenzierten Artikel erwähnt. Dieses ist GCC-spezifisch - andere Compiler haben andere Möglichkeiten, sie zu beschreiben, einige davon mit expliziteren (und weniger esoterischen) Anweisungen.

__asm__ ist eine gcc-Erweiterung, die die Eingabe von Anweisungen in Assemblersprache in Ihrem C-Code ermöglicht. Hier wird sie verwendet, um Nebeneffekte anzugeben, die den Compiler daran hindern, bestimmte Arten von Optimierungen durchzuführen (die in diesem Fall möglicherweise enden) falschen Code generieren).

__volatile__ ist erforderlich, um sicherzustellen, dass die asm - Anweisung selbst nicht mit anderen flüchtigen Zugriffen neu geordnet wird (eine Garantie in der Sprache C).

memory ist eine Anweisung an GCC, die besagt, dass die Inline-ASM-Sequenz Nebenwirkungen auf den globalen Speicher hat und daher nicht nur Auswirkungen auf lokale Variablen berücksichtigt werden müssen.

18
unixsmurf

Die Bedeutung wird hier erklärt:

http://en.wikipedia.org/wiki/Memory_ordering

Grundsätzlich bedeutet dies, dass der Assembly-Code dort ausgeführt wird, wo Sie ihn erwarten. Sie weist den Compiler an, die Anweisungen in der Umgebung nicht neu anzuordnen. Das ist es, was codiert wird, bevor dieses Stück Code ausgeführt wird, und was danach codiert wird, wird danach ausgeführt.

9
Udo Klein