Dans cet article, nous explorerons le sujet de Jeux d'instructions de manipulation de bits sous différentes perspectives et approches, dans le but de fournir une vision complète et complète de ce sujet. Tout au long du texte, nous analyserons à la fois ses origines et sa pertinence aujourd’hui, ainsi que ses implications et conséquences possibles. De même, nous examinerons différentes études et recherches qui ont été menées autour de Jeux d'instructions de manipulation de bits, afin de proposer une vision académique et scientifiquement fondée. En fin de compte, cet article cherche à fournir au lecteur un aperçu approfondi et détaillé de Jeux d'instructions de manipulation de bits, afin qu'il puisse acquérir des connaissances solides et complètes sur le sujet.
Les jeux d’instructions de manipulation de bits (Bit Manipulation Instruction sets, BMI) sont des extensions de l’architecture de jeu d'instructions x86 pour les microprocesseurs d’Intel et d’AMD. Le but de ces jeux d’instructions est d’améliorer la vitesse de manipulation de bits. Toutes les instructions de ces ensembles sont non-SIMD et ne fonctionnent que sur des registres à usage général.
Il existe deux ensembles publiés par Intel : BMI (maintenant appelé BMI1) et BMI2 ; ils ont tous deux été introduits avec la microarchitecture Haswell avec les fonctionnalités d’appariement BMI1 offertes par le jeu d’instructions ABM d’AMD et BMI2 les étendant. Deux autres ensembles ont été publiés par AMD : ABM (Advanced Bit Manipulation, qui est également un sous-ensemble de SSE4a implémenté par Intel dans le cadre de SSE4.2 et de BMI1), et TBM (Trailing Bit Manipulation, une extension introduite avec les processeurs basés sur Piledriver en tant qu’extension de BMI1, mais abandonnée ensuite dans les processeurs basés sur Zen)[1].
AMD a été le premier à introduire les instructions qui forment maintenant le BMI1 d’Intel dans le cadre de son jeu d’instructions ABM (Advanced Bit Manipulation), puis a ajouté plus tard la prise en charge des nouvelles instructions BMI2 d’Intel. AMD annonce aujourd’hui la disponibilité de ces fonctionnalités via les flags CPU BMI1 et BMI2 d’Intel et demande aux programmeurs de les choisir en conséquence[2].
Tandis qu'Intel considère POPCNT
comme appartenant à SSE4.2 et LZCNT
appartenant à BMI1, Intel et AMD indiquent tous deux de la présence de ces deux instructions individuellement. POPCNT
a un drapeau CPUID de même nom et Intel et AMD utilisent le drapeau AMD ABM
pour indiquer le support de LZCNT
(puisque LZCNT
combiné avec BMI1 et BMI2 complète le jeu d'instructions ABM étendu)[2],[3].
Code | Instruction | Description[4] |
---|---|---|
F3 0F B8 /r
|
POPCNT
|
Comptage de population |
F3 0F BD /r
|
LZCNT
|
Compte le nombre de bits à zéro à gauche (en tête) |
LZCNT
est liée à l'instruction Bit Scan Reverse (BSR
), mais arme les drapeaux ZF (si le résultat vaut zéro) et CF (si la source vaut zéro) plutôt que d'armer le drapeau ZF (si la source vaut zéro). Il produit également un résultat défini (la taille de l'opérande source en bits) si l'opérande source vaut zéro. Pour un argument différent de zéro, la somme des résultats de LZCNT
et de BSR
est la taille en bits de l'argument moins 1 (par exemple, si l'argument 32 bits est 0x000f0000
, LZCNT donne 12 et BSR donne 19, la somme vaut 31).
Le codage de LZCNT
est tel que si ABM n'est pas supporté, alors l'instruction BSR
est exécutée à la place[4]:227.
Les instructions ci-dessous sont celles activées par le bit BMI
dans le CPUID. Intel considère officiellement LZCNT
comme partie de BMI, mais indique le support de LZCNT
à l'aide du flag CPUID ABM
[3]. BMI1 est disponible sur les processeurs AMD Jaguar[5], Piledriver[6] et plus récents, et sur les processeurs Intel Haswell[7] et plus récents.
Code | Instruction | Description[3] | Expression C équivalente[8],[9],[10] |
---|---|---|---|
VEX.LZ.0F38 F2 /r
|
ANDN
|
(NON A) ET B logique | ~x & y
|
VEX.LZ.0F38 F7 /r
|
BEXTR
|
Extraction d'un champ de bits (avec registre) | (src >> start) & ((1 << len) - 1)
|
VEX.LZ.0F38 F3 /3
|
BLSI
|
Extrait le bit isolé à 1 de poids le plus faible | x & -x
|
VEX.LZ.0F38 F3 /2
|
BLSMSK
|
Obtient le masque jusqu'au bit à 1 de poids le plus faible | x ^ (x - 1)
|
VEX.LZ.0F38 F3 /1
|
BLSR
|
Met à zéro le bit à 1 de poids le plus faible | x & (x - 1)
|
F3 0F BC /r
|
TZCNT
|
Compte le nombre de bits à zéro en queue (à droite) |
L'instruction TZCNT
est presque identique à l'instruction Bit Scan Forward (BSF
), mais arme les drapeaux ZF (si le résultat vaut zéro) et CF (si la source vaut zéro) plutôt que d'armer le drapeau ZF (si la source vaut zéro). Pour un argument différent de zéro, les résultats de TZCNT
et de BSF
sont identiques.
Comme avec LZCNT
, le codage de TZCNT
est tel que si BMI1 n'est pas supporté, alors l'instruction BSF
est exécutée à la place[4]:352.
Intel a introduit BMI2 en même temps que BMI1 dans sa gamme de processeurs Haswell. Seul AMD a produit des processeurs supportant BMI1 mais pas BMI 2 ; BMI2 est supporté par l'architecture AMD Excavator et plus récente[11].
Codage | Instruction | Description |
---|---|---|
VEX.LZ.0F38 F5 /r
|
BZHI
|
Mise à zéro des bits les plus élevés à partir d'une position de bit spécifiée |
VEX.LZ.F2.0F38 F6 /r
|
MULX
|
Multiplication non signée sans affecter les drapeaux, avec des registres de destination quelconques |
VEX.LZ.F2.0F38 F5 /r
|
PDEP
|
Dépôt de bits en parallèle |
VEX.LZ.F3.0F38 F5 /r
|
PEXT
|
Extraction de bits en parallèle |
VEX.LZ.F2.0F3A F0 /r ib
|
RORX
|
Rotation logique vers la droite sans affecter les drapeaux |
VEX.LZ.F3.0F38 F7 /r
|
SARX
|
Décalage arithmétique vers la droite sans affecter les drapeaux |
VEX.LZ.F2.0F38 F7 /r
|
SHRX
|
Décalage logique vers la droite sans affecter les drapeaux |
VEX.LZ.66.0F38 F7 /r
|
SHLX
|
Décalage logique vers la gauche sans affecter les drapeaux |
Les instructions PDEP
et PEXT
sont de nouvelles instructions généralisées de compression et d’expansion au niveau du bit. Elles prennent deux entrées : l’un est une source, et l’autre est un sélecteur. Le sélecteur est une image bitmap qui sélectionne les bits qui doivent être compressés ou décompressés. PEXT
copie les bits sélectionnés de la source vers les bits de poids faible contigus de la destination ; les bits de destination d’ordre supérieur sont effacés. PDEP
fait l’inverse pour les bits sélectionnés : les bits de poids faible contigus sont copiés sur les bits sélectionnés de la destination ; les autres bits de destination sont effacés. Cela peut être utilisé pour extraire n’importe quel champ de bits de l’entrée, et même effectuer beaucoup de brassage au niveau des bits, ce qui aurait été coûteux auparavant. Bien que ces instructions soient similaires aux instructions SIMD de gather-scatter (en) (collecte-diffusion) au niveau des bits, les instructions PDEP
et PEXT
(comme le reste des jeux d’instructions BMI) fonctionnent sur des registres à usage général[12].
Les instructions sont disponibles en versions 32 bits et 64 bits. Voici un exemple d’utilisation d’une source et d’un sélecteur arbitraires en mode 32 bits :
Instruction | Masque du sélecteur | Source | Destination |
---|---|---|---|
PEXT |
0xff00fff0 |
0x12345678 |
0x00012567
|
PDEP |
0xff00fff0 |
0x00012567 |
0x12005670
|
Les processeurs AMD avant Zen 3[13] qui implémentent PDEP et PEXT le font en microcode, avec une latence de 18 cycles[14] contre 3 cycles sur Zen 3[15]. Par conséquent, il est souvent plus rapide d'utiliser d'autres instructions sur ces processeurs[16].
TBM est constitué d'instructions complémentaires au jeu d'instructions initié par BMI1 ; leur nature complémentaire signifie qu'elles n'ont pas nécessairement besoin d'être utilisées directement mais qu'elles peuvent être générées par un compilateur optimisé si elles sont supportées. AMD a introduit TBM en même temps que BMI1 dans sa gamme de processeurs Piledriver[6] ; les processeurs AMD suivants Jaguar et Zen ne supportent pas TBM[5]. Aucun processeur Intel (au moins jusqu'à Alder Lake) ne supporte TBM.
Codage | Instruction | Description[4] | Expression C équivalente[17],[9] |
---|---|---|---|
XOP.LZ.0A 10 /r id
|
BEXTR
|
Extraire un champ de bits (avec immédiat) | (src >> start) & ((1 << len) - 1)
|
XOP.LZ.09 01 /1
|
BLCFILL
|
Remplir à partir du bit à zéro de poids le plus faible | x & (x + 1)
|
XOP.LZ.09 02 /6
|
BLCI
|
Isoler le bit à zéro de poids le plus faible | x | ~(x + 1)
|
XOP.LZ.09 01 /5
|
BLCIC
|
Isoler le bit à zéro de poids le plus faible et calculer le complément |
~x & (x + 1)
|
XOP.LZ.09 02 /1
|
BLCMSK
|
Masque à partir du bit à zéro de poids le plus faible | x ^ (x + 1)
|
XOP.LZ.09 01 /3
|
BLCS
|
Mettre à 1 le bit de poids le plus faible | x | (x + 1)
|
XOP.LZ.09 01 /2
|
BLSFILL
|
Remplir à partir du bit à 1 de poids le plus faible | x | (x - 1)
|
XOP.LZ.09 01 /6
|
BLSIC
|
Isoler le bit à 1 de poids le plus faible et calculer le complément |
~x | (x - 1)
|
XOP.LZ.09 01 /7
|
T1MSKC
|
Masque inverse pour les zéros en queue (à droite) | ~x | (x + 1)
|
XOP.LZ.09 01 /4
|
TZMSK
|
Masque pour les zéros en queue (à droite) | ~x & (x - 1)
|
On notera que le support des extensions d'instructions signifie que le processeur est capable d'exécuter les instructions supportées dans un objectif de compatibilité logicielle. Le processeur peut très bien ne pas très efficace en faisant cela. Par exemple, les processeurs Excavator jusqu'à Zen 2 implémentent les instructions PEXT et PDEP à l'aide de microcode, ce qui fait que les instructions s'exécutent de façon beaucoup plus lente que le même comportement reproduit avec d'autres instructions[20] (Une méthode logicielle appelée "zp7" est, en fait, plus rapide sur ces machines)[21]. Pour une performance optimale, il est recommandé aux développeurs de compilateurs de choisir d'utiliser les instructions individuelles dans les extensions sur la base de profils de performance spécifiques à l'architecture plutôt que sur la disponibilité de l'extension.