Und noch ne Anfängerfrage
-
Hallo, ich versuche mich jetzt an meiner ersten Optimierung mit inline Assembler:
Der Compiler mag folgendes nicht, obwohl ich das so ähnlich schon oft gelesen habe:
http://www.devmaster.net/forums/showthread.php?t=10924__asm { movaps xmm3,m_a1 movaps xmm4,m_a2 movaps xmm5,m_a3 movaps xmm6,m_a4 movaps xmm7,m_a5 ... }
m_a1,m_a2... sind __declspec(align(16)) float[4]
Der Fehler, den er erzeugt, sieht so aus:error C2415: improper operand type
Wenn ich kurz vorher einen pointer auf den Array anfang setze:
float* pa1=&m_a1[0]; __asm { movaps xmm3,pa1
Ist es ok, aber wenn ich einen Pointer aus meiner Klasse (also member, nicht lokal) will er wieder nicht. Versteht das jemand?
Will er grundsätzlich keine Member-Variablen?
Vielen Dank
Sören
-
float* pa1=&m_a1[0]; __asm { movaps xmm3,pa1
Hier versuchst du, xmm3 mit dem Inhalt des Pointers + den Speicher dahinter zu füllen - das geht folglich schief. Im Allgemeinen spielt der deklarierte Typ einer Variablen nur insoweit eine Rolle, als er dazu dient, die Operandengröße zu bestimmen.
Im Prinzip sollte dein erster Versuch funktioniern, zeig doch noch mal die exakte Deklaration von m_a1 etc.Ist es ok, aber wenn ich einen Pointer aus meiner Klasse (also member, nicht lokal) will er wieder nicht. Versteht das jemand?
In C++ brauchst du ein Objekt, um auf ein (nicht-statischen) Member zuzugreifen, das ist in Assembler natürlich nicht anders. Tatsächlich werden die Memberobjekte hier nur als Offset bezogen auf die Basisadresse des Objekts begriffen. Die exakte Syntax zum Zugriff ist nicht ganz intuitiv (mit masm würde man das ganze per Struktureoverride/assume lösen, das geht beim inline-Assembler nicht). Ich habe bisher zwei Formen gefunden, die einerseits das gewünschte Resultat bewirken, als auch sowohl von Visual C++ als auch dem Intel-Compiler akzeptiert werden:
struct Foo { __declspec(align(16)) float f[4]; }; void foo(Foo* p) { __asm { mov ecx, p movaps xmm1, [ecx]Foo.f // oder: movaps xmm2, Foo::f[ecx] // das ist nichts anderes als movaps xmm3, [ecx+Offset] // nur akzeptiert der Assembler diese Form nicht für Member } }
hier lasse ich also ecx auf das Objekt zeigen. Die erste Syntaxvariante ist etwas näher dran am Standalone-Assembler.
-
HI!
Hier die exakte Deklaration unter protected in der Klassendeklaration:__declspec(align(16)) float m_a1[4],m_a2[4],m_a3[4],m_a4[4],m_a5[4];
Ich befinde mich doch auch in einer Memberfunktion, da kann ich nicht einfach auf Membervariablen zugreifen? Doof...
Ich hoffe, ich muss nicht wirklich den Umweg gehen, den du mir aufgezeigt hast. Oder wohl doch?
Bis dann
Sören
-
soerenP schrieb:
Ich hoffe, ich muss nicht wirklich den Umweg gehen, den du mir aufgezeigt hast.
Wieso sollte das ein Umweg sein. Auch in C++ greifst du selbst in Memberfunktionen nie direkt auf ein Member zu (was sollte das auch für eine Bedeutung haben). Zwar musst du dort nicht immer this-> mitschreiben, aber das ist nur eine Syntaxerleichterung, das (*this). wird implizit durch den Compiler hinzugefügt. Glücklicherweise kannst du auch in Assembler auf this zugreifen
struct Foo { __declspec(align(16)) float f[4]; void foo() { __asm { mov ecx, this movaps xmm1,[ecx]Foo.f } } };
Sehr häufig wird sich der Objektzeiger bereits in ecx befinden, so dass dieses mov unnötig wird, aber darauf verlassen sollte man sich nicht (und wenn es überflüssig ist, dürfte es keinen Einfluss auf die Ausführungsgeschwindigkeit haben).
-
Hi!
Funktioniert das nur mit structs?
Mit meiner Klasse will er das nicht machen:mov ecx,this movaps xmm3,[ecx]PMFdmOsString.m_a1
...erzeugt folgenden Fehler:
error C2411: 'm_a1' : illegal struct/union member in 'second operand'Das hier lässt sich compilieren, endet aber in einem Zugriff auf 0xffffffff und einer Acces Violation:
movaps xmm3,[ecx+m_a1]
Auch das hier lässt sich kompilieren und gibt eine Acces Violation:
movaps xmm3,PMFdmOsString::m_a1[ecx]
-
Zeig doch mal die Klassendefinition. Visual C++ erlaubt ein paar weitere Syntaxformen, die der Intel-Compiler nicht mag, einige davon machen auch nicht das richtige. Zudem würde ich von unqualifizierten Bezeichnern unbedingt abraten.
-
So sieht die Klasse aus:
Hab zuerst vermutet, es könnte daran gelegen haben, dass ich die m_as einer Eltern-Klasse deklariert habe, womit es unter bestimmten umständen ja auch Probleme mit Adressen geben kann (Funktion, die Elternklasse erwartet), aber auch in der ageleiteten Klasse gehts nicht. Sie sieht so aus:class PMFdmOsString:public PMFdmString { public: PMFdmOsString(PMStringDescriptor descriptor); ~PMFdmOsString(); float getOutput(); void playNote(float frequency, float velocity); void stop(); protected: int m_osFactor; PMAntiAliasFilter* m_aaFilter; PMAntiAliasFilter m_factor2Filter; PMAntiAliasFilter m_factor3Filter; PMAntiAliasFilter m_factor4Filter; PMAntiAliasFilter m_dummyFilter; //For SIMD: __declspec(align(16)) float m_a1[4],m_a2[4],m_a3[4],m_a4[4],m_a5[4]; };
Auch in den Elternklassen gibts keine besonderen Deklarationen, oder so. Sehen ziemlich ähnlich aus:
class PMFdmString : public PMString { public: PMFdmString(PMStringDescriptor descriptor); ~PMFdmString(); void playNote(float frequency, float velocity); float getOutput(); void stop(); protected: void excite(float velocity); int t,tp1,tm1,tm2; int m_forceBufferPointer; float* m_y[4]; float* m_forceBuffer[kForceBufferLength]; //The coefficients: float m_coeff_a1,m_coeff_a2,m_coeff_a3,m_coeff_a4,m_coeff_a5; float* pm_a1,pm_a2,pm_a3,pm_a4,pm_a5; int m_nElements; }; class PMString { public: PMString(); ~PMString(); virtual float getOutput()=0; virtual void stop()=0; virtual void playNote(float frequency,float velocity)=0; void setSamplingFrequency(float freq); virtual void setExciterPosition(float pos); void setSustainFactor(float factor); void setBodyInteractionFactor(float factor); void setTrebleLossFactor(float factor); void setEModulus(float eModulus); void setDensity(float density); float* getBodyInput(){return &m_bodyInput;} void setBodyOutput(float* outputPtr){m_bodyOutput=outputPtr;} void nextStep(); protected: virtual void excite(float velocity)=0; PMLoopFilter* m_loopFilter; float m_frequency; float m_samplingFrequency; //Parameters float m_exciterPosition; float m_pickupPosition; float m_sustainFactor; float m_bodyInteractionFactor; float m_trebleLossFactor; float m_denormalGuardCounter; PMStringDescriptor m_descriptor; bool m_disable; int m_exciterElement; //BodyInteraction: float m_bodyInput; float m_bodyInputBuffer; float* m_bodyOutput; };
Nix besonderes bei, oder?
Was mich stutzig macht ist, warum er auf 0xffffffff zugreifen möchte:
this = 0x0b8da008
m_a1 = 0x0b8da298
Doofe Noob-Frage zum Abschluss:
Wenn ich einen 64Bit-Prozessor habe, kann ich mir dadurch keine Probleme einfangen, oder? Will nur alles ausschließen...
-
soerenP schrieb:
mov ecx,this movaps xmm3,[ecx]PMFdmOsString.m_a1
...erzeugt folgenden Fehler:
error C2411: 'm_a1' : illegal struct/union member in 'second operand'Bei mir nicht. Zeig mal die Funktion, in der der Fehler auftritt.
soerenP schrieb:
Wenn ich einen 64Bit-Prozessor habe, kann ich mir dadurch keine Probleme einfangen, oder? Will nur alles ausschließen...
Visual C++ unterstützt sowieso keinen inline-Assembler für 64bit-Targets.
soerenP schrieb:
Was mich stutzig macht ist, warum er auf 0xffffffff zugreifen möchte:
this = 0x0b8da008
m_a1 = 0x0b8da298Offensichtlich ist das Objekt nicht auf 16Byte ausgerichtet, damit muss ein movaps fehlschlagen. Zwar hast du diese mit __declspec deklariert, und diese Deklaration wirkt sich auch auf das Alignment des gesamten Objektes aus. Es ändert aber nicht das Verhalten von Allokationsfunktionen - die Deklaration mir align(16) genügt daher nur für statische und automatische Variablen. Die Allokationsfunktionen der Standardbibliothek ebenso wie die C-Funktionen malloc,realloc etc. liefern Speicher, der hinreichend ausgerichtet für jeden Standardtypen ist. Die strengste Ausrichtung, die ein solcher Typ hat, ist aber nur 8 - das genügt nicht für sse. Du hast zwei Möglichkeiten: entweder verzichtest du auf Befehle, die ausgerichtete Operanden verlangen (movaps -> movups), das ist aber nicht unbedingt empfehlenswert. Oder du schreibst eine eigene Allokationsfunktion, global oder Klassenspezifisch. Die Überladung in der Klasse hat den Vorteil geringeren Speicherverbrauchs, da sie nur mit den Klassen arbeitet, die dies wirklich benötigen - andererseits ist eine globale Lösung einfacher zu benutzen (du brauchst die Funktion nicht nur für PMFdmOsString, sondern auch für jede andere Klasse, die PMFdmOsString als nicht-statischen Member hat). Das kann z.B. so durch Zurückführung auf die Standardversionen geschehen (wir reservieren ein bisschen mehr Speicher, und suchen uns die nächste hinreichend ausgerichtete Adresse dieses Speicherblocks, unmittelbar vor dieser Adresse speichere ich einen Zeiger auf den Anfang des Speicherblocks, den ich dann zum Löschen benötige):
class PMFdmOsString:public PMFdmString { public: static void* operator new(std::size_t size) { static const std::size_t alignment = 16; static const std::size_t offset = alignment + sizeof( char* ); char* p = new char[ size + offset ]; // das geht elegant bei klassenspezifischen Funktionen // bei globaler Ersetzung könnten wir nicht einfach die Version der Standardbibliothek aufrufen, // in dieser Hinsicht ist die Überladung in der Klasse eleganter std::size_t pp = ( reinterpret_cast< std::size_t >( p ) + offset ) & ~( alignment - 1 ); char** q = reinterpret_cast< char** >( pp ); // das ist offensichtlich nicht portabel, aber eine portable Lösung wird prinzipiell erst mit C++0x möglich sein q[ -1 ] = p; return q; } static void operator delete(void* p) { delete [] static_cast< char** >( p )[ -1 ]; } static void* operator new[](std::size_t size) { return operator new( size ); } static void operator delete[](void* p) { operator delete( p ); } // usw. };
Effizienter wäre nat. die Implementation eines eigenen Allokators, der mit einem eigenen bereits ausgerichteten Speicherpool arbeitet - dann hat man nicht diese zusätzlichen Kosten für jedes einzelne Objekt.
Nebenbei gesagt, sollte der Destruktor von PMString virtuell sein.
-
Ich hab noch ein bisschen gebastelt
#include <new> #include <boost/type_traits/alignment_of.hpp> namespace SSE { struct EmptyBase {}; struct StrangeClass : virtual EmptyBase { virtual ~StrangeClass(); }; struct Alignments { long long a; void* b; StrangeClass* c; StrangeClass d; long double e; void(StrangeClass::*f)(); char StrangeClass::*g; }; const std::size_t max_standard_alignment = boost::alignment_of< Alignments >::value; template<std::size_t alignment, bool = ( alignment > max_standard_alignment ) > struct aligned_new_delete { static void* malloc(std::size_t size) { char* unaligned_storage = new( std::nothrow ) char[ size + alignment ]; if ( !unaligned_storage ) return NULL; std::size_t unaligned_address = reinterpret_cast< std::size_t >( unaligned_storage ); std::size_t aligned_address = ( unaligned_address + alignment ) & ~( alignment - 1 ); char** aligned_storage = reinterpret_cast< char** >( aligned_address ); new( aligned_storage - 1 ) char*( unaligned_storage ); return aligned_storage; } static void free(void* aligned_block) { char** aligned_storage = static_cast< char** >( aligned_block ); delete [] aligned_storage[ -1 ]; } }; template<std::size_t alignment> struct aligned_new_delete< alignment, false > { static void* malloc(std::size_t size) { return new( std::nothrow ) char[ size ]; } static void free(void* p) { delete [] static_cast< char* >( p ); } }; template<typename Derived, typename Base = EmptyBase> struct AlignedAlloc : public Base { AlignedAlloc() : Base() {} template<typename T0> AlignedAlloc(const T0& arg0) : Base( arg0 ) {} template<typename T0, typename T1> AlignedAlloc(const T0& arg0, const T1& arg1) : Base( arg0, arg1 ) {} template<typename T0, typename T1, typename T2> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2) : Base( arg0, arg1, arg2 ) {} template<typename T0, typename T1, typename T2, typename T3> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3) : Base( arg0, arg1, arg2, arg3 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4) : Base( arg0, arg1, arg2, arg3, arg4 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5) : Base( arg0, arg1, arg2, arg3, arg4, arg5 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6) : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg) : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg7, const T8& arg8) : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 ) {} template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg7, const T8& arg8, const T9& arg9) : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 ) {} static void* operator new(std::size_t size, const std::nothrow_t&) { return aligned_new_delete< boost::alignment_of< Derived >::value >::malloc( size ); } static void* operator new[](std::size_t size, const std::nothrow_t& t) { return operator new( size, t ); } static void operator delete(void* p, const std::nothrow_t&) { aligned_new_delete< boost::alignment_of< Derived >::value >::free( p ); } static void operator delete[](void* p, const std::nothrow_t& t) { operator delete( p, t ); } static void* operator new(std::size_t size) { void* p = operator new( size, std::nothrow ); return p ? p : throw std::bad_alloc(); } static void* operator new[](std::size_t size) { return operator new( size ); } static void operator delete(void* p) { operator delete( p, std::nothrow ); } static void operator delete[](void* p) { operator delete( p ); } static void* operator new(std::size_t size, void* p) { return ::operator new( size, p ); } static void* operator new[](std::size_t size, void* p) { return ::operator new[]( size, p ); } static void operator delete(void* p, void* q) { ::operator delete( p, q ); } static void operator delete[](void* p, void* q) { ::operator delete[]( p, q ); } }; }
AlignedAlloc ist als Mixin konzipiert, dass die notwendigen Allokationsfunktion automatisch generiert:
struct Foo : SSE::AlignedAlloc< Foo > { }; struct Bar : SSE::AlignedAlloc< Bar, Foo > { __declspec(align(16)) float x[4]; };
Der erste Parameter ist die Klasse, für die wir die Funktion brauchen, die also von dem Mixin ableitet. Der zweite optionale Parameter gibt eine Klasse an, von der das Mixin ableitet. Damit kann das Mixin in eine bestehende Klassenhierarchie eingebunden werden, ohne dass man deswegen gleich Mehrfachvererbung nutzen muss (man kann das natürlich tun) - das kann effizienter sein. Als Mixin ist AlignedAlloc natürlich nicht als polymorphe Basisklasse gedacht. Das Ganze ist so konzipiert, dass kein Overhead entsteht, wenn man das Mixin auf Klassen anwendet, die eigentlich keine eigene Allokationsfunktion benötigen, man kann es also sehr liberal nutzen.
Zum Beispiel würden sich die notwendigen Änderungen im ursprünglichen Code dann darauf beschränken, die Zeile
class PMFdmString : public PMString
durch
class PMFdmString : public SSE::AlignedAlloc< PMFdmString, PMString >
zu ersetzen.
-
Also, ich hab zwar nicht wirklich verstanden, was genau in der new() funktion passiert, ich nehme aber an, sie liefert mir richtig ausgerichteten Speicher?
Wie sieht es eigentlich mit addps aus? In meiner Assembler Referenz steht nichts davon, dass, wenn der zweite Operand aus dem Speicher stammt, dieser auch aligned sein muss. Scheint aber so zu sein...
-
soerenP schrieb:
Also, ich hab zwar nicht wirklich verstanden, was genau in der new() funktion passiert, ich nehme aber an, sie liefert mir richtig ausgerichteten Speicher?
genau. so kannst du new/new[] ganz normal einsetzen.
soerenP schrieb:
Wie sieht es eigentlich mit addps aus? In meiner Assembler Referenz steht nichts davon, dass, wenn der zweite Operand aus dem Speicher stammt, dieser auch aligned sein muss. Scheint aber so zu sein...
Speicheroperanden müssen grundsätzlich ausgerichtet sein, es sei denn, der Befehl spezifiziert ausdrücklich etwas anderes, das ist an sich nur bei den entsprechenden movxyz-Befehlen der Fall.
-
Ich korrigiere mich (Das letzte Posting bezog sich auf das zwei vorher): Ich versteh nur noch Bahnhof. So etwas sag ich nicht gerne, aber es ist gerade angebracht. Ich glaube, die obige Version gefällt mir doch ganz gut...;)
Kannst du mir jetzt noch sagen, warum ich beim Rücksprung in eine übergeordnete (aufrufende) Funktion eine Acces-Violation bekomme. Im Call-Stack (ganz oben) steht nur 0a74fcfc()...
Ist irgendwas mit irgendwelchen Rücksprung-Adressen falsch?
Das kann doch eigentlich alles gar nicht so schwierig sein....Ein riesen Dank für deine Bemühungen!
Sören
-
naja, da ist auch jede Menge syntaktischer Lärm dabei, um für alle möglichen Eventualitäten vorzusorgen. Der wirklich interessante Teil ist der Gleiche wie zuvor.
Kannst du mir jetzt noch sagen, warum ich beim Rücksprung in eine übergeordnete (aufrufende) Funktion eine Acces-Violation bekomme. Im Call-Stack (ganz oben) steht nur 0a74fcfc()...
Ist irgendwas mit irgendwelchen Rücksprung-Adressen falsch?Code musst du schon zeigen.
-
Also, hier erstmal mein Assembler Code:
mov ecx,this movups xmm3,PMFdmOsString::m_a1[ecx]//Move coefficients to registers movups xmm4,PMFdmOsString::m_a2[ecx] movups xmm5,PMFdmOsString::m_a3[ecx] movups xmm6,PMFdmOsString::m_a4[ecx] movups xmm7,PMFdmOsString::m_a5[ecx] mov eax,8 //Prepare Loop mov ebx,16 //we are doing 4 steps at a time mov edx,end //eax is start and ecx is end of the loop schleife: movups xmm1,[pm_y_t+eax-8]//t[x-2]*a4 movups xmm2,[pm_y_t+eax+8]//t[x+2]*a4 addps xmm1,xmm2 mulps xmm1,xmm6//after having added, multiply with coefficient a4 movaps xmm0,xmm1//move to other register, where the multiplication results can be accumulated movups xmm1,[pm_y_t+eax-4]//t[x-1]*a3 movups xmm2,[pm_y_t+eax+4]//[t][x+1]*a3 addps xmm1,xmm2 mulps xmm1,xmm5//multiply with coefficient addps xmm0,xmm1//accumulate in xmm0 movups xmm1,[pm_y_t+eax]//[t][x]*a1 mulps xmm1,xmm3 addps xmm0,xmm1//accumulate in xmm0 movups xmm1,[pm_y_tm1+eax]//[t-2][x]*a2 mulps xmm1,xmm4 addps xmm0,xmm1//accumulate in xmm0 movups xmm1,[pm_y_tm2+eax]//[t-2][x]*a5 movups xmm2,[pm_y_tm1+eax-4] addps xmm1,xmm2 movups xmm2,[pm_y_tm1+eax+4] addps xmm1,xmm2 mulps xmm1,xmm7 addps xmm0,xmm1//accumulate in xmm0 movntps [pm_y_tp1+eax],xmm0 //write accumulation-result to memory add eax,ebx //increase loop counter by 16Bytes cmp eax,edx jb schleife ret
Dort, wo die Exception auftritt sieht die Dissemblierung so aus:
0A74FCFA je 0A74FD06 //Hier tritt der Fehler auf: 0A74FCFC les edx,fword ptr [ebx-6Bh] 0A74FCFF or eax,dword ptr [eax] 0A74FD01 add byte ptr [eax],al
und zwar ist ebx=0, was wahrscheinlich nicht so sein sollte.
Die Register zu sichern mit pusha, popa hat auch nichts gebracht.
Hat dir das was gebracht?
Ich weiß nicht, was sonst noch interessant sein könnte
Grüße
Sören
-
zeig immer den ganzen Funktionsrumpf. Ich würde aber darauf tippen, dass das ret dort nicht hingehört. Außer in naked-Funktionen solltest du Funktionen niemals direkt mit ret verlassen (und naked ist hier nicht angebracht).
-
nebenbei gesagt, gibt es eigentlich keinen Grund, die Koeffizienten m_a als sse-Vektor zu speichern, wenn sie sowieso in allen Komponenten gleich sind. Es dürfte sinnvoller sein, sie normal als float zu speichern und dann erst bei der Verarbeitung zu duplizieren:
movss xmm3,PMFdmOsString::m_a3[ecx] shufps xmm3,xmm3,0
-
Oha!
Ja, gut zu wissen, alles. Vielen Dank!
Was die koeffizienten angeht: Ich dachte, man könnte mit der Aktion vielleicht einen Schritt sparen. Mal schauen...
Sören
-
Und das nächste Problem:
Diesmal ist der this-Pointer nach der Aktion im Arsch. Der steht auf irgendeinem quatsch-Wert. Er sollte ungefähr auf:0x0b950063 stehen, steht aber auf 0xc79a39ab.
Gibt es irgendeine Stelle, wo der this Pointer von irgendwoher geladen wird und an dort nicht das richtige steht? ecx? In ecx steht übrigens noch die richtige Adresse, wie kann da der this Pointer kaputt gehen?
Grüße
Sören
-
soerenP schrieb:
Und das nächste Problem:
Diesmal ist der this-Pointer nach der Aktion im *****. Der steht auf irgendeinem quatsch-Wert. Er sollte ungefähr auf:0x0b950063 stehen, steht aber auf 0xc79a39ab.
Gibt es irgendeine Stelle, wo der this Pointer von irgendwoher geladen wird und an dort nicht das richtige steht? ecx? In ecx steht übrigens noch die richtige Adresse, wie kann da der this Pointer kaputt gehen?
Grüße
Sörenhöchstwahrscheinlich ist this irgendwo auf dem stack zu finden, jedenfalls im Assemblerteil. Aber ohne Code kann ich nur raten.
-
Den Code kennst du ja;)...
Direkt nach dem Rücksprung zu C++ ist der Pointer nicht mehr der alte und es gibt bei einem simplen t++ (member-Variable) natürlich eine Access-Violation, weil er logischerweise t dann auch nicht mehr kennt, wenn der this pointer falsch ist...//Hier steht der Code vom früheren Posting jb schleife }//asm t++;//hier tritt die Acces violation auf
Bis denn und schönen Abend
Sören