Question

Est-ce que quelqu'un sait comment je peux me débarrasser de l'avertissement suivant concernant l'assembleur?

Le code est x86, 32 bits:

int test (int x)
{
  int y;
  // do a bit-rotate by 8 on the lower word. leave upper word intact.
  asm ("rorw $8, %0\n\t": "=q"(y) :"0"(x));
  return y;
}

Si je le compile, l'avertissement suivant (très valide) est émis:

Warning: using `%ax' instead of `%eax' due to `w' suffix

Ce que je recherche, c’est un moyen de dire au compilateur / assembleur que je souhaite accéder au sous-registre 16 bits inférieur de% 0. L'accès aux sous-registres d'octets (dans ce cas, AL et AH) serait également utile.

J'ai déjà choisi l'option "q". modificateur, le compilateur est donc obligé d’utiliser EAX, EBX, ECX ou EDX. Je me suis assuré que le compilateur doit choisir un registre contenant des sous-registres.

Je sais que je peux forcer le code asm à utiliser un registre spécifique (et ses sous-registres), mais je souhaite laisser le travail d'allocation de registres au compilateur.

Était-ce utile?

La solution

Vous pouvez utiliser % w0 si je me souviens bien. Je viens de le tester aussi. : -)

int
test(int x)
{
    int y;
    asm ("rorw $8, %w0" : "=q" (y) : "0" (x));
    return y;
}

Modifier: en réponse à l'OP, vous pouvez également effectuer les opérations suivantes:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

À l'heure actuelle, le seul endroit (à ma connaissance, documenté) est gcc / config / i386 / i386.md , pas dans la documentation standard.

Autres conseils

Il y a longtemps, mais j'en aurai probablement besoin pour ma propre référence future ...

En ajoutant à la réponse précise de Chris, la clé utilise un modificateur compris entre '%' et le numéro de l'opérande de sortie. Par exemple, "MOV% 1,% 0" peut devenir "MOV% q1,% w0" .

Je n'ai rien trouvé dans contraintes.md, mais /gcc/config/i386/i386.c avait ce commentaire potentiellement utile dans le code source de print_reg () :

/* Print the name of register X to FILE based on its machine mode and number.
   If CODE is 'w', pretend the mode is HImode.
   If CODE is 'b', pretend the mode is QImode.
   If CODE is 'k', pretend the mode is SImode.
   If CODE is 'q', pretend the mode is DImode.
   If CODE is 'x', pretend the mode is V4SFmode.
   If CODE is 't', pretend the mode is V8SFmode.
   If CODE is 'h', pretend the reg is the 'high' byte register.
   If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
   If CODE is 'd', duplicate the operand for AVX instruction.
 */

Un commentaire ci-dessous pour ix86_print_operand () offre un exemple:

  

b - affiche le nom QImode du registre pour l'opérande indiqué.

     

% b0 affichera% al si les opérandes [0] sont reg 0.

Quelques autres options utiles sont répertoriées dans le modèle de sortie de la Documentation de GCC Internals :

  

& # 8216;% cdigit & # 8217; peut être utilisé pour substituer un opérande qui est une constante   valeur sans la syntaxe qui indique normalement un opérande immédiat.

     

& # 8216;% ndigit & # 8217; ressemble à &% 8216;% cdigit & # 8217; sauf que la valeur de la constante est   annulé avant impression.

     

& # 8216;% adigit & # 8217; peut être utilisé pour substituer un opérande comme s'il s'agissait d'une mémoire   référence, avec l'opérande réel traité comme l'adresse. Cela peut être   utile pour émettre une & # 8220; adresse de chargement & # 8221; instruction, parce que souvent le   La syntaxe de l'assembleur pour une telle instruction nécessite que vous écriviez le   opérande comme s’il s’agissait d’une référence mémoire.

     

& # 8216;% ldigit & # 8217; est utilisé pour substituer un label_ref dans une instruction de saut.

     

& # 8216;% = & # 8217; génère un nombre unique pour chaque instruction du   toute la compilation. Ceci est utile pour que les étiquettes locales soient   référé à plus d'une fois dans un seul modèle qui génère   plusieurs instructions d'assembleur.

La construction '% c2 ' permet de formater correctement une instruction LEA en utilisant un décalage:

#define ASM_LEA_ADD_BYTES(ptr, bytes)                            \
    __asm volatile("lea %c1(%0), %0" :                           \
                   /* reads/writes %0 */  "+r" (ptr) :           \
                   /* reads */ "i" (bytes));

Notez le 'c' crucial mais peu documenté dans '% c1 '. Cette macro est équivalente à

ptr = (char *)ptr + bytes

mais sans utiliser les ports d’exécution arithmétiques entiers habituels.

Modifier pour ajouter:

Il est parfois difficile de passer des appels directs en x64, car cela nécessite un autre modificateur non documenté: '% P0 ' (ce qui semble être pour le PIC)

#define ASM_CALL_FUNC(func)                                         \
    __asm volatile("call %P0") :                                    \
              /* no writes */ :                                     \
              /* reads %0 */ "i" (func))                           

Un modificateur "p" en minuscule semble également fonctionner de la même manière dans GCC, bien que seul le "P" majuscule soit reconnu par ICC. Plus de détails sont probablement disponibles à l'adresse / gcc / config / i386 /. i386.c . Recherchez "'p'".

Pendant que j'y réfléchis ... remplacez le & q; q " contrainte avec un capital " Q " contrainte dans la deuxième solution de Chris:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

& q; " et " Q " sont légèrement différentes en mode 64 bits, où vous pouvez obtenir l'octet le plus bas pour tous les registres d'entiers (ax, bx, cx, dx, si, di, sp, bp, r8-r15). Mais vous ne pouvez obtenir que le deuxième octet le plus faible (par exemple, ah) pour les quatre registres 386 d'origine (ax, bx, cx, dx).

Donc, apparemment, il existe des astuces pour le faire ... mais cela n’est peut-être pas aussi efficace. Les processeurs x86 32 bits sont généralement lents à manipuler des données 16 bits dans des registres à usage général. Vous devez le comparer si la performance est importante.

À moins que cela ne soit (a) critique sur le plan des performances et (b) avéré être beaucoup plus rapide, je voudrais éviter quelques tracas de maintenance et le faire en C:

uint32_t y, hi=(x&~0xffff), lo=(x&0xffff);
y = hi + (((lo >> 8) + (lo << 8))&0xffff);

Avec GCC 4.2 et -O2, cela s’optimise jusqu’à six instructions ...

Gotcha. Eh bien, s’il s’agit d’une routine primitive que vous allez réutiliser encore et encore, je n’ai aucune objection à en faire ... le truc de nommage de registre que Chris a souligné est un joli procédé dont je vais devoir me souvenir. / p>

Ce serait bien qu’il soit également inclus dans la documentation GCC standard!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top