2017-01-15 1 views
1

J'essaie actuellement d'utiliser le popcnt intégré afin de compter le nombre de 1 dans un tableau de unsigned char.gcc inline asm ne compilant pas

J'ai eu la fonction fonctionnant avec le __builtin_popcount régulier mais avec des exigences de vitesse plus agressives, j'ai décidé d'aller avec l'approche en ligne asm.

size_t popcnt_manual(unsigned char *bytes, size_t len) { 
    size_t i, cnt = 0; 
    for(i = 0; i < len; i++) { 
     __asm__(
       "popcnt %0, %0 \n\t" 
       "add %0, %1 \n\t" 
       : "+r" (cnt) 
       : "r" (bytes[i])); 
    } 

    return cnt; 
} 

Et le compilateur ne cesse de dire

suffixe

ou opérandes non valides pour ajouter

Répondre

2

En dehors de l'erreur de syntaxe dans votre code (" "r" ->: "r"), votre problème est paramètres dépareillées .

En regardant la sortie de S:

add rax, r8b 

Depuis cnt est size_t et bytes[i] est un octet, c'est ce que vous attendez. Ajouter exige qu'ils soient de la même taille. Puis-je également suggérer d'utiliser des builtins au lieu de inline asm? Il évite des problèmes comme celui-ci (ainsi que beaucoup d'autres).


Est-il possible d'ajouter le résultat de POPCNT sans l'enregistrer dans un registre de premier?

Umm. C'est en fait une question entièrement différente. L'erreur que vous avez posée concerne le mélange d'un octet et d'un size_t dans une seule instruction add. Il peut être résolu en faisant:

__asm__(
      "popcnt %0, %0 \n\t" 
      "add %0, %1 \n\t" 
      : "+r" (cnt) 
      : "r" ((size_t)bytes[i])); 

Je ne devrais pas vous encourager à continuer à ajouter de nouvelles questions (comment vais-je obtenir mes points de karma), mais en regardant ce site, le code qu'il semble jouer avec est la suivante:

uint32_t builtin_popcnt_unrolled_errata(const uint64_t* buf, int len) { 
    assert(len % 4 == 0); 
    int cnt[4]; 
    for (int i = 0; i < 4; ++i) { 
    cnt[i] = 0; 
    } 

    for (int i = 0; i < len; i+=4) { 
    cnt[0] += __builtin_popcountll(buf[i]); 
    cnt[1] += __builtin_popcountll(buf[i+1]); 
    cnt[2] += __builtin_popcountll(buf[i+2]); 
    cnt[3] += __builtin_popcountll(buf[i+3]); 
    } 
    return cnt[0] + cnt[1] + cnt[2] + cnt[3]; 
} 

Il est explicitement utilise cnt [x] pour tenter d'éviter le problème « fausse dépendance » de POPCNT.

avec gcc 6.1 et la compilation avec -m64 -O3 -march=native -mtune=native, je vois cela comme sortie:

.L14: 
     popcnt r11, QWORD PTR [rcx] 
     add  rcx, 32 
     add  edx, r11d 
     popcnt r11, QWORD PTR -24[rcx] 
     add  eax, r11d 
     popcnt r11, QWORD PTR -16[rcx] 
     add  r10d, r11d 
     popcnt r11, QWORD PTR -8[rcx] 
     add  r9d, r11d 
     cmp  rcx, r8 
     jne  .L14 

qui « stocker hors dans un registre » faites-vous référence?

+0

oublié de mentionner que j'ai commencé avec builtins mais je voulais quelque chose de plus rapide. Est-il possible d'additionner le résultat de popcnt sans le stocker dans un registre en premier? Vous avez l'idée de https://danluu.com/assembly-intrinsics/ –

+0

merci pour la réponse? Je dois avoir été juste confondu avec ce que le blog essayait de dire. Voici vos points de karma :) –

+0

Le blog n'a pas de date dessus, mais il a été écrit en utilisant 4.8.2. C'était il y a longtemps, donc les choses ont peut-être changé. C'est une autre raison pour laquelle je recommande d'utiliser des builtins au lieu de inline. Les builtins peuvent s'améliorer avec le temps, mais si vous écrivez en ligne, vous obtenez exactement ce que vous écrivez. BTW, si la performance est critique, l'utilisation de la popcnt 64 bits est (probablement) plus rapide que les 8 bits répétés. Vous aurez besoin de vérifier la longueur et gérer les tailles impaires, mais je voudrais l'essayer et le chronométrer. –