Wie wird das Registerflagbei CMPSD gesetzt?



  • camper schrieb:

    Funktioniert denn so eine einfache Variante?

    Es funktioniert auch so eine einfache Variante - wobei mir folgende Zeilen schleierhaft sind:

    lea     esi, [eax+ecx-1]
        lea     edi, [edx+ecx-1]
    

    Im EAX bzw. EDX Register steht bereits die Adresse des Strings ... mit der Adresse des Pointers selbst kann ich nicht viel anfangen.

    Ansonsten ist es halt einfach schneller zunächst die DWORDs zu überprüfen (gerade bei größeren Werten) würde ich sagen.


  • Mod

    FrEEzE2046 schrieb:

    Ansonsten ist es halt einfach schneller zunächst die DWORDs zu überprüfen (gerade bei größeren Werten) würde ich sagen.

    Stimmt, allerdings muss der Wert von esi/edi beim Übergang von cmpsd auf cmpsb entsprechend angepasst werden. Da die Fehlerbeschreibung nicht ganz zutreffend ist, ist das bisher vermutlich niemandem aufgefallen:

    function MemCompare(const lho, rho; size: DWORD): Integer; assembler;
      asm
        { EAX -> lho              result = -1        lho < rho
          EDX -> rho              result = 0         lho == rho
          ECX -> size             result = 1         lho > rho    }
    
        push    edi
        push    esi
    
        lea     esi, [eax+ecx-1]
        lea     edi, [edx+ecx-1]
        std
    
        mov     eax, ecx
        shr     eax, 2
        and     ecx, 3
    
        repe cmpsb
        jne     @@Exit
    
        mov     ecx, eax
        lea     esi, [esi-3]
        lea     edi, [edi-3]
    
        repe cmpsd
    
    @@Exit:
        cld
        seta    eax
        cmovb   eax, -1
    
        pop     esi
        pop     edi
      end;
    

    Da es wahrscheinlicher sein dürfte, dass der Stringanfang dword ausgerichtet ist, als dass das beim Stringende der Fall ist, sollte die byte-weisen Vergleiche zuerst stattfinden, damit die Vergleichsadressen für den dword-Vergleich ausgerichtet sind.

    Edit: kleine Änderung, damit es auch mit size<4 klappt (Flags!)



  • camper schrieb:

    Da es wahrscheinlicher sein dürfte, dass der Stringanfang dword ausgerichtet ist, als dass das beim Stringende der Fall ist, sollte die byte-weisen Vergleiche zuerst stattfinden, damit die Vergleichsadressen für den dword-Vergleich ausgerichtet sind.

    Ich gebe dir recht im Bezug darauf, dass es wahrscheinlicher sein dürfte, dass der Anfang DWORD aligned ist, als das Ende - bei einem String, in diesem Fall jedoch nicht.

    Warum du jedoch vom edi und esi Register 3 abziehst verstehe ich nicht.
    Erst einmal sollte dir doch auffallen, dass ich von hinten Vergleiche und danach entscheide ob lho oder rho größer, kleiner, gleich ist.
    Wenn ich Strings vergleichen würde (also Zeichenketten), dann wäre es ziemlicher Schwachsinn am least significant byte fest zu machen welcher größer ist oder 😉

    Dieser byteweise Vergleich ist vorallem für ganzzahlige Typen gedacht gewesen (oder Vektoren davon). Da wir die Werte in little endian Reihenfolge vorfinden, ist das "hintere" dword eines (z.B.) long long nun mal das signifikantere für die Feststellung größer, kleiner, gleich.
    Daher ist es - aus meiner Sicht - auch richtiger "zuerst" die DWORDs zu vergleichen und dann die Bytes.

    Desweiteren sehe ich kein Problem bei der Verwendung meiens Codes. Nenn bitte ein Beispiel, bei dem der Vergleich "fehlschlagen" würde. Ich will damit nicht sagen, dass du unrecht hast, aber ich sehe es wirklich nicht.


  • Mod

    Betrache:
    lho = "000aaa"
    rho = "000aaa"
    size = 6
    In deinem Code: beim ersten Vergleich betrachtest du die letzten vier Zeichen:

    000aaa
    000aaa
      ^      esi/edi
    

    Am Ende des Vergleiches werden esi/edi erniedrigt um die Größe des Wergleichswortes, also 4:

    000aaa
      000aaa
    ^         esi/edi
    

    Der folgende byte-weise Vergleich vergleicht also gar nicht mehr Bytes, die zu den Strings gehören, denn es wurden 3 Byte übersprungen.

    Analog werden, wenn, wie in meinem Code, erst der byte-weise Vergleich erfolgt, ohne Anpassung 3 bytes des letzten dwords doppelt verglichen. Die würden dann am Ende fehlen.



  • BTW:

    cmovb   eax, -1
    

    cmovxx erlaubt keine immediate's - allgemein: CMOVcc r,r/m


  • Mod

    stimmt, dann eben

    sbb eax, eax
    


  • cld 
        seta    eax 
        cmovb   eax, -1
    

    - 'seta eax' gibt es ebenfalls nicht: seta al oder ah.
    - sbb eax,eax würde für den Fall !CARRY das gesetzte bit in al/ah löschen.
    =>

    mov eax,0
    	seta al
    	sbb eax,0
    


  • cmovb schrieb:

    cld 
        seta    eax 
        cmovb   eax, -1
    

    - 'seta eax' gibt es ebenfalls nicht: seta al oder ah.
    - sbb eax,eax würde für den Fall !CARRY das gesetzte bit in al/ah löschen.
    =>

    mov eax,0
    	seta al
    	sbb eax,0
    

    Warum diese Reihenfolge. Hier wird immer -1 rauskommen oder etwa nicht?

    Ich brauche jetzt auch noch eine Funktion zum String-Vergleich. Grundsätzlich kann ich da ja genau so verfahren (abgesehen davon, dass ich eine "logisches" big endian byte order habe.
    Was mir allerdings Probleme macht ist, wenn die Strings nicht alle Chars auf Upper/Lower Case haben, sondern Groß-/Kleinschreibung enthalten. Wie kann ich das am besten lösen?

    Ich will keine Reihenfolge dieser Art haben:
    ABC
    Bertha
    Omega
    alpha


  • Mod

    FrEEzE2046 schrieb:

    Warum diese Reihenfolge. Hier wird immer -1 rauskommen oder etwa nicht?

    Warum? Nochmal drüber nachdenken, wie jede Instruktion die Flags beeinflusst.

    Eigentlich hatte ich gehofft, eine Instruktion einsparen zu können - eine Instruktion pro mögliches Ergebnis ist nicht besonders schön. Geht wohl doch nicht. Das mov könnte man noch durch xor ersetzen

    push    edi
        push    esi
    
        lea     esi, [eax+ecx-1]
        lea     edi, [edx+ecx-1]
        std
    
        mov     edx, ecx
        shr     edx, 2
        and     ecx, 3
        xor     eax, eax
    
        repe cmpsb
        jne     @@Exit
    
        mov     ecx, edx
        lea     esi, [esi-3]
        lea     edi, [edi-3]
    
        repe cmpsd
    
    @@Exit:
        cld
        seta    al
        sbb     eax, 0
    
        pop     esi
        pop     edi
    


  • mov eax,0
    	seta al
    	sbb eax,0
    

    1. Zeile: mov eax, 0: Schreibet 0 in eax
    2. Zeile: seta (set byte on condition / if above) al: Wenn höher dann 1
    3. Zeile: sbb (subtraction with borrow), 0: Wenn Carry-Flag gesetzt um 1 weiter dekrementieren.

    mmmh...


  • Mod

    FrEEzE2046 schrieb:

    Was mir allerdings Probleme macht ist, wenn die Strings nicht alle Chars auf Upper/Lower Case haben, sondern Groß-/Kleinschreibung enthalten. Wie kann ich das am besten lösen?

    Es gibt mehrere Möglichkeiten, z.B. kann erst einmal ein
    repe cmps
    durchführen, und bei Ungleichheit zusätzlich prüfen, ob die Ungleichheit nur auf Unterschiede zwischen Groß und Klein zurückzuführen ist, und den Vergleich dann ggf. fortsetzen. Auch hier ist die Optimierung per cmpsd denkbar.



  • camper schrieb:

    durchführen, und bei Ungleichheit zusätzlich prüfen, ob die Ungleichheit nur auf Unterschiede zwischen Groß und Klein zurückzuführen ist, und den Vergleich dann ggf. fortsetzen. Auch hier ist die Optimierung per cmpsd denkbar.

    Danke für deine Antwort.

    Was mir aufgefallen ist:

    seta  al
    sbb   eax, 0
    

    liefert die Ergebnisse genau falsch rum^^. Daher meine bedenken.


  • Mod

    FrEEzE2046 schrieb:

    seta  al
    sbb   eax, 0
    

    liefert die Ergebnisse genau falsch rum^^.

    esi/edi wurden auch vertauscht.



  • Ich habe dazu noch mal eine generelle Frage:

    Wo ist eigentlich der Unterschied zwischen:

    lea edi, [eax+ecx-1]
    

    und

    mov edi, eax
    add edi, ecx
    dec edi
    

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    EDIT:
    Warum sind bei mir die Ergebniswerte vertauscht (1 u. -1)? Ich mach doch nicht viel anders als du:

    function __MemCompare__LE_DF__(const lho, rho; size: DWORD): Integer; assembler;
      asm
        { EAX -> lho          		result = 0		equal
          EDX -> rho              result = 1		lho < rho
          ECX -> size             result = -1		lho > rho	}
    
        push	edi
        push	esi
        push	ebx
    
        std								// set direction flag to decrement
        mov		edi, eax
        mov		esi, edx
    
        xor		eax, eax 		// null result
        mov		ebx, ecx		// save value
    
        shr		ecx, 2			// look for dword align
        jz		@@Rest
    
        mov		edx, ebx
        sub		edx, 4
        add		edi, edx    // position on last dword
        add		esi, edx
    
        repe	cmpsd
        jne		@@Exit
    
      @@Rest:
        mov		ecx, ebx
        and		ecx, 3
        jz		@@Exit
    
        add		edi, ecx   	// byte offset
        add		esi, ecx
    
        repe	cmpsb
    
      @@Exit:
      	cld
        seta  al
        sbb   eax, 0
    
        pop		ebx
        pop		esi
        pop		edi
      end; (* of MemCompare *)
    (* ------------------------------------------------------------------------------------ *)
    

  • Mod

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.



  • camper schrieb:

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?


  • Mod

    FrEEzE2046 schrieb:

    camper schrieb:

    FrEEzE2046 schrieb:

    Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?

    Mit lea ist es wahrscheinlich schneller, zudem werden keine Flags beeinflusst.

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?

    Weil due esi/edi andersherum initialisierst, nebenbei fehlt bei dir noch die Adresskorrektur vor @@Rest.



  • camper schrieb:

    Ich hab mal meinen Beitrag editiert. Weißt du woran es liegen kann?

    Weil due esi/edi andersherum initialisierst, nebenbei fehlt bei dir noch die Adresskorrektur vor @@Rest.[/quote]

    Du hast recht danke.

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    EDIT:

    Also, sieht jetzt so aus:

    push	edi
        push	esi
    
        std											// set direction flag to decrement
        lea		esi, [edx+ecx]
        lea		edi, [eax+ecx]
    
        xor		eax, eax 					// null result
        mov		edx, ecx					// save value
    
        shr		ecx, 2						// look for dword align
        jz		@@Rest
    
        lea		esi, [esi-4]			// position on last DWORD
        lea		edi, [edi-4]
    
        repe	cmpsd
        jne		@@Exit
    
      @@Rest:
        mov		ecx, edx
        and		ecx, 3						// remaining bytes
        jz		@@Exit
    
        lea		esi, [esi+ecx] 		// byte offset
        lea		edi, [edi+ecx]
    
        repe	cmpsb
    
      @@Exit:
      	cld
        seta  al
        sbb   eax, 0
    
        pop		esi
        pop		edi
    

    Noch 2 Sachen dazu:

    1. Lohnen sich die Vergleiche inkl. jump oder sollte ich lieber (so wie du) einfach cmpsb/d drüber laufen lassen, auch wenn in ecx 0 steht?

    2. Bei mir sind die Ergebniswerte immer noch vertauscht. Verstehe ich nicht wirklich.


  • Mod

    FrEEzE2046 schrieb:

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    Woher weisst du, dass in ecx genau siezof(dword)-sizeof(byte)=3 drin steht? Abgesehen davon passt das mit dem Fall size<=3 ohnehin nicht.



  • camper schrieb:

    FrEEzE2046 schrieb:

    Zur Adresskorrektur:

    add		edi, ecx   	// byte offset
        add		esi, ecx
    

    mache ich doch.

    Woher weisst du, dass in ecx genau siezof(dword)-sizeof(byte)=3 drin steht?

    Ich weiß, dass in ecx die restlichen Bits sind, welche nicht dword aligned sind und die addiere ich drauf. Ist doch korrekt würde ich sagen.


Anmelden zum Antworten