Domanda

Qualcuno sa come posso liberarmi del seguente avvertimento assembler?

Il codice è x86, 32 bit:

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;
}

Se lo compilo ricevo il seguente avviso (molto valido):

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

Quello che sto cercando è un modo per dire al compilatore / assemblatore che voglio accedere al sotto-registro a 16 bit inferiore di% 0. Accedere ai sottoregistratori di byte (in questo caso AL e AH) sarebbe utile saperlo.

Ho già scelto il " q " modificatore, quindi il compilatore è costretto a usare EAX, EBX, ECX o EDX. Mi sono assicurato che il compilatore dovesse scegliere un registro con sotto-registri.

So che posso forzare il codice asm a usare un registro specifico (e i suoi sotto-registri), ma voglio lasciare il lavoro di allocazione del registro fino al compilatore.

È stato utile?

Soluzione

Puoi usare % w0 se ricordo bene. L'ho appena provato anche io. : -)

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

Modifica: in risposta all'OP, sì, puoi anche fare quanto segue:

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

Al momento, l'unico posto (di cui sono a conoscenza) in cui è documentato è gcc / config / i386 / i386.md , non nella documentazione standard.

Altri suggerimenti

Molto tempo fa, ma probabilmente ne avrò bisogno per il mio riferimento futuro ...

Aggiungendo la risposta eccellente di Chris, la chiave sta usando un modificatore tra '%' e il numero dell'operando di output. Ad esempio, " MOV% 1,% 0 " potrebbe diventare " MOV% q1,% w0 " .

Non sono riuscito a trovare nulla in limitsints.md, ma /gcc/config/i386/i386.c ha avuto questo commento potenzialmente utile nella fonte per 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 commento qui sotto per ix86_print_operand () offre un esempio:

  

b - stampa il nome QImode del registro per l'operando indicato.

     

% b0 stamperebbe% al se gli operandi [0] sono reg 0.

Alcune altre utili opzioni sono elencate in Modello di output del Documentazione GCC Internals :

  

& # 8216;% cdigit & # 8217; può essere usato per sostituire un operando che è una costante   valore senza la sintassi che normalmente indica un operando immediato.

     

& # 8216;% ndigit & # 8217; è come & # 8216;% cdigit & # 8217; tranne che il valore della costante è   negato prima della stampa.

     

& # 8216;% adigit & # 8217; può essere usato per sostituire un operando come se fosse un ricordo   riferimento, con l'operando effettivo trattato come l'indirizzo. Può essere   utile quando viene emesso un indirizzo di caricamento & # 8220; & # 8221; istruzione, perché spesso il   La sintassi dell'assemblatore per tale istruzione richiede di scrivere il file   operando come se fosse un riferimento di memoria.

     

& # 8216;% ldigit & # 8217; è usato per sostituire un label_ref in un'istruzione jump.

     

& # 8216;% = & # 8217; genera un numero univoco per ciascuna istruzione nel file   intera raccolta. Questo è utile per creare etichette locali   riferito a più di una volta in un singolo modello che genera   istruzioni multiple per l'assemblatore.

Il costrutto '% c2 ' consente di formattare correttamente un'istruzione LEA usando un offset:

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

Nota la 'c' cruciale ma scarsamente documentata in '% c1 '. Questa macro è equivalente a

ptr = (char *)ptr + bytes

ma senza utilizzare le solite porte di esecuzione aritmetica dei numeri interi.

Modifica per aggiungere:

Effettuare chiamate dirette in x64 può essere difficile, poiché richiede ancora un altro modificatore non documentato: '% P0 ' (che sembra essere per PIC)

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

Anche un modificatore 'p' minuscolo sembra funzionare allo stesso modo in GCC, sebbene solo il capitale 'P' sia riconosciuto da ICC. Maggiori dettagli sono probabilmente disponibili su / gcc / config / i386 / i386.c . Cerca " 'p' " ;.

Mentre ci sto pensando ... dovresti sostituire il " q " vincolo con un capitale "Q" vincolo nella seconda soluzione di Chris:

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

" q " e "Q" sono leggermente diversi nella modalità a 64 bit, dove è possibile ottenere il byte più basso per tutti i registri di numeri interi (ax, bx, cx, dx, si, di, sp, bp, r8-r15). Ma puoi ottenere solo il secondo byte più basso (ad es. Ah) per i quattro registri 386 originali (ax, bx, cx, dx).

Quindi apparentemente ci sono dei trucchi per farlo ... ma potrebbe non essere così efficiente. I processori x86 a 32 bit sono generalmente lenti nel manipolare i dati a 16 bit nei registri di uso generale. Dovresti confrontarlo se le prestazioni sono importanti.

A meno che non si tratti di (a) prestazioni critiche e (b) si dimostri molto più veloce, mi risparmierei un po 'di problemi di manutenzione e lo farei semplicemente in C:

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

Con GCC 4.2 e -O2 questo viene ottimizzato fino a sei istruzioni ...

Gotcha. Bene, se è una routine primitiva che stai per riutilizzare più e più volte, non ho alcun argomento a riguardo ... il trucco di denominazione del registro che Chris ha sottolineato è una cosa carina che dovrò ricordare.

Sarebbe bello se lo facesse anche nei documenti GCC standard!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top