Obligement - L'Amiga au maximum

Vendredi 06 juin 2025 - 12:23  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Dossier : PowerPC, une architecture singulière
(Article écrit par Mathias Parnaudeau - mars 2010)


Il existe de nombreuses architectures de processeurs mais peu d'entre elles sont connues : l'idée commune est que les ordinateurs sont basés sur x86 et que les téléphones mobiles sont basés sur ARM. C'est vrai mais quoi d'autre ? Personne ne sait que certains décodeurs ou la PSP de Sony contiennent un MIPS. Certains se souviennent que la précédente génération d'ordinateurs Apple disposait de processeurs PowerPC mais ignorent que ceux-ci équipent toutes les consoles de nouvelle génération et sont utilisés dans l'aéronautique, la défense, les réseaux, les serveurs...

Même si ces exemples prouvent que le PowerPC a de solides arguments en faveur d'une utilisation sérieuse, nous savons que dans les industries et les marchés de consommation, les technologies ne sont pas toujours choisies parce qu'elles sont les meilleures mais parce qu'elles sont moins chères ou mieux vendues. Dans les entreprises, il y a aussi des raisons historiques qui font que les ingénieurs restent avec les anciennes technologies modernisées, redoutés par un saut technologique.

Quoi qu'il en soit, le PowerPC mérite d'être connu car plus que des qualités, il possède également des fonctionnalités spécifiques qui seront décrites dans cet article. Je voudrais souligner les points forts du PowerPC et ce qui le rend différent. Je montrerai d'abord en quoi c'est moderne, épuré... et ensuite les instructions montreront ce qui est singulier, avec des faits techniques bien sûr. Des comparaisons seront faites avec d'autres architectures RISC comme ARM et MIPS.

Un autre objectif est de donner des clés pour améliorer la programmation du PowerPC tout en comprenant comment fonctionne le processeur et ce que font les compilateurs.

Bases de l'architecture

