2015-12-24 1 views
0

I ont le code assembleur en ligne ci-dessous:rapports de llvm: asm inline non supporté: entrée avec une sortie correspondant à type 'void *' de type 'int'

int get_year(int a, int *b, char * c) 
{ 
    int ret, t1, t2; 

    asm (
     "addl %3, %[a]     \n\t" 
     "movl %[a], %[t1]    \n\t" 
     "movl $58, %%edx    \n\t" 
     "movb %%dl, 0x04(%1)   \n\t" 
     : [t1] "=r" (t1), "=&D" (t2) 
     : [a] "r" (a), "rm" (*b), "1" (c) 
     : "edx", "memory" 
    ); 

    ret = t1; 

    return ret; 
} 

Lorsque je compile via les llvm, décharges erreur:

error: unsupported inline asm: input with type 'char *' matching output with type 'int' 
       : [a] "r" (a), "rm" (*b), "1" (c) 
              ^

Cependant, la fonction memcpy dans le noyau linux a le même format d'utilisation de montage en ligne:

void *memcpy(void *dest, const void *src, size_t n) 
{ 
    int d0, d1, d2; 
    asm volatile(
     "rep ; movsl\n\t" 
     "movl %4,%%ecx\n\t" 
     "rep ; movsb\n\t" 
     : "=&c" (d0), "=&D" (d1), "=&S" (d2) 
     : "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src) 
     : "memory"); 

    return dest; 
} 

et t ses travaux correctement sans aucune erreur de compilation.

+0

On peut supposer que le compilateur gcc ciblé par le noyau n'est pas si strict. Vous pouvez facilement le résoudre en changeant 't2' en' char * ' – Jester

+0

Compiler avec le débogueur' lldb'? –

+0

@MichaelPetch désolé, c'est une erreur de bureau. J'utilise llvm .... –

Répondre

4

Tout d'abord, si vous essayez de vous mouiller les pieds en apprenant asm, GNU C inline asm est l'un des plus difficiles façons d'utiliser asm. Non seulement vous devez écrire asm correct, vous devez passer beaucoup de temps en utilisant la syntaxe ésotérique pour informer le compilateur de exactement ce que votre code a besoin pour les opérandes d'entrée et de sortie, ou vous aurez un mauvais moment. Écriture de fonctions entières dans ASM est beaucoup plus facile. Ils ne peuvent pas être en ligne, mais c'est un exercice d'apprentissage de toute façon. La fonction normale ABI est beaucoup plus simple que la limite entre C et ASM en ligne avec des contraintes. Voir le wiki ...


Outre cette erreur de compilation, vous avez un bug: vous CLOBBER %[a], même si vous avez dit gcc c'est un opérande d'entrée uniquement. Je suppose que c'est encore un "travail en cours", puisque vous pourriez obtenir le même résultat avec un meilleur code. (par exemple en utilisant %edx comme une regre scratch est totalement inutile.) Bien sûr, dans le cas général où ceci est incorporé dans un code où a pourrait être une constante de compilation, ou connu pour être lié à autre chose, vous obtiendriez mieux le code de juste le faire en C (à moins que vous avez passé beaucoup de temps à faire des variantes en ligne SMO pour différents cas.)

int get_year(int a, int *b, char * c) 
{ 
    int ret, t1, t2; 

    asm (
     "addl %[bval], %[a] \n\t" 
     "movb $58, 4 + %[cval]\n\t" // c is an "offsetable" memory operand 

     : [t1] "=&r" (t1), [cval] "=o" (*c) 
     : [a] "0" (a), [bval] "erm" (*b) 
     : // no longer clobbers memory, because we use an output memory operand. 
    ); 

    ret = t1; // silly redundancy here, could have just used a as an input/output operand and returned it, since you apparently want the value 
    return ret; 
} 

Ce maintenant compiles and assembles (en utilisant l'option « binaire » de Godbolt à assembler en fait). Le 4 + (%rdx) produit un avertissement, mais s'assemble à 4(%rdx). IDK comment écrire le décalage d'une manière qui ne fait pas d'erreur s'il y a déjà un décalage. (Par exemple, si l'opérande est *(c+4), de sorte que le produit est asm 4 + 4(%rdx), il ne fonctionnerait pas quitter le +.)

Ceci est encore en utilisant l'astuce correspondante sortie opérandes, mais je changé en utilisant la mémoire ou contraintes générales pour permettre aux constantes de temps de compilation de finir par faire addl $constant, %edi.

Ceci permet au compilateur d'avoir le maximum de flexibilité lors de l'inlining. par exemple. Si un appelant exécutait get_year(10, &arr[10], &some_struct.char_member), il pouvait utiliser le mode d'adressage voulu pour le chargement et le stockage, au lieu de devoir générer c dans un seul registre. Ainsi, la sortie en ligne pourrait finir par être movb $58, 4+16(%rbp, %rbx) par exemple, au lieu de le forcer à utiliser 4(%reg).

+0

Pouvez-vous m'expliquer davantage sur l'astuce matching-output-operand? Je ne le sais pas clairement BTW, j'ai compilé votre code dans mon macbook en utilisant le compilateur llvm de Apple, j'ai '' : 2: 17: note: instanciée en assemblage ici movb $ 58, 4 + (% rdx) '... –

+0

J'ai des quesitons serval: 1) dans' [cval] "= o" (* c) ', pourquoi c'est' (* c) 'mais' (c) ', je pense que c est une adresse, * c est une variable char. 2) dans '[bval]" erm "(* b)', que représente e? Je ne trouve pas le manuel gcc ... –

+0

re: l'astuce match-output-opérande. Je l'ai obtenu du code que vous avez posté ... IDK si cela a des avantages par rapport à la suggestion de Ross Ridge d'utiliser un opérande d'entrée/sortie '" + r "' pour les entrées que vous voulez écraser dans le bloc asm. J'ai juste gardé cette astuce puisque je ne l'avais pas vu auparavant, et je voulais voir ce qui s'était passé.Je ne peux pas penser à quelque chose immédiatement où je m'attendrais à ce que la méthode opton-output-opérande fasse un meilleur code, quand l'opérande de sortie est juste une variable tmp jetable. Peut-être que s'il a une taille différente, il est plus facile d'éviter les préfixes pour obtenir '% eax' vs'% rcx'? –

2

Je peux reproduire le problème si je compile votre code avec clang uniquement lors de la génération de code 64 bits. Lorsque vous ciblez le code 32 bits, il n'y a pas d'erreur. Comme Michael Petch l'a dit, cela suggère que le problème réside dans les différentes tailles des deux opérandes.

Il n'est pas tout à fait clair quel serait le meilleur correctif, car votre instruction asm n'a pas beaucoup de sens. Il est l'équivalent de:

int get_year(int a, int *b, char *c) { 
    a += *b; 
    c[4] = 58; 
    return a; 
}   

Il n'y a aucun avantage à utiliser une déclaration d'assemblage pour faire ce qui peut être fait plus clairement et plus efficacement en utilisant le code C ci-dessus. Donc, la meilleure solution serait de remplacer complètement votre code par le code C équivalent.

Si vous êtes juste jouer avec l'assembleur en ligne, puis l'assemblage en ligne équivalent serait:

int get_year2(int a, int *b, char * c) 
{ 
     asm("addl %[b], %[a]" 
      : [a] "+r" (a) 
      : [b] "m" (*b) 
      : "cc"); 
     asm("movb $58, %[c4]" 
      : [c4] "=rm" (c[4])); 
     return a; 
} 

Je l'ai utilisé deux déclarations asm parce que les deux parties ne sont pas liés. Les garder séparés offre plus de possibilités d'optimisation. Par exemple si vous appelez cette fonction mais n'utilisez pas la valeur de retour, le compilateur peut éliminer la première instruction asm car son résultat n'est pas utilisé et il n'a aucun effet secondaire. Au lieu d'utiliser une contrainte de correspondance, la contrainte "1" qui vous causait des problèmes, j'ai utilisé le modificateur de contrainte "+" pour marquer l'opérande à la fois comme une entrée et une sortie. Je trouve que cela fonctionne mieux. La contrainte pour l'opérande [b] devrait vraiment être "rm" mais unfortunately clang doesn't handle rm constraints well.

Vous avez probablement remarqué que j'ai utilisé seulement deux instructions d'assemblage où votre exemple utilisait quatre. L'instruction MOVL n'est pas nécessaire, le compilateur peut gérer le déplacement du résultat dans le registre de valeur de retour, si nécessaire. Vos deux dernières instructions d'assemblage peuvent être regroupées en une seule instruction qui déplace la constante directement dans la mémoire sans écraser un registre. En parlant de cela, votre déclaration ASM clobbers EFLAGS, les codes de condition, donc "cc" devrait être répertorié un clobbered, mais comme Peter Cordes note que ce n'est pas nécessaire avec les cibles x86, mais le compilateur suppose qu'ils sont de toute façon.

+1

Fun fait qui était une surprise pour moi aussi: x86/x86-64 inline asm implicitement http://stackoverflow.com/questions/6659414/efficient-128-bit-addition-using-carry-flag#comment56610424_6671550 Donc, peu d'instructions * n'affectent pas * les drapeaux qui causeraient beaucoup plus de bugs que –

+1

@PeterCordes Ah, je me demandais pourquoi tant d'énoncés asm apparemment brisés fonctionnaient dans la pratique, je supposais juste qu'il y avait juste quelques occasions de faire usage des drapeaux à travers une machine. –