2013-04-02 5 views
7

Wir betreiben ein wissenschaftliches Programm und möchten AVX Features implementieren. Das ganze Programm (geschrieben in Fortran + C) wird vektorisiert werden und im Moment versuche ich eine komplexe Zahlenmultiplikation innerhalb der GCC Inline Assembly zu implementieren.Assemblercode/AVX-Anweisungen zur Multiplikation komplexer Zahlen. (GCC Inline Assembly)

Der Assembler-Code dauert 4 komplexe Zahlen und führt zwei auf einmal komplex Multiplikationen:

v2complex cmult(v2complex *a, v2complex *b) { 
    v2complex ret; 
    asm (
     "vmovupd %2,%%ymm1;" 
     "vmovupd %2, %%ymm2;" 
     "vmovddup %%ymm2, %%ymm2;" 
     "vshufpd $15,%%ymm1,%%ymm1,%%ymm1;" 
     "vmulpd %1, %%ymm2, %%ymm2;" 
     "vmulpd %1, %%ymm1, %%ymm1;" 
     "vshufpd $5,%%ymm1,%%ymm1, %%ymm1;" 
     "vaddsubpd %%ymm1, %%ymm2,%%ymm1;" 
     "vmovupd %%ymm1, %0;" 
     : 
     "=m"(ret) 
     : 
     "m" (*a), 
     "m" (*b) 
     ); 
    return ret; 
} 

wobei a und b 256-Bit-Doppelpräzisions sind:

typedef union v2complex { 
    __m256d v; 
    complex c[2]; 
} v2complex; 

Das Problem ist das, dass die Code erzeugt meistens das korrekte Ergebnis, aber manchmal schlägt es fehl.

Ich bin sehr neu in der Montage, aber ich habe versucht, es selbst herauszufinden. Es scheint, dass das C-Programm (optimiertes -O3) mit den Registern ymm interagiert, die im Assemblercode verwendet werden. Zum Beispiel kann ich einen der Werte (z.B. a) vor dem Ausführen der Multiplikation drucken, und das Programm liefert niemals falsche Ergebnisse.

Meine Frage ist, wie man GCC sagt, nicht mit ymm zu interagieren. Ich schaffte es nicht, setzen Sie die ymm zu clobbered Registerliste.

Antwort

7

Wie Sie vermuten, ist das Problem, dass Sie GCC nicht gesagt haben, welche Register Sie sind, die Sie verarschen. Ich bin überrascht, wenn sie YMM-Register in der Clobber-Liste noch nicht unterstützen; Welche Version von GCC verwendest du?

In jedem Fall wird es mit ziemlicher Sicherheit die entsprechenden XMM Register in der clobber Liste stattdessen zu setzen ausreichen, um:

: "=m" (ret) : "m" (*a), "m" (*b) : "%xmm1", "%xmm2"); 

Einige andere Hinweise:

  • Sie laden beide Eingaben zweimal, was ineffizient ist. Es gibt keinen Grund, das zu tun.
  • Ich würde "r" (a), "r" (b) als Einschränkungen verwenden und meine Lasten wie vmovupd (%2), %%ymm1 schreiben. Wahrscheinlich kein Unterschied im generierten Code, aber es scheint idiomatisch korrekt zu sein.
  • Vergessen Sie nicht, einen vzeroupper folgenden AVX-Code zu setzen, bevor ein SSE-Code ausgeführt wird, um (große) Blockierungen zu vermeiden.
+0

Vielen Dank, dass das Problem gelöst =). Ich benutze gcc 4.7.2 und thx für Ihren Rat. –

+1

Verwenden Sie nicht '" r "(a)," r "(b)' mit 'vmovupd (% 2), %% Ymm1' etc, GCC wird davon ausgehen, dass * a und * b nicht zugegriffen werden (außer Sie füge einen "Memory" Clobber hinzu. –

3

Ich füge zwei Kommentare, die nicht direkt Ihre Frage zu beantworten:

  • empfehle ich Compiler-Spezifika statt direkte Montage verwendet wird. Auf diese Weise kümmert sich der Compiler um die Registerzuordnung und kann den Code besser optimieren (Inline-Methoden, Nachbestellungsanweisungen usw.)
  • Agner Fog hat eine C++ vector class library von optimierten vektorisierten Operationen, einschließlich Operationen mit komplexen Zahlen. Auch wenn Sie seine Bibliotheken möglicherweise nicht direkt in Ihrem C-Code verwenden können, könnte sein optimierter Code ein guter Ausgangspunkt sein. siehe src/special/complexvec.h in the zipped source code.
+0

Thx, ich werde es mir ansehen, obwohl ich es nicht benutzen kann. Eigentlich hatte ich beide Versionen kompiliert, den intrinsischen Code und die Assembly, und ich wollte wissen, warum die Assembly nicht gut lief, da beide auf den gleichen optimierten Assemblercode (objdump -S ..) kompiliert wurden. –

+0

Aber intrinsics haben auch Nachteile: Der Compiler kümmert sich um die Zuweisung von Registern und Anweisungen umordnen usw. Es ist nicht allgemein wahr, dass ein Compiler dies besser macht. –