Même si elle est héritée de l'architecture POWER, elle avait été conçue comme une architecture nouvelle et épurée au début des années 1990 bénéficiant de dizaines d'années d'évolution des processeurs. Le PowerPC est un processeur RISC dont tous les concepts majeurs ont été spécifiés et implémentés dès le départ :
  • Correction des instructions de 32 bits de large pour faciliter leur décodage.
  • Modèle de magasin de chargement ("load-store"), toutes les opérations sont effectuées dans des registres.
  • Grand nombre de registres (32 registres à usage général et 32 registres à virgule flottante).
  • Instructions de chargement atomique (ou exclusives) à utiliser dans un contexte multicoeur.
  • Commande gros boutiste avec possibilité de travailler en petit boutiste.
  • Architecture 64 bits avec le comportement des instructions spécifié pour ce mode.
  • Aucun rôle spécifique pour les registres à usage général (r1 utilisé comme pointeur de pile est un choix ABI, pas un choix d'architecture).
  • Modèle MMU non défini, il est spécifique à l'implémentation avec globalement deux modèles à utiliser dans des appareils ou des serveurs intégrés.
Par rapport à d'autres architectures, on voit qu'il y a 20 ans, ils ont réfléchi à des choix importants qui sont toujours là. Pour autant, l'architecture n'est pas figée, elle évolue et est désormais pilotée par le comité Power.org. Il est décrit dans les documents PowerISA dont la dernière version (2.06) a été publiée en janvier 2010 avec des fonctionnalités avancées comme la virtualisation pour le modèle embarqué.

Comparons quelques points avec une autre architecture dite RISC, ARM qui est très populaire, elle est notamment embarquée dans tous les téléphones mobiles, choisie au départ pour sa faible consommation, qu'elle tend à ajouter sans cesse de nouvelles fonctionnalités. En 2010, il n'existe toujours pas de processeurs ARM 64 bits et le multicoeur arrive avec le Cortex-A9 (même s'il y avait auparavant une implémentation multicoeur avec l'ARM11MP).

Pour donner quelques exemples et prouver la validité des choix PowerPC initiaux, ARMv5 a ajouté une instruction pour compter les zéros non significatifs (clz) mais de nombreuses autres fonctionnalités ont été ajoutées avec ARMv6 en 2002 : la gestion du boutisme ("setend" pour passer d'un mode à l'autre), l'accès exclusif aux données (ldrex/strex), une première implémentation SIMD (VFP, améliorée avec NEON sur ARMv7), des ASID pour les processus, un compteur de cycles (CCNT) qui n'est que de 32 bits... Le cache L2 est devenu un standard avec ARMv7. Pour tous ces exemples, on voit que le PowerPC vit avec ces fonctionnalités depuis le début !

Alors, en quoi le PowerPC est singulier ?

Nous nous concentrerons sur le jeu d'instructions, qui est la partie la plus visible et la plus importante pour les programmeurs d'applications. Nous expliquerons également comment ces propriétés sont gérées par les compilateurs, avec des exemples pour illustrer comment ces instructions peuvent être utilisées. Enfin, lorsque cela est possible et parce qu'il est toujours intéressant de comparer, une note sera donnée sur d'autres architectures.

# Prédiction de branchement statique

Dans les programmes, les branchements doivent être évités car c'est un cauchemar pour le pipeline. C'est pourquoi les processeurs disposent parfois de mécanismes pour améliorer la prédiction dynamique avec des caches spécifiques, etc. Pour la prédiction de branchement statique, le PowerPC a quelques règles de base :
  • Une branche avant est supposée ne pas être prise.
  • Une branche arrière est supposée être prise.
Le but est d'améliorer l'efficacité des boucles (en plus d'exécuter les instructions qui sont dans le cache). Parfois, ces comportements par défaut ne sont pas souhaités. C'est pourquoi l'architecture PowerPC a introduit un bit dans les codes opérations des instructions de branchement pour inverser la prédiction par défaut, ce bit est mentionné avec le signe "+" mis en suffixe.

Il est difficile de savoir quelle est la politique d'un compilateur et quand le bit inverse est utilisé. Ceci est un exemple simple dans la fonction main :

    if (argc == 1){
        printf("Aucun argument\n");
    }else{
        printf("%d arguments\n", argc - 1);
    }

Dans ce cas, les deux parties étant après la comparaison, argc est évalué avec la prédiction qu'il n'est pas égal à 1. Un deuxième exemple montre (dans le code assembleur généré par le compilateur) que le bloc est pris mais la raison est peut-être là. n'est qu'un bloc :

    handle = fopen(...);
    if (handle){
        /* Du code ici */
    }

Sauf dans les boucles, il est difficile pour un compilateur de savoir quel bloc d'une instruction if/then doit être choisi en priorité. Pour vous donner une idée, GCC fournit une fonction intégrée appelée "__builtin_expect" qui est parfois utilisée dans des macros plus connues :

#define likely(x)   __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)

Ainsi, le compilateur peut réorganiser le code en déplaçant des blocs ou en inversant la condition de branchement (par exemple "eq" devenant "ne"). Il pourrait également utiliser le bit inverse.

Sur ARM, une fonctionnalité intéressante existe pour éviter les branchements : les instructions conditionnelles. Cela signifie que les instructions sont suffixées avec la condition de branchement et sont exécutées en fonction de l'évaluation de cette condition :

    cmp   r0, r2       @ le registre des conditions est mis à jour ici
    moveq r2, #0       @ si r0 est égal à r2 alors r2 est effacé
    addgt r3, r3, r0   @ si r0 est supérieur à r2, r0 est ajouté à r3

# Registre de condition (CR)

Le registre de condition est en fait un ensemble de huit champs de bits qui agissent indépendamment comme huit registres de condition, nommés de cr0 à cr7 ! Lors d'une comparaison, l'instruction code le champ qu'elle souhaite mettre à jour, par exemple :

    cmpwi cr7, r30, 0
    ble   cr7, .L4

Ici, cr7 est utilisé, donc le champ par défaut cr0 est conservé. Dans les boucles imbriquées, cela peut être très utile.

De plus, il est possible d'appliquer des opérations logiques (and, or, nand, eqv...) entre les bits du registre de condition ! Par exemple, l'expression "do ... while ((a == 6) || (b == c))" pourrait s'écrire ainsi :

    cmpwi cr7, r3, 6
    cmpw  cr0, r4, r5
    cror  cr7[eq], cr0[eq], cr7[eq]
    bne   cr7, loop_start

Ainsi, cela a permis d'économiser une branche conditionnelle, qui a toujours un coût en raison de la prédiction de branchement. Avec des comparaisons et des "cror" exécutés suffisamment tôt, la prédiction est calculée avant l'exécution du branchement, ce qui sauve à nouveau les cycles.

Voici un exemple donné pour effectuer un changement avec un minimum de branches conditionnelles : wall.riscom.net/books/proc/ppc/cwg/code1.html. Il n'y a qu'un seul branchement conditionnel pour un commutateur ("switch") avec quatre cas allant vers le même code.

Autre point qui n'est pas surprenant avec la philosophie RISC ("ne faites pas ce qui n'est pas nécessaire") : le registre de conditions n'est mis à jour que lorsque cela est demandé. La syntaxe est un point placé comme suffixe du nom de l'instruction.

Sur MIPS, il n'y a pas de registre de conditions ! Dans ce cas, les branchements se font en comparant directement les valeurs des registres, par exemple "bge $4, $5, label" provoquera un saut vers le label si le contenu du registre $4 est supérieur ou égal à celui du registre $5.

# Opérations sur les bits

C'est l'une des plus grandes forces du PowerPC qui fournit de nombreuses instructions puissantes pour travailler sur les bits. Par exemple, "andc" effectue en un cycle une opération AND où le masque est un complément d'une valeur qui est une opération habituelle dans les programmes :

    res = value & ~mask;

Sinon, les opérations sur les bits se font avec quelques instructions de la famille "rotation left and mask". "rlwinm" a cette syntaxe :

    rlwinm rA, rB, n, mB, mE

Il décale la valeur de rB de n bits et crée un masque allant du bit mB à mE. Ensuite, il applique une opération AND sur rB avec le masque donné et stocke les résultats dans rA. Cela permet simplement de décaler ou de faire pivoter une valeur mais aussi de conserver ou d'effacer un ensemble de bits. Une autre instruction est "rlwimi" dont le nom décrit ce qu'elle fait : faire pivoter le mot vers la gauche immédiatement puis insérer un masque. Avec une logique similaire à celle de l'exemple précédent, il insère un groupe de bits d'un mot dans un mot destination ! Un exemple typique est la gestion d'un mot contenant une valeur RVB, chaque composant pouvant être mis à jour indépendamment.

L'instruction "rlwimi" est également utilisée par les compilateurs pour écrire des bits dans un champ de bits (en un cycle). Même si ce type de données est connu pour poser des problèmes de portabilité et doit être évité pour les interfaces externes, il peut s'avérer très utile et évite de nombreuses opérations : créer une valeur RVB ne nécessite que trois instructions.

Comme d'habitude pour un processeur RISC, la rotation à droite n'a pas d'instruction spécifique car elle n'est pas nécessaire, il s'agit en fait d'une opération de rotation à gauche, ce qui est tout à fait logique. Pour faire pivoter une valeur de 5 bits vers la droite, utilisez :

    rlwinm r3, r4, 27, 0, 31

Et bien sûr, le mnémonique "rotrwi" existe pour plus de commodité. Pour faire la même chose que le cas précédent avec une syntaxe plus explicite, il suffit d'écrire :

    rotrwi r3, r4, 5

# Load-store avec octet inversé

Même si le PowerPC peut être utilisé en mode petit boutiste, cela n'arrive pratiquement jamais. Mais il fournit des instructions puissantes pour charger et stocker des données 16 bits et 32 bits en échangeant les octets. Cela évite par exemple de charger une valeur dans un registre et d'appeler des opérations supplémentaires pour arriver au même résultat.

Dans ce cas, si le compilateur ne fournit pas de composant intégré, il doit être utilisé en assembleur. Une fonction à jour (utilisable avec GCC 4.3.0 ou supérieur) est fournie sur un excellent site à l'adresse hardwarebug.org/2008/10/25/gcc-inline-asm-annoyance/ :

static inline uint32_t load_le32(const uint32_t *p)
{
    uint32_t v;
    asm ("lwbrx %0, %y1" : "=r"(v) : "Z"(*p));
    return v;
}

Le qualificatif "Z" étant récent, une autre syntaxe pourrait être :

static inline uint32_t load_le32(const uint32_t *p)
{
    uint32_t v;
    asm ("lwbrx %0, 0, %1" : "=r"(v) : "r"(p));
    return v;
}

Sinon, si un échange d'octets doit être effectué sans accès mémoire, les versions récentes de GCC fournissent une fonction intégrée appelée "__builtin_bswap32". S'il n'est pas disponible, il peut être codé ainsi pour échanger des octets dans le registre r3, avec quatre instructions :

    rotlwi  r0, r3, 8
    rlwimi  r0, r3, 24, 0, 7
    rlwimi  r0, r3, 24, 16, 23
    mr      r3, r0

Sur ARM, une instruction swap (rev) est apparue tardivement (ARMv6) et sur MIPS, elle est tout simplement absente.

# Registre de comptage (CTR)

Ce compteur permet de boucler un nombre de fois donné sans utiliser de GPR et sans modifier le registre de condition (CR). Par exemple, pour une boucle qui doit être exécutée 100 fois :

    mtctr r3, 99
label:
    
    bdnz label

Ce type de boucle peut bénéficier d'une meilleure prédiction de branchement. Et il est facile à lire et facilement utilisable par les compilateurs.

Une autre propriété curieuse mais intéressante est la possibilité d'utiliser CR pour accéder à l'adresse qu'il contient. Cela se fait par l'instruction "bcctr", comme "bclr" utilise LR. Il n'y a pas de registre PC (aussi appelé pointeur d'instruction) sur PowerPC, il n'est pas possible de jouer directement avec. Cela peut préserver la prédiction de branche. Mais un saut vers une adresse calculée peut être effectué avec CTR qui peut être utilisé dans l'instruction switch avec une table contenant des pointeurs vers lesquels accéder.

Dans cet article, nous avons vu cinq fonctionnalités du jeu d'instructions PowerPC qui le différencient des autres architectures. Même si votre code n'est pas écrit en assembleur, nous savons comprendre comment ces instructions et mécanismes peuvent être utilisés avec un langage de niveau supérieur comme le C, avec l'aide du compilateur.

Comme le PowerPC est moderne, la recherche sur l'optimisation était peut-être moins intensive par rapport à d'autres architectures, mais au cours des dernières années, le travail sur d'autres fonctionnalités intéressantes comme AltiVec et les instructions de cache (voir www.freevec.org) a montré que le PowerPC était peut-être envisagé à un moment donné, sans savoir quel était son plein potentiel.

Espérons que le PowerPC soit encore au point, avec de nouveaux processeurs IBM (POWER7, 476FP...) et Freescale (famille QorIQ) qui sont de nouvelles occasions de diffuser et de faire connaître cette architecture puissante et singulière.


[Retour en haut] / [Retour aux articles]