2014-04-17 11 views
12

Ich würde gerne wissen, ob ich strenge Aliasing-Regeln mit diesem Snippet zu brechen. (Ich glaube, so da es dereferencing einen punned-Zeiger, aber es ist in einem einzigen Ausdruck gemacht wird und/Wand weint nicht.)Bricht ich strenge Aliasing-Regeln?

inline double plop() const // member function 
{ 
    __m128d x = _mm_load_pd(v); 
    ... // some stuff 
    return *(reinterpret_cast<double*>(&x)); // return the lower double in xmm reg referred to by x. 
} 

Wenn ja, was ist die Abhilfe? Die gleichzeitige Verwendung verschiedener Repräsentationen wird immer schwieriger, wenn Sie die Spezifikation respektieren wollen.

Danke für deine Antworten, ich verliere meine gute Laune und versuche eine Lösung zu finden.

Antworten, die nicht akzeptiert und warum werden:

„Verwendung mm_store“ -> Der Optimierer schlägt fehl, es zu entfernen, wenn die folgenden Anweisungen ein XMM-Register erfordern, so dass es eine Last nur, nachdem sie erzeugt. Speichern + Laden für nichts.

"Verwenden Sie eine Union" -> Aliasing Regelverletzung, wenn Sie die beiden Arten für das gleiche Objekt verwenden. Wenn ich den Artikel von Thiago Macieira gut verstanden habe.

+0

Was ist mit einem einfachen alten 'memcpy' zu einem' double'? – Praetorian

+1

Es ist fast unmöglich, Aliasing im Umgang mit SIMD zu vermeiden. Im Idealfall vermeiden Sie den Zugriff auf einzelne Elemente, wie Sie es gerade sind, aber wenn Sie unbedingt benötigen, empfehle ich eine Vereinigung für Dinge auf dem Stapel und einen Zeiger für Zeiger aus Parametern. Unionstyp-Punning ist in C99 explizit erlaubt, und alle Mainstream-Compiler werden es auch nach C++ übertragen. Der Versuch, im Umgang mit einer nicht standardmäßigen Erweiterung vollständig normkonform zu sein, ist in gewisser Weise in sich widersprüchlich. – Mysticial

+0

@Praetorian: verwendet nicht simd intrinsics und memcpy ein bisschen paradoxal nennen? ^^ – ThiSpawn

Antwort

2

Es gibt nur eine intrinsische, dass „Extrakte“ die niederwertigen Doppel Wert von XMM-Register:

double _mm_cvtsd_f64 (__m128d a) 

Sie es auf diese Weise nutzen könnten:

return _mm_cvtsd_f64(x); 

Es ist ein Widerspruch zwischen den verschiedenen Referenzen . MSDN sagt: This intrinsic does not map to any specific machine instruction. Während Intel intrinsische Anleitung erwähnt movsd Anweisung. Im letzteren Fall wird diese zusätzliche Anweisung vom Optimierer leicht eliminiert. Mindestens gcc 4.8.1 mit -O2 Flag generiert Code ohne zusätzliche Anweisung.

+0

Intel sagt, dass es auch nicht abbildet :) Dies ist die Art, auf Compiler Impl zu verlassen, um Alias-Regeln zu respektieren. – ThiSpawn

4

Der Aufzählungspunkt in Fettdruck sollte ich denke, lassen Sie Ihre Besetzung hier, wie wir __m128d als ein Aggregat von vier double Union zum vollen Register betrachten können. In Bezug auf striktes Aliasing war der Compiler immer sehr um die Vereinigung herum versöhnt, wo am Ursprung nur eine Umwandlung in (char *) als gültig angenommen wurde.

