• Aucun résultat trouvé

Syntaxe assembleur avancée

Dans le document Programmation Avancée sous Linux (Page 189-193)

chaîne "r" indique qu’elle est stockée dans un registre mais ne contient pas le signe égal car il s’agit d’un opérande d’entrée et non pas de sortie.

La quatrième et dernière section indique que l’instruction modifie la valeur du registre de code condition cc.

9.2.1 Convertir un asm en instructions assembleur

Le traitement des constructions asm par GCC est très simple. Il produit des instructions assembleur pour traiter les opérandes de l’asm et replace la construction asm par l’instruction que vous spécifiez. Il n’analyse pas du tout l’instruction.

Par exemple, GCC convertit cet extrait de code :

d o u b l e foo , bar ;

asm ( " m y c o o l _ a s m %1 , %0 " : " = r " ( bar ) : " r " ( foo ) ) ;

en la séquence d’instructions x86 suivante :

m o v l -8(%e b p) ,%e d x m o v l -4(%e b p) ,%e c x

# APP

m y c o o l _ a s m %edx, %e d x

# N O _ A P P

m o v l %edx, -16(%e b p) m o v l %ecx, -12(%e b p)

Souvenez-vous quefooetbarrequièrent chacun deux mots d’espace de stockage dans la pile sur une architecture x86 32 bits. Le registre ebppointe vers les données sur la pile.

Les deux premières instructions copientfoodans les registresEDXetECXqu’utilisemycool_asm . Le compilateur a décidé d’utiliser les mêmes registres pour stocker la réponse, qui est copiée dans bar par les deux dernières instructions. Il choisit les registres adéquats, peut même les réutiliser, et copie les opérandes à partir et vers les emplacements appropriés automatiquement.

9.3 Syntaxe assembleur avancée

Dans les sous-sections qui suivent, nous décrivons les règles de syntaxe pour les constructions asm. Leurs sections sont séparées par deux-points.

Nous utiliserons l’instruction asmsuivante qui calcule l’expression booléennex > y :

asm ( " f u c o m i p %% st (1) , %% st ; s e t a %% al " :

" = a " ( r e s u l t ) : " u " ( y ) , " t " ( x ) : " cc " , " st " ) ;

Tout d’abord, fucomipcompare ses deux opérandes x ety et stocke les valeurs indiquant le résultat dans le registre de code condition. Puis,seta convertit ces valeurs en 0 ou 1.

9.3.1 Instructions assembleur

La première section contient les instructions assembleur, entre guillemets. La construction asm exemple contient deux instructions assembleur, fucomip et seta, séparées par un point-virgule. Si l’assembleur n’autorise pas les points-virgule, utilisez des caractères de nouvelle ligne

(\n) pour séparer les instructions. Le compilateur ignore le contenu de cette première section, excepté qu’il supprime un niveau de signes pourcent, %% devient donc %. La signification de

%%st(1) et d’autres termes similaires dépend de l’architecture.

GCC se plaindra si vous spécifiez l’option -traditional ou -ansi lors de la compilation d’un programme contenant des constructions asm. Pour éviter de telles erreurs, utilisez le mot clé alternatif__asm__ comme dans les fichiers d’en-tête cités précédemment.

9.3.2 Sorties

La seconde section spécifie les opérandes de sortie des instructions en utilisant une syntaxe C.

Chaque opérande est décrit par une contrainte d’opérande sous forme de chaîne suivie d’une expression C entre parenthèses. Pour les opérandes de sortie, qui doivent être des lvalues, la chaîne de contrainte doit commencer par un signe égal. Le compilateur vérifie que l’expression C pour chaque opérande de sortie est effectivement une lvalue.

Les lettres spécifiant les registres pour une architecture donnée peuvent être trouvée dans le code source de GCC, dans la macro REG_CLASS_FROM_LETTER. Par exemple, le fichier de configu-ration gcc/config/i386/i386.h de GCC liste les lettres de registre pour l’architecture x863. Le Tableau 9.1 en fait le résumé.

Tab. 9.1 – Lettres correspondant aux Registres sur l’Architecture Intel x86 Lettre Registres Éventuellement Utilisés par GCC

R Registre général (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP) q Registre de données général (EAX, EBX, ECX, EDX)

f Registre virgule flottante

t Registre en virgule flottante supérieur u Second registre en virgule flottante

a Registre EAX

b Registre EBX

c Registre ECX

d Registre EDX

x Registre SSE (Streaming SIMD Extension)

y Registres multimédias MMX

A Valeur de 8 octets formée à partir de EAX et EDX

