Le monde ARM est un peu désordonné.
Pour les programmeurs C, les choses sont simples : toutes les architectures ARM offrent un modèle de programmation régulier, 32 bits avec adressage plat. Tant que vous restez avec le code source C, la seule différence que vous pouvez voir concerne l'adressage et les performances. La plupart des processeurs ARM (même les anciens modèles) peuvent être à la fois big-endian et little-endian ; le choix est alors fait par la carte logique et le système d'exploitation. Un bon code C est neutre endienne La neutralité endienne est une bonne chose pour la fiabilité et la maintenabilité, mais aussi pour les performances : un code non neutre est un code qui accède aux mêmes données par des pointeurs de tailles différentes, ce qui nuit aux règles strictes d'aliasing que le compilateur utilise pour optimiser le code.
La situation est tout à fait différente si l'on considère binaire la compatibilité (c'est-à-dire la réutilisation d'un code qui a été compilé une fois) :
- Il existe plusieurs jeux d'instructions :
- le jeu d'instructions ARM original avec un compteur de programme de 26 bits (très ancien, très peu susceptible d'être rencontré de nos jours)
- le jeu d'instructions ARM avec un compteur de programme de 32 bits (souvent appelé "code ARM")
- le jeu d'instructions Thumb (opcodes simplifiés de 16 bits)
- le jeu d'instructions Thumb-2 (Thumb avec extensions)
Un processeur donné peut mettre en œuvre plusieurs jeux d'instructions. Le processeur le plus récent qui ne connaît que le code ARM est le StrongARM, un représentant de l'ARMv4 qui est déjà assez vieux (15 ans). L'ARM7TDMI (architecture ARMv4T) connaît à la fois ARM et Thumb, comme presque tous les systèmes ARM ultérieurs, à l'exception du Cortex-M. Les codes ARM et Thumb peuvent être mélangés au sein d'une même application, à condition d'insérer la colle appropriée là où les conventions changent ; c'est ce que l'on appelle l'interfonctionnement des pouces et peut être géré automatiquement par le compilateur C.
Le Cortex-M0 ne connaît que les instructions Thumb. Il connaît quelques extensions, car dans les processeurs ARM "normaux", le système d'exploitation doit utiliser du code ARM (pour gérer les interruptions) ; ainsi, le Cortex-M0 connaît quelques éléments Thumb pour le système d'exploitation. Cela n'a pas d'importance pour le code d'application.
Les autres Cortex-M ne connaissent que le pouce 2. Le pouce 2 est principalement rétrocompatible avec Thumb, au moins au niveau de l'assemblage.
- Certaines architectures ajoutent des instructions supplémentaires.
Ainsi, si un code est compilé avec un commutateur de compilateur indiquant que c'est pour un ARMv6, alors le compilateur peut utiliser une des quelques instructions avec l'ARMv6 a mais pas l'ARMv5. C'est une situation courante, rencontrée sur presque toutes les plateformes : par exemple, si vous compilez du code C sur un PC, avec GCC, en utilisant l'option -march=core2
le binaire résultant peut ne pas fonctionner sur un ancien processeur Pentium.
- Il existe plusieurs conventions d'appel.
La convention d'appel est l'ensemble des règles qui spécifient comment les fonctions échangent des paramètres et des valeurs de retour. Le processeur ne connaît que ses registres, et n'a aucune notion de pile. La convention d'appel indique dans quels registres vont les paramètres, et comment ils sont codés (par exemple, s'il y a un paramètre de retour dans le processeur). char
il est placé dans les 8 bits inférieurs d'un registre, mais l'appelant est-il censé effacer/étendre les 24 bits supérieurs, ou non ?) Il décrit la structure et l'alignement de la pile. Il normalise les conditions d'alignement et le remplissage des champs de la structure.
Il existe deux conventions principales pour ARM, appelées ATPCS (ancienne) et AAPCS (nouvelle). Elles sont assez différentes en ce qui concerne les valeurs à virgule flottante. Pour les paramètres entiers, elles sont pratiquement identiques (mais AAPCS exige un alignement de pile plus strict). Bien entendu, les conventions varient en fonction du jeu d'instructions et de la présence de l'interfonctionnement du Pouce.
Dans certains cas, il est possible d'avoir un code binaire conforme à la fois à l'ATPCS et à l'AAPCS, mais ce n'est pas fiable et il n'y a pas d'avertissement en cas d'incompatibilité. En résumé, il est impossible d'obtenir une véritable compatibilité binaire entre des systèmes qui utilisent des conventions d'appel distinctes.
- Il existe des coprocesseurs optionnels.
L'architecture ARM peut être étendue avec des éléments optionnels, qui ajoutent leurs propres instructions au jeu d'instructions de base. Le FPU est un tel coprocesseur optionnel (et il est très rarement rencontré en pratique). Un autre coprocesseur est NEON, un jeu d'instructions SIMD que l'on trouve sur certains des processeurs ARM les plus récents.
Le code qui utilise un coprocesseur ne s'exécutera pas sur un processeur qui ne dispose pas de ce coprocesseur, à moins que le système d'exploitation ne piège les opcodes correspondants et n'émule le coprocesseur en logiciel (c'est plus ou moins ce qui se passe avec les arguments à virgule flottante lorsqu'on utilise la convention d'appel ATPCS, et c'est lent ).
En résumé, si vous avez du code C, recompilez-le. N'essayez pas de réutiliser du code compilé pour une autre architecture ou un autre système.