Problem mit Inline Assembler + SSE
-
Hi,
ich habe 2 Funktionen, naja doch das will nicht, das Programm schmiert gnadenlos ab
__declspec(naked) inline void naked_cross (float* dst, float* src1, float* src2) { __asm { mov esi, src1 mov edi, src2 movups xmm0, [esi] ; << HIER GIBT ES DEN FEHLER! movups xmm1, [edi] movaps xmm2, xmm0 movaps xmm3, xmm1 shufps xmm0, xmm0, 0xC9 shufps xmm1, xmm1, 0xD2 mulps xmm0, xmm1 shufps xmm2, xmm2, 0xD2 shufps xmm3, xmm3, 0xC9 mulps xmm2, xmm3 subps xmm0, xmm2 mov esi, dst movups [esi], xmm0 } } inline const vector<float> vector<float>::crossProduct (const vector<T> &other) { vector<float> gaga; naked_cross((float*)&gaga, (float*)this, (float*)&other); return gaga; }
ich bekomme diesen Fehler: "Unhandled exception at 0x004010e6 in nova.exe: 0xC0000005: Access violation reading location 0x00000008."
Schonmal danke im voraus!
-
naked und inline schliessen sich gegenseitig aus, ausserdem darf man nicht auf lokale variablen oder argumente (über ihren namen) zugreifen solange ebp nicht initialisiert ist; für diesen zweck sind makros vorhanden, besser ist es aber, direkt über [esp+...] auf die argumente zuzugreifen; ausserdem dürfen nur eax, ecx und edx in der funktion verändert werden - folglich ist es besser, diese zum addressieren zu benutzen anstelle von esi und edi, sonst sind zusätzliche push/pops notwendig.
ausserdem ist ein vector kein array. dass so ein simpler cast überhaupt vernünftige resultate erzeugt, wäre zu prüfen.
-
wie würdest du es machen?
-
Hab zwar vom Assembler-teil nicht so viel Ahnung. Aber std::vector hat auf jeden Fall ein Speicherlayout wie ein gewöhnliches Array. Bekommen kann man die Startadresse mit &gaga[0].
MfG Jester
-
@Jester: danke, bedeutet das aber, dass &gaga dasselbe ist wie &gaga[0] ? - und ist gaga[0] überhaupt ein gültiger bezug, solange noch keine elemente eingefügt wurden?
@***: es gibt verschiedene möglichkeiten, das zu heilen. falls es sich hier generell nur um 3- oder 4-dimensionale vectoren handelt, wäre es günstiger, auf std:vector zu verzichten und statt dessen xmm intrinsics zu verwenden, das erlaubt dem compiler effizienter mit resourcen umzugehen. falls naked_cross nur in dieser einen funktion benötigt wird, sollte der assembler text direkt in die methode eingefügt werden, dann ist man den funktionsoverhead schon mal los (wenn man mittels inline assembler auf argumente zugreift, wird immer ein stackrahmen eingerichtet, daher nützt inline im zusammenhang mit dem inline assembler nicht viel - bei gcc ist das nat. ganz anders).
etwa so - ungetestet:
const vector<float> vector<float>::crossProduct(const vector<T> &other) { vector<float> gaga; float* this0 = (float*)&(*this)[0]; float* other0 = (float*)&other[0]; float* gaga0 = (float*)&gaga[0]; __asm { mov ecx, this0 movups xmm0, [ ecx ] mov edx, other0 movaps xmm2, xmm0 shufps xmm0, xmm0, 0xC9 // 11001001 movups xmm1, [ edx ] shufps xmm1, xmm1, 0xD2 // 11010010 mulps xmm0, xmm1 shufps xmm2, xmm2, 0xD2 // 11010010 mov eax, gaga0 shufps xmm1, xmm1, 0xD2 // 11010010 (11001001) mulps xmm2, xmm1 subps xmm0, xmm2 movups [ eax ], xmm0 } return gaga; }
effizienter sind hier intrinsics - allerdings ist dann das gesamte design zu überarbeiten, z.b. so:
#include <xmmintrin.h> inline __m128 crossProduct(const __m128 &left, const __m128 &right) { return _mm_sub_ps( _mm_mul_ps( _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 0, 2, 1 ) ), _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 1, 0, 2 ) ) ), _mm_mul_ps( _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 1, 0, 2 ) ), _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 0, 2, 1 ) ) ) ); }
-
Hi,
danke der 1. code bringt meinen PC zum absturz, der 2 ebenso
Was sind denn intrinsics? ist das ne neue form des assemblers?
-
normalerweise verwenden compiler nur die einfachsten befehle der cpu. bessere compiler dann schon mehr, aber SSE und solche spezialsachen dann doch nicht (ja, es gibt ausnahmen :-)). die intrinsics geben dem programmierer die möglichkeit, solche spezialbefehle der cpu doch direkt ohne inline asm zu verwenden.
björn
-
welche art fehler bekommst du bei der zweiten variante? und wie rufst du es auf?
der grosse vorteil von intrinsics besteht darin, dass der compiler ihre seiteneffekte kennt und auch die registerverwendung selbst bestimmen kann. bei echtem inline assembler ist dass nicht der fall, was verhindert, dass der compiler irgendwelche optimierungen über einen assemblerblock hinweg vornimmt (das geht schon damit los, dass keine variablen in registern gehalten werden können). gcc ist in dieser hinsicht wesentlich mächtiger durch constraints.
-
Hi,
ich habs mal getestet, funzt prima, weis nicht was *** damit für probleme hat?
wie benutzt man eigentlich diese Funktion??
#include <xmmintrin.h> inline __m128 crossProduct(const __m128 &left, const __m128 &right) { return _mm_sub_ps( _mm_mul_ps( _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 0, 2, 1 ) ), _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 1, 0, 2 ) ) ), _mm_mul_ps( _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 1, 0, 2 ) ), _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 0, 2, 1 ) ) ) ); }
Ich kann doch keinen vector in einen _m128 casten?
-
achja noch was: gibt es sonst noch irgendwo diese instricts funktionen? z.B. für matrizenmultiplikation oder vector*matrix?
-
Ich kann doch keinen vector in einen _m128 casten?
das würde höchstwahrscheinlich auch zu einer zugriffsverletzung führen
_m128 ist prinzipiell als ausgerichtetes float[4] array zu verstehen (zugriff über xyz.f32[...]) - für die exacte deklaration schau ins headerfile. aus performance gründen sollte man den direkten zugriff aber vermeiden (wenn er zeitgleich mit intrinsics erfolgen würde), da dieser nur über den speicher möglich ist, es gibt auch für diesen zweck geeignete intrinsics.achja noch was: gibt es sonst noch irgendwo diese instricts funktionen? z.B. für matrizenmultiplikation oder vector*matrix?
siehe MSDN - prinzipiell sind intrinsics nur wrapper für assemblerinstructionen, komplexere operationen musst du daraus selbst aufbauen; assemblerkenntnisse sind nützlich, da nicht alle kombinationen zu optimalen code führen - das ist aber nicht zwingend erforderlich. das gegenstück bei gcc sind die x86 built-in functions - da verweise ich einfach auch mal auf die entsprechende dokumentation
-
wie benutze ich denn die funktion wenn ich 2 Arrays á 4 floats habe?
-
die funktion geht davon aus, dass sich die x,y,z koordinaten jeweils in [0] [1] bzw [2] befinden, das ergebnis hat das selbe format, der 4te wert wird hier nicht benötigt - im ergebnis ist er immer 0 (wenn es nicht gerade zum überlauf kommt, bzw. er vorher schon NaN war).