IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

Réécriture en Pascal d'une fonction en Assembleur


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut Réécriture en Pascal d'une fonction en Assembleur
    Bonjour !

    Dans un projet Delphi (qui n'est pas de moi), j'ai une unité qui contient des fonctions en Assembleur. Je souhaiterais les réécrire en Pascal. Plus exactement, il y a deux fonctions. Les autres apparemment ne sont pas utilisées. J'ai déjà réécrit la première. Pour la deuxième, je ne suis pas sûr de savoir ce qu'elle fait exactement, et c'est sur ce point que j'aurais besoin de votre aide.

    Voici l'unité Pascal en question.

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
     
    unit UASMTools;
     
    interface
     
    //function FirstOne(aBoard: Int64): Integer;
    //function LastOne(aBoard: Int64): Integer;
    function BitCount(aBoard: Int64): Byte;// assembler;
    function BitScanForward(var X: Int64): Integer;
    //function BitScanReverse(var X: Int64): Integer;
    //function BitScan(var X, Y: Integer; Z: pointer): Integer;
     
    implementation
     
    //FirstOne scans low register first (forward)
    //LastOne scans high register first (reverse)
    //PopCount count the number of bits set in a Int64
     
    function BitCount(aBoard: Int64): Byte;
    var
      LDam: Int64;
    begin
      result := 0;
      LDam := aBoard;
      while LDam <> Int64(0) do
      begin
        if LDam and Int64(1) = Int64(1) then
          Inc(result);
        LDam := LDam shr 1;
      end;
    end;
     
    function BitScanForward(var X: Int64): Integer;
    begin
      result := 0;
    end;
     
    (*
    function FirstOne(aBoard: Int64): Integer;
    asm
          xor     eax, eax
          bsf     edx, dword ptr aBoard
          jnz     @@1
          mov     eax, 32
          bsf     edx, dword ptr aBoard + 4
     @@1: add     eax, edx
    end;
     
    function LastOne(aBoard: Int64): Integer;
    asm
          xor     eax, eax
          bsr     edx, dword ptr aBoard
          jnz     @@1
          mov     eax, 32
          bsr     edx, dword ptr aBoard + 4
     @@1: add     eax, edx
    end;
     
    function BitCount(aBoard: Int64): Byte; assembler;
    asm
          mov     ecx, dword ptr aBoard
          xor     eax, eax
          test    ecx, ecx
          jz      @@1
     @@0: lea     edx, [ecx-1]
          inc     eax
          and     ecx, edx
          jnz     @@0
     @@1: mov     ecx, dword ptr aBoard+4
          test    ecx, ecx
          jz      @@3
     @@2: lea     edx, [ecx-1]
          inc     eax
          and     ecx, edx
          jnz     @@2
     @@3:
    end;
     
    function BitScanReverse(var X: Int64): Integer;
    asm
      {store lowest 32 bit into ECX and the higher 32 bit into EDX}
      MOV   ECX,[EAX]
      MOV   EDX,[EAX+4]
      {test if lower 32 bit contain  a '1'}
      BSR   EAX,ECX
      JNZ   @READY
      {test if higher 32 bit contain  a '1'}
      BSR   EAX,EDX
      JNZ   @CALCINDEX
      {set error code}
      MOV   EAX, -1
      JMP   @READY
    @CALCINDEX:
      {add 32 to get the overall index}
      ADD   EAX, 32
    @READY:
      RET
    end;
     
    function BitScanForward(var X: Int64): Integer;
    asm
      {store lowest 32 bit into ECX and the higher 32 bit into EDX}
      MOV   ECX,[EAX]
      MOV   EDX,[EAX+4]
      {test if lower 32 bit contain  a '1'}
      BSF   EAX,ECX
      JNZ   @READY
      {test if higher 32 bit contain  a '1'}
      BSF   EAX,EDX
      JNZ   @CALCINDEX
      {set error code}
      MOV   EAX, -1
      JMP   @READY
    @CALCINDEX:
      {add 32 to get the overall index}
      ADD   EAX, 32
    @READY:
      RET
    end;
     
    function BitScan(var X, Y: Integer; Z: pointer): Integer;
    var
      dummy: Cardinal;
    asm
      PUSHA
      MOV     EAX,[z]
      MOV     Dummy, EAX  // voor het geval eax al een waarde heeft ontvangen
      MOV     EDX,[z+4]
      BSF     X,EAX
      JZ      @E          //Als niet gevonden
      ADD     X, 32     //Het is een int64
      BSR     Y,EDX     //dan moet het in edx gezocht worden
      JZ      @B          //Als niet gevonden
    @B:
      BSR     EDX,Dummy
      JZ      @SetNull    //Als niet gevonden
      ADD     Y, 32     //Gevonden in het tweede register.
      JMP     @READY
    @E:
      BSF     X,EDX     //Als niet gevonden in EAX dan misschien in EDX
      JZ      @SetNull    //Anders maar op nul!
      JMP     @READY
    @SetNull:
      MOV     X, $-1
      MOV     Y, $-1
    @READY:
      POPA
      RET
    end;
    *)
    end.

    C'est un programme d'échecs. Je voudrais me débarrasser de l'Assembleur parce que j'essaie de compiler le programme avec Free Pascal sous Linux et que je n'y arrive pas.

    Donc, à votre avis, que renvoie exactement la fonction BitScanForward ?
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  2. #2
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 930
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 930
    Points : 59 398
    Points
    59 398
    Billets dans le blog
    2
    Par défaut
    Bonjour Roland,

    Le principe des deux instructions BSF Destination,Source (Bit Scan Forward) et BSR Destination,Source (Bit Scan Reverse) est similaire : l'opérande Source est analysé de la droite vers la gauche (BSF) ou de la gauche vers la droite (BSR) jusqu'à ce que l'on rencontre un bit égal à 1. Le bit correspondant dans l'opérande Destination sera alors également mis à 1 et tous les autres équivaudront à 0. Si l'opérande Source est nul alors le flag ZF est mis à 1.

    Qu'est-ce qui coince dans le code Assembleur, dans la compilation avec Free Pascal ? La directive {$asmMode intel} est-elle bien présente ?
    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  3. #3
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Merci Alcatîz ! Avec la directive que tu indiques, ça compile.

    Je vais quand même essayer de réécrire la fonction, pour vérifier que j'ai compris ton explication.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  4. #4
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Avec la directive {$asmMode intel}, mon unité se compile et le programme fonctionne. Donc je pourrais éventuellement la laisser comme ça.

    La fonction BitCount réécrite paraît aussi fonctionner.

    J'ai essayé de réécrire aussi la fonction BitScanForward et apparemment je n'ai pas réussi car le programme ne fonctionne pas. Voici ce que j'avais essayé :

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function BitScanForward(var X: Int64): Integer;
    var
      LDam: Int64;
    begin
      Assert(X <> Int64(0));
      result := 0;
      LDam := X;
      while (LDam and Int64(1)) = Int64(0) do
      begin
        Inc(result);
        LDam := LDam shr 1;
      end;
      X := Int64(1) shl result;
    end;

    Avec ou sans la dernière ligne (sur laquelle j'ai un doute), le programme ne fonctionne pas. Il semble que j'aie mal compris ton explication.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  5. #5
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 930
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 930
    Points : 59 398
    Points
    59 398
    Billets dans le blog
    2
    Par défaut
    Je n'ai pas le temps de regarder ce soir (désolé) mais je vois que l'instruction BSF existe dans la RTL de Free Pascal : https://www.freepascal.org/docs-html.../bsfqword.html

    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  6. #6
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Merci pour l'information. J'ai fait un essai rapide qui n'a rien donné. Il faut que je prenne le temps d'y regarder de plus près.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  7. #7
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 930
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 930
    Points : 59 398
    Points
    59 398
    Billets dans le blog
    2
    Par défaut
    Ta fonction simule bien ce que fait l'instruction BSF mais le code assembleur de la fonction BitScanForward fait autre chose. Le résultat de la fonction est d'ailleurs un entier 32 bits et non 64 bits.

    1. BSF est appliqué sur les 32 bits de poids faible de X :
    2. si un 1 est trouvé
    3. alors la fonction renvoie l'opérande destination de BSF
    4. sinon BSF est appliqué sur les 32 bits de poids fort de X :
    5. si un 1 est trouvé
    6. alors la fonction renvoie l'opérande destination de BSF + 32
    7. sinon la fonction renvoie -1 (erreur)

    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  8. #8
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    @Alcatîz

    Merci pour ton aide.

    Voici une nouvelle tentative, toujours sans succès.

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    function BSF(var Destination: Longint; const Source: Longint): boolean;
    var
      aux: Longint;
      i: integer;
    begin
      result := Source <> 0;
      if result then
      begin
        aux := Source;
        i := 0;
        while (aux and 1) = 0 do
        begin
          Inc(i);
          aux := aux shr 1;
        end;
        Destination := 1 shl i;
      end;
    end;
     
    function BitScanForward(var X: Int64): Integer;
    var
      aux: longint;
    begin
      if BSF(aux, X and $FFFFFFFF) then
        result := aux
      else
        if BSF(aux, X shr 32) then
          result := aux + 32
        else
          result := -1;
    end;

    J'ai dû louper quelque chose.

    J'attache en pièce jointe le programme complet, au cas où toi-même ou quelqu'un d'autre voudrait l'essayer.

    Voici les différentes lignes de commandes que j'utilise.

    Code Batch : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    C:\FPC\3.0.4\bin\i386-win32\fpc.exe -B -Mdelphi -dDEBUG -dNOASM deepross.dpr
    if exist deepross.exe move deepross.exe deepross-fpc32-noasm.exe
    C:\fpcupdeluxe\fpc\bin\x86_64-win64\fpc.exe -B -Mdelphi -dDEBUG -dNOASM deepross.dpr
    if exist deepross.exe move deepross.exe deepross-fpc64-noasm.exe
    "C:\Program Files (x86)\Embarcadero\Studio\20.0\bin\DCC32.EXE" -CC -NSsystem -DDEBUG -dNOASM deepross.dpr
    if exist deepross.exe move deepross.exe deepross-dcc32-noasm.exe
    C:\FPC\3.0.4\bin\i386-win32\fpc.exe -B -Mdelphi -dDEBUG deepross.dpr
    if exist deepross.exe move deepross.exe deepross-fpc32-asm.exe
    C:\fpcupdeluxe\fpc\bin\x86_64-win64\fpc.exe -B -Mdelphi -dDEBUG deepross.dpr
    if exist deepross.exe move deepross.exe deepross-fpc64-asm.exe
    "C:\Program Files (x86)\Embarcadero\Studio\20.0\bin\DCC32.EXE" -CC -NSsystem -DDEBUG deepross.dpr
    if exist deepross.exe move deepross.exe deepross-dcc32-asm.exe

    Pour essayer le programme, je le lance depuis l'invite de commandes et je fais

    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ucinewgame
    position startpos
    go

    et quand ça fonctionne le programme répond au bout d'une demi-seconde "bestmove ...". Quand ça ne fonctionne pas il ne répond rien du tout.

    Pour le moment seules les versions Assembleur 32 bits fonctionnent.
    Fichiers attachés Fichiers attachés
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  9. #9
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 930
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 930
    Points : 59 398
    Points
    59 398
    Billets dans le blog
    2
    Par défaut
    Bonjour Roland,

    J'ai testé ta fonction BitScanForward (mais pas l'entièreté du projet) : elle renvoie un Integer alors qu'elle devrait renvoyer un LongInt. Avec cette modification, elle semble faire le job.

    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  10. #10
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    C'est curieux. Même en remplaçant Integer par Longint, ça ne fonctionne toujours pas chez moi. De toute façon, quand on compile en 32 bits, Integer et Longint c'est la même chose non ?

    Bon, il faut que je prenne le temps de regarder les choses plus en profondeur, pour trouver ce qui coince.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  11. #11
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Assembleur et Free Pascal sont dans un bateau
    Bonjour,

    Free Pascal se débrouille bien avec l'assembleur alors pourquoi l'abandonner ?

    J'utilise Lazarus sous Windows et compile en 64 bits. Je mets un "nostackframe" pour éviter que le compilateur passe les arguments par la pile ce qui crée des mouvements de données supplémentaires qui font perdre beaucoup de temps dans des instructions aussi courtes. Je les déclare inline même si aujourd'hui Free Pascal ne l'applique pas encore aux procédures assembleurs (mais je suis optimiste et laisse le inline )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    {$AsmMode intel}
    //...
    // N° du premier bit à 1 de Int64 (-1 si n = 0)______________________________________________TOOLS
    function bFirst1(n : Int64): Int64; assembler; nostackframe; inline;
    asm
       mov   rdx,   -1
       bsf   rax,   rcx                                 // rax = n° du 1er bit à 1
       cmovz rax,   rdx                                 // -1 si rcx = 0
    end;
     
    // N° du dernier bit à 1 de Int64 (-1 si n = 0_______________________________________________TOOLS
    function bLast1(n : Int64): Int64; assembler; nostackframe; inline;
    asm
       mov   rdx,   -1
       bsr   rax,   rcx                              // rax = n° du 1er bit à 1
       cmovz rax,   rdx                              // -1 si rcx = 0
    end;
    Dans le cas cité il faut modifier un peu le code (je ne l'ai pas vérifié mais ça doit tomber en marche).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    // N° du premier bit à 1 de Int64 (-1 si n = 0)______________________________________________TOOLS
    function bFirst1(var n : Int64): Int64; assembler; nostackframe; inline;
    asm
       mov   rdx,   -1
       bsf   rax,   [rcx]                                 // rax = n° du 1er bit à 1,   [rcx] = n
       mov   r9,     0
       cmovz rax,   rdx                                   // -1 si n = 0
       jz    @bFirst_End                                  // Si n <> 0 alors
       bts   r9,    rax                                   //    r9 = 1^rax
       mov   [rcx],  r9                                   //    n = 1^rax
    @bFirst_End:
    end;
    En espérant que cela pourra être utile.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  12. #12
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut
    Re-bonjour,

    Pour compter les bits (toujours sous Lazarus en compile 64 bits).

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // Compte les bits à 1_________________________________________________________________________TOOLS
    function bCount(n : Int64): Int64; assembler; nostackframe; inline;
    asm
       popcnt rax,   rcx                             // rax = nombre de bit à 1 de rcx donc de n
    end;

    Bon week-end.
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  13. #13
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Free Pascal se débrouille bien avec l'assembleur alors pourquoi l'abandonner ?
    Merci pour votre réponse et pour le code. La raison, au départ, c'est que j'ai voulu compiler avec Free Pascal (au lieu de Delphi) et que je n'y arrivais pas. Ensuite Alcatîz m'a indiqué la directive {$asmMode intel}, qui a résolu le problème mais pas complètement. Quand j'essaie de compiler sous Linux, j'ai toujours des erreurs. Je ne sais pas si c'est à cause de Linux ou à cause de la version de FPC que j'utilise sous Linux (FPC 64 bits au lieu de 32 bits sous Windows). Et j'ai essayé tellement de choses différentes que tout s'est un peu embrouillé dans ma tête.

    Je vais reprendre tranquillement les choses du début et reviens vers vous dès que j'y vois plus clair. Bon week-end à vous aussi !
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  14. #14
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut
    Bonjour,

    Il y a un truc assez agaçant avec l'assembleur en ligne.

    Les anciennes versions de Delphi passaient les arguments (pour des integers) via les registres ainsi : arg1->eax, arg2->ecx, arg3-> edx.
    Les versions récentes (y compris Free Pascal) utilisent la convention fastcall qui passe les arguments ainsi : arg1->ecx, arg2->edx, arg3-> eax, arg4 -> rd8 (je ne sais pas s'il existe un moyen de forcer un fonctionnement à l'ancienne).

    Pour que Free Pascal utilise de préférence les registres, il faut lui dire en mettant un nostackframe. Mais dans tous les cas, si le nombre d'arguments dépasse 4 (actuellement), un cadre de pile est créé pour les arguments suivants.

    Pour revenir au problème posé, le code initial semble utiliser l'ancienne convention d'appel via registres (eax en premier). Le code peut donc compiler sans erreur mais, avec la convention d'appel fastcall, il n'y a aucune chance que le code assembleur fasse ce qu'il est censé faire.

    Un truc pas élégant est de réallouer les registres en ajoutant, avant l'ancien code assembleur, un code similaire à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    xchg   eax, ecx   // eax, ecx, edx -> ecx, eax, edx
    xchg   eax, edx   // eax, ecx, edx -> ecx, edx, eax
    De plus il faut ajouter nostackframe sinon les arguments seront dans la pile et pas dans les registres.

    Ce bricolage permet au moins de tester le code initial.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  15. #15
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Play it again Sam
    Il faut se méfier du code Delphi car il n'est pas homogène et, je crois, avec des erreurs.

    Par exemple le code suivant retourne, sauf erreur de ma part, n'importe quoi si aBoard = 0 (surtout pas 0 qui signifierait que le premier bit, bit n°0, est à 1):
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function FirstOne(aBoard: Int64): Integer;
    asm
          xor     eax, eax
          bsf     edx, dword ptr aBoard
          jnz     @@1
          mov     eax, 32
          bsf     edx, dword ptr aBoard + 4
     @@1: add     eax, edx
    end;
    LastOne : même punition, même motif.

    Mais il est possible que ces fonctions ne soient pas utilisées ou jamais avec 0.

    BitCount me semble OK (même si popcnt fait le job directement).

    BitScanReverse et BitScanForward récupèrent l'argument depuis EAX au lieu de ECX (changement de convention d'appel), il faudrait remplacer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
       MOV   ECX,[EAX]
       MOV   EDX,[EAX+4]
    par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
       mov   edx, [ecx+4]
       mov   ecx, [ecx]
    Et mettre l'attribut nostackframe.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  16. #16
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    @Guesset

    Merci.

    Bon, j'ai repris les choses du début.

    J'ai écrit ce petit programme de test :

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    uses
      SysUtils,
    {$IFDEF NOASM}
      UPasTools;
    {$ELSE}
      UASMTools;
    {$ENDIF}
     
    var
      LPiecesBlanches: Int64 = 65535; //%0000000000000000000000000000000000000000000000001111111111111111;
      LCavaliers: Int64 = 4755801206503243842; //%0100001000000000000000000000000000000000000000000000000001000010;
     
    begin
      WriteLn(BitCount(LPiecesBlanches));
      WriteLn(BitScanForward(LPiecesBlanches));
      WriteLn(LPiecesBlanches);
      WriteLn(BitCount(LCavaliers));
      WriteLn(BitScanForward(LCavaliers));
      WriteLn(LCavaliers);
    end.

    J'ai réécrit ma fonction BitScanForward :

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function BitScanForward(var X: Int64): Longint;
    var
      aux: Int64;
    begin
      if X = 0 then
        result := -1
      else
      begin
        aux := X;
        result := 0;
        while (aux and 1) = 0 do
        begin
          Inc(result);
          aux := aux shr 1;
        end;
        //X := 1 shl result;
      end;
    end;

    J'ai toujours un doute si la fonction doit changer la valeur de X, mais je pense plutôt que non.

    Et pour le moment j'ai laissé l'Assembleur tel quel, en ajoutant seulement nostackframe;.

    Enfin je compile, avec mes trois compilateurs, la version Assembleur et la version Pascal. Voici ce que ça donne :

    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    C:\Roland\echecs\sources\deepross\uci>build-dcc32-asm
    C:\Roland\echecs\sources\deepross\uci>test
    16
    0
    65535
    4
    1
    4755801206503243842
    
    C:\Roland\echecs\sources\deepross\uci>build-dcc32-noasm
    C:\Roland\echecs\sources\deepross\uci>test
    16
    0
    65535
    4
    1
    4755801206503243842
    
    C:\Roland\echecs\sources\deepross\uci>build-fpc32-asm
    C:\Roland\echecs\sources\deepross\uci>test
    20
    0
    65535
    20
    1
    4755801206503243842
    
    C:\Roland\echecs\sources\deepross\uci>build-fpc32-noasm
    C:\Roland\echecs\sources\deepross\uci>test
    16
    0
    65535
    4
    1
    4755801206503243842
    
    C:\Roland\echecs\sources\deepross\uci>build-fpc64-asm
    C:\Roland\echecs\sources\deepross\uci>test
    An unhandled exception occurred at $00000001000157B2:
    EAccessViolation: Access violation
      $00000001000157B2  BITCOUNT,  line 51 of uasmtools.pas
      $00000001000016B6  main,  line 17 of test.pas
      $0000000100001806
      $000000010000D4D0
      $0000000100001680
      $00007FFEDDB97974
      $00007FFEDEBDA271
    
    C:\Roland\echecs\sources\deepross\uci>build-fpc64-noasm
    C:\Roland\echecs\sources\deepross\uci>test
    16
    0
    65535
    4
    1
    4755801206503243842
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  17. #17
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Entre deux
    Bonjour,

    1. Effectivement, la fonction assembleur BitScanForward ne modifie pas la variable X. Son adresse est dans eax et il n'y a pas d'écriture (du genre mov [eax], edx). Ça permet de simplifier le code (pas de var) et d'utiliser x au sein de la fonction.
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function BitScanForward(X: Int64): Longint;  // Code pour Free Pascal
    begin
      if X = 0 then Exit(-1);
      result := 0;
      while (X and 1) = 0 do
      begin
        Inc(result);
        X := X shr 1;
      end;
    end;

    2. Il n'est pas possible de mettre seulement nostackframe sans changer les rôles des registres (ce serait trop facile ).
    En 32 bit, un int64 ne peut être passé par registre, il est donc mis sur la pile (qu'il y ait ou non un nostackframe) et aBoard (a priori dans ecx) désigne l'adresse dans la pile. Or la première ligne écrase ecx par le contenu à l'adresse [ecx]. Ce qui, à l'appel suivant pour la deuxième partie de l'Int64, provoque le crash ou un retour fantaisiste.
    En 64 bit, c'est pire. aBoard ne désigne plus une adresse mais la valeur même du registre rcx. L'utiliser comme adresse a les effets observés
    La solution la plus simple est d'utiliser le petit code avec popcnt (que j'ai modifié pour être compilable en 32 et 64 bits).
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    function BitCount(var n : Int64): byte; assembler; nostackframe; inline; // Testé en 64 bits même si ce code n'est pas optimal en 64 bits 
    asm
       popcnt eax,   dword ptr [n]        // ecx = @n
       popcnt edx,   dword ptr [n+4]   
       add    eax,    edx                   
    end;

    Bon courage.
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  18. #18
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Merci pour la nouvelle version de la fonction BitCount. Effectivement, en 64 bits, ça règle le problème, mais pas en 32 bits, semble-t-il.

    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    C:\Roland\echecs\sources\deepross\uci>build-fpc32-asm
    C:\Roland\echecs\sources\deepross\uci>test
    An unhandled exception occurred at $00412794:
    EAccessViolation: Access violation
      $00412794  BITCOUNT,  line 66 of uasmtools.pas
    
    C:\Roland\echecs\sources\deepross\uci>build-fpc64-asm
    C:\Roland\echecs\sources\deepross\uci>test
    16
    2
    65535
    4
    2
    4755801206503243842
    
    C:\Roland\echecs\sources\deepross\uci>build-dcc32-asm
    C:\Roland\echecs\sources\deepross\uci>test
    Exception EAccessViolation dans le module test.exe en 0001BEA0.
    Violation d'accès à l'adresse 0041BEA0 dans le module 'test.exe'. Lecture de l'adresse 00000014.

    Remarquez que je n'en ai pas vraiment besoin, puisque la version Pascal semble fonctionner, ce qui était mon objectif. À moins qu'il n'y ait une grosse différence de performance ?

    Merci pour la confirmation au sujet de la fonction BitScanForward. Je vais tester les modifications que vous avez suggérées dans la version Assembleur. On voit bien ci-dessus qu'avec FPC 64 les résultats sont faux.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  19. #19
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 317
    Points : 4 124
    Points
    4 124
    Par défaut Mea culpa
    Bonsoir,

    Je croyais que Free Pascal appliquait désormais la même convention d'appel en 64 bits et 32 bits. J'avais tort. Certainement pour des raisons de compatibilité avec les vielles versions de delphi, en 32 bits ils ont gardé l'ancienne convention.

    En résumé, en 32 bits l'adresse de n est dans eax (ie. dword ptr [n] = dword ptr [eax]) et en 64 bits l'adresse de n est dans rcx (ie. dword ptr [n] = dword ptr [rcx]) .

    En 32 bits, comme mon code modifie eax à la première ligne, eax ne correspond plus à l'adresse de n qui est nécessaire à la seconde ligne. En permutant les rôles de edx et eax on règle le problème (on a de la chance car il n'y a qu'un argument).

    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    function BitCount(var n : Int64): byte; assembler; nostackframe; inline; // Testé en 32 et 64 bits
    asm
       popcnt edx,   dword ptr [n]        // 32 bits eax = @n,  en 64 bits rcx = @n 
       popcnt eax,   dword ptr [n+4]   
       add    eax,    edx                   
    end;

    Moralité, il sera difficile d'avoir un même code assembleur compilable à la fois en 32 bits et 64 bits. Ça sent la compilation conditionnelle du genre {$IF WIN32} asm 32 {$ELSEIF WIN64} asm 64 {$ELSE} pascal. Ça devient un peu lourd.

    Coté performances l'assembleur est évidemment sensiblement meilleur (la traduction d'une opération native d'un cpu en pascal est vraisemblablement l'un des cas les plus défavorables. Mais si le taux d'usage est faible (hors d'une grande boucle par exemple) cela ne se verra pas.

    Encore mes excuses, je travaille essentiellement en 64 bits et ai extrapolé un peu vite au 32 bits.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  20. #20
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 062
    Points : 15 353
    Points
    15 353
    Billets dans le blog
    9
    Par défaut
    Merci beaucoup pour votre aide. Je vais essayer la nouvelle version de la fonction. Au départ je voulais me débarrasser de l'Assembleur mais s'il y a vraiment une différence de performance, pourquoi pas le garder effectivement. J'ai un autre programme d'échecs (que j'ai écrit moi-même, cette fois) qui est basé sur les bitboards. Je vais essayer de voir si les fonctions en Assembleur sont beaucoup plus performantes que les miennes.

    Pour la compilation conditionnelle, ça ne me dérange pas trop, vu que ce sont deux fonctions dans une unités à part.

    Bon, il faut encore aussi que je regarde comment ça se passe sous Linux. À demain pour la suite !
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 3
    Dernier message: 30/12/2016, 22h59
  2. Utiliser une fonction ec C dans un code assembleur (ARM)
    Par serialC dans le forum Autres architectures
    Réponses: 1
    Dernier message: 26/12/2012, 16h51
  3. Réécriture d'une fonction
    Par gedeon555 dans le forum Zend Framework
    Réponses: 1
    Dernier message: 27/06/2009, 00h27
  4. Réponses: 0
    Dernier message: 21/01/2009, 01h16
  5. Réponses: 6
    Dernier message: 21/09/2007, 15h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo