2010-10-10 5 views
15

Ich habe Schwierigkeiten zu verstehen, die Rollenbeschränkungen in Inline-GCC-Assembly (x86) zu spielen. Ich habe read the manual, was genau erklärt, was jede Einschränkung tut. Das Problem ist, dass, obwohl ich verstehe, was jede Einschränkung tut, ich sehr wenig Verständnis dafür habe, warum Sie eine Beschränkung gegenüber einer anderen verwenden würden, oder was die Implikationen sein könnten.GCC-Inline-Assembly: Einschränkungen

Ich weiß, dass dies ein sehr weites Thema ist, also sollte ein kleines Beispiel den Fokus eingrenzen. Das Folgende ist eine einfache ASM-Routine, die nur zwei Zahlen hinzufügt. Wenn ein Integer-Überlauf auftritt, schreibt er einen Wert von 1 in eine Ausgabe-C-Variable.

int32_t a = 10, b = 5; 
int32_t c = 0; // overflow flag 

__asm__ 
(
    "addl %2,%3;"  // Do a + b (the result goes into b) 
    "jno 0f;"   // Jump ahead if an overflow occurred 
    "movl $1, %1;"  // Copy 1 into c 
    "0:"     // We're done. 

    :"=r"(b), "=m"(c) // Output list 
    :"r"(a), "0"(b)  // Input list 
); 

Jetzt funktioniert dies gut, außer ich willkürlich mit den Zwängen hatte Geige, bis ich es bekam richtig zu arbeiten. Ursprünglich habe ich die folgenden Einschränkungen:

:"=r"(b), "=m"(c) // Output list 
    :"r"(a), "m"(b)  // Input list 

Beachten Sie, dass anstelle einer „0“ ist, ich ein „m“ Einschränkung für b verwenden. Dies hatte einen seltsamen Nebeneffekt, dass, wenn ich mit Optimierungsflags kompilierte und die Funktion zweimal aufruft, das Ergebnis der Additionsoperation aus irgendeinem Grund auch in c gespeichert würde. Ich lese schließlich über "", mit dem Sie angeben können, dass eine Variable als Eingabe- und Ausgabeoperand verwendet werden soll. Als ich "m"(b) zu "0"(b) änderte, funktionierte es.

Aber ich verstehe nicht wirklich, warum Sie eine Beschränkung gegenüber einer anderen verwenden würden. Ich meine ja, ich verstehe, dass "r" bedeutet, dass die Variable in einem Register sein sollte und "m" bedeutet, dass es im Speicher sein sollte - aber ich verstehe nicht, verstehen, was die Implikationen der Auswahl eines anderen sind, oder warum Der Additionsvorgang funktioniert nicht korrekt, wenn ich eine bestimmte Kombination von Einschränkungen auswähle.

Fragen: 1) Warum hat die "m" Einschränkung auf b Ursache in c in dem obigen Beispielcode geschrieben werden? 2) Gibt es ein Tutorial oder eine Online-Ressource, die mehr über Einschränkungen informiert?

Antwort

13

Hier ist ein Beispiel besser zu veranschaulichen, warum Sie sorgfältig Einschränkungen wählen sollten (gleiche Funktion wie bei Ihnen, aber vielleicht ein wenig kurz und bündig geschrieben):

bool add_and_check_overflow(int32_t& a, int32_t b) 
{ 
    bool result; 
    __asm__("addl %2, %1; seto %b0" 
      : "=q" (result), "+g" (a) 
      : "r" (b)); 
    return result; 
} 

So sind die verwendeten Einschränkungen waren: q, r und g.

  • q bedeutet nur eax, ecx, edx oder ebx ausgewählt werden könnten. Dies liegt daran, dass die Anweisungen set* in ein 8-Bit-adressierbares Register geschrieben werden müssen (al, ah, ...). Die Verwendung von b in der %b0 bedeutet, verwenden Sie die niedrigste 8-Bit-Teil (al, cl, ...).
  • Für die meisten Zwei-Operanden-Anweisungen muss mindestens einer der Operanden ein Register sein. Verwenden Sie also nicht m oder g für beide; Verwenden Sie r für mindestens einen der Operanden.
  • Für den letzten Operanden ist es egal, ob es sich um Register oder Speicher handelt, also verwenden Sie g (allgemein).

In dem obigen Beispiel, wählte ich g (statt r) für a zu verwenden, da Verweise in der Regel als Speicherzeiger implementiert sind, so eine r Einschränkung mit dem referenten in ein Register erforderlich wäre Kopieren zuerst, und dann zurückkopieren. Unter Verwendung von g könnte der Referent direkt aktualisiert werden.


Wie, warum die Originalversion Ihrer c mit dem Zusatz Wert überschrieben, das ist, weil Sie =m in dem Ausgabeschlitz angegeben, anstatt (sagen wir) +m; das bedeutet, dass der Compiler den gleichen Speicherort für die Eingabe und Ausgabe wiederverwenden darf.

In Ihrem Fall bedeutet, dass zwei Ergebnisse (da die gleiche Speicherstelle für b und c verwendet wurde):

  • Der Zusatz Überlauf nicht

    : dann, bekam c mit dem Wert von b überschrieben (das Ergebnis der Addition).
  • Der Zusatz ist übergelaufen: dann wurde c 1 (und b könnte auch 1 werden, je nachdem, wie der Code generiert wurde).
+0

Danke - das ist eine ausgezeichnete Antwort. Nur eine Klarstellung: Warum gibt der Constraint-Modifier '=' (Nur-Schreiben) dem Compiler das Recht, den gleichen Speicherort wiederzuverwenden, obwohl 'b' und' c' verschiedene Variablen mit unterschiedlichen Speicherorten sind? – Channel72

+0

@ Channel72: "obwohl' b' und 'c' sind verschiedene Variablen mit unterschiedlichen Speicherorten" --- das ist eigentlich eine wichtige Annahme, eine, die oft nicht zutrifft. Wenn "b" und "c" lokale Variablen sind, sind die Chancen gut, dass sie beide tatsächlich von Registern und nicht von einem Speicherort unterstützt werden. In diesem Fall ist der Speicherort einfach ein temporärer Halteplatz, der nur eingerichtet ist, um Ihre 'm'-Beschränkung zu berücksichtigen - in diesem Fall könnten' b 'und' c 'sehr gut dieselbe temporäre Position verwenden. –

+0

Nun, wenn 'b' und' c' eigentlich beide wirklich von Speicherplätzen unterstützt würden, dann hättest du recht darin, dass sie sich normalerweise überhaupt nicht überschneiden sollten. Und wenn eines vom Speicher und das andere vom Register unterstützt wird, dann ist eines dieser Szenarien möglich. –