§3.10: Wenn ein Programm versucht, den gespeicherten Wert eines Objekts durch eine glvalue von anderen als einer der folgenden Typen zuzugreifen das Verhalten undefined (Die Absicht dieser Liste ist es, diese Umstände zu spezifizieren in dem ein Objekt aliased werden kann oder auch nicht):

  • die dynamische Art des Objekts,
  • eine cv-qualifizierte Version des dynamischen Typs des Objekts,
  • eine Art ähnlich (wie (definiert in 4.4) zum dynamischen Typ des Objekts,
  • ein Typ, der dem dynamischen Typ des Objekts entspricht,
  • ein Typ, bei dem es sich um einen Typ mit oder ohne Vorzeichen handelt. qualifizierte Version des dynamischen Objekttyps,
  • ein Aggregat- oder Union-Typ, der eine der oben genannten Typen unter seinen Elementen oder nichtstatische Datenelemente enthält (einschließlich rekursiv ein Element oder nicht statisches Datenelement eines Unteraggregats) oder enthalten Union),
  • ein Typ, der eine (möglicherweise cv-qualifizierte) Basisklasse Typ von ist der dynamische Typ des Objekts,
  • ein char oder unsigned Char-Typ.
+0

Wirklich nette Antwort Ich habe nicht berücksichtigt, dass kein Compiler diesen Typ tatsächlich als Schlüsselwort verwendet, sondern dass ein spezifischer Typ entweder ein explizit kompatibler Typ typedef oder eine Union mit einer kompatiblen Repräsentation ist ... lass mich einige Überprüfungen durchführen und Ich werde deine Antwort mit meiner ganzen Dankbarkeit annehmen. – ThiSpawn

+0

Dies ist die zweitbeste Lösung, aber es erfordert das Umwickeln jedes SIMD-Typs, da einige Compiler seltsame Sonderdarstellungen haben, die das Double-Array nicht enthalten. – ThiSpawn

1

Ja, Ich denke, diese strenge Aliasing bricht. In der Praxis ist dies jedoch in der Regel gut.
(Ich schreibe dies vor allem als Antwort, weil es schwierig ist, gut in einem Kommentar zu beschreiben)

Aber Sie stattdessen so etwas tun könnte:

inline double plop() const // member function 
{ 
    __m128d x = _mm_load_pd(v); 
    ... // some stuff 

    union { 
     unsigned long long i; // 64-bit int 
     double    d; // 64-bit double 
    }; 

    i = _mm_cvtsi128_si64(_mm_castpd_si128(x)); // _mm_castpd_si128 to interpret the register as an int vector, _mm_cvtsi128_si64 to extract the lowest 64-bits 

    return d; // use the union to return the value as a double without breaking strict aliasing 
} 
+0

Gemäß dem Standard wird nach dem Zuweisen eines Mitglieds einer Union der Wert aller anderen Elemente unspezifiziert. Verbindungen können nicht portabel verwendet werden, um Bitmuster neu zu interpretieren. – Sneftel

+0

@Sneftel: Dies ist [Implementierung definiert] (http://gcc.gnu.org/onlinedocs/gcc/C-Implementation.html) Verhalten, die Implementierung ist erforderlich, um das Verhalten zu definieren. Im Falle von GCC (und jedem Compiler, den ich je benutzt habe), bricht es kein striktes Aliasing. Sie können lesen, wie das Verhalten [hier] definiert ist (http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2dpunning). – Apriori

+0

Der Standard beschreibt es als "unspezifiziert", nicht "implementierungsdefiniert". Das heißt, Implementierungen sind frei, es nicht zu definieren. – Sneftel

1

Was return x.m128d_f64[0];?

+1

Dies setzt eine Implementierung voraus, in der '__m128d' Mitglieder hat, auf die so zugegriffen werden kann. Nicht alle Implementierungen. Zum Beispiel: http://clang.llvm.org/doxygen/emmintrin_8h_source.html, wobei '__m128d' definiert ist als' typedef double __m128d __attribut __ ((__ vector_size __ (16))); ' – bames53

+0

Ein compilerabhängiges Makro ist vielleicht eine Idee wenn Alle Compiler haben eine Möglichkeit, einen Zugriff auf Elemente auszudrücken, mit m128d_f64 für msvc, direkt mit dem [] -Operator für clang etc ..., was eine Idee sein könnte, um sicher zu gehen, dass der Optimierer-Teil des Compilers nicht verloren geht tun Optimierungen .. – ThiSpawn