D Pointeur de destination pour les opérations sur les chaînes (EDI) S Pointeur source pour les opérations sur les chaînes (ESI)

Lorsqu’une structureasmutilise plusieurs opérandes, elles doivent être décrites par une chaîne de contrainte et une expressions C et séparées par des virgules, comme l’illustre l’exemple donné précédemment. Vous pouvez spécifier jusqu’à 10 opérandes, numérotées de %0 à %9 dans les

3Vous devrez avoir une certaine familiarité avec le fonctionnement de GCC pour comprendre le contenu de ce fichier.

9.3. SYNTAXE ASSEMBLEUR AVANCÉE 177 sections d’entrée et de sortie. S’il n’y a pas d’opérandes de sortie ou de registre affectés, laissez la section de sortie vide ou signalez la avec un commentaire du type/* no outputs */.

9.3.3 Entrées

La troisième section décrit les entrées des instructions assembleur. La chaîne de contrainte d’un opérande d’entrée ne doit pas contenir de signe égal, ce qui indique une lvalue. Mis à part cela, leur syntaxe est identique à celle des opérandes de sortie.

Pour indiquer qu’un registre est à la fois lu et écrit par la même construction asm, utilisez l’indice de l’opérande de sortie comme chaîne de contrainte d’entrée. Par exemple, pour indiquer qu’un registre d’entrée est le même que le premier registre de sortie, utilisez 0. Spécifier la même expression C pour des opérandes d’entrée et de sortie ne garantit pas que les deux valeurs seront placées dans le même registre.

La section d’entrée peut être omise s’il n’y a pas d’opérandes d’entrée et que le section de déclaration des modifications est vide.

9.3.4 Déclaration des modifications

Si une instruction modifie les valeur d’un ou plusieurs registres par effet de bord, spécifiez ces registres dans la quatrième section de la structure asm. Par exemple, l’instruction fucomip modifie le registre de code condition, qui est désigné par cc. Les registres modifiés sont décrits dans des chaînes individuelles séparées par des virgules. Si l’instruction est susceptible de modifier un emplacement mémoire arbitraire, spécifiez memory. En utilisant les informations sur la modification des registres, le compilateur détermine les valeurs qui doivent être restaurées après l’exécution du bloc asm. Si vous ne renseignez pas ces informations correctement, GCC pourrait supposer que certains registres contiennent des valeurs qui ont en fait été écrasées, ce qui pourrait affecter le fonctionnement de votre programme.

9.3.5 Exemple

L’architecture x86 dispose d’instructions qui déterminent les positions du bit non nul le plus significatif ou le moins significatif dans un mot. Le processeur peut exécuter ces instructions de façon relativement efficace. Par contre, l’implémentation de la même opération en C nécessite une boucle et un décalage binaire.

Par exemple, l’instruction assembleurbsrlcalcule la position du bit le plus significatif de son premier opérande et place cette position (à partir de 0 qui représente le bit le moins significatif) dans le second. Pour placer la position du bit non nul le plus significatif de number dans position, nous pourrions utiliser cette structure asm :

asm ( " b s r l %1 , %0 " : " = r " ( p o s i t i o n ) : " r " ( n u m b e r ) ) ;

Une façon d’implémenter la même opération en C, est d’utiliser une boucle de ce genre :

l o n g i ;

f o r ( i = ( n u m b e r > > 1) , p o s i t i o n = 0; i != 0; ++ p o s i t i o n ) i > >= 1;

Pour comparer les vitesses de ces deux versions, nous allons les placer dans une boucle qui calcule les positions pour de grands nombres. Le Listing 9.1 utilise l’implémentation en C.

Le programme boucle sur des entiers, en partant de 1 jusqu’à la valeur passée sur la ligne de commande. Pour chaque valeur de number, il calcule la position du bit non nul le plus significatif.

Le Listing 9.2 effectue les mêmes opérations en utilisant une construction assembleur en ligne.

Notez que dans les deux versions, nous plaçons la position calculée à une variable volatile result. Cela permet d’éviter que l’optimiseur du compilateur ne supprime le calcul ; si le résultat n’est pas utilisé ou stocké en mémoire, l’optimiseur élimine le calcul en le considérant comme du code mort.

Listing 9.1 – (bit-pos-loop.c) – Recherche d’un Bit en Utilisant une Boucle

1 # i n c l u d e < s t d i o . h >

Listing 9.2 – (bit-pos-asm.c) – Recherche d’un Bit en Utilisant bsrl

1 # i n c l u d e < s t d i o . h >

Compilons les deux versions avec les optimisations actives :

Dans le document Programmation Avancée sous Linux (Page 189-193)