Par Wilfrid et Michel
Du RTL àl'assembleur
Les fichiers de descriptions permettent
d'interfacer le back-end (le portage sur une cible particulière) avec
l'architecture interne de GCC. En interne GCC utilise un
méta-language nommé RTL. Il est
généré après l'optimisation
de l'arbre généré après
l'étape de front-end (parsing). Pour appréhender les subtilités de ce
langage il est utile de se plonger dans la documentation interne de GCC (GCC
internals).
Les fichiers de description contiennent
non pas des
codes RTL mais la correspondance de patterns RTL avec la sortie
assembleur qui va etre généré.
RTL est issu de la représentation d'arbre de
syntaxe abstraite, modifié par divers passages dans le
'middle-end' de GCC. Puis Il est converti en langage assembleur pour la
cible préalablement choisie. GCC emploie actuellement la
forme RTL pour effectuer une partie de son travail
d'optimisation. RTL est habituellement écrit sous une
forme qui ressemble à une expression de LISP.
Une liste RTL est composée de 5 types d’objets :
- entiers : Les entiers sont à comprendre au sens des types du C (int), c'est à dire des nombres entiers.
- entiers larges : Les entiers larges sont également à comprendre au sens des types de C, mais ils ont une taille supérieure aux entiers précédents.
- chaînes de caractères : Les chaînes de caractères sont identiques aux char * du C. Mais en RTL, une chaîne de caractères ne peut être nulle (une chaîne vide est considérée comme un pointeur null : char temp[10]= ""; équivaut à char * temp=null;)
- les vecteurs : Les vecteurs contiennent un nombre arbitraire d'expressions. Et comme pour les chaînes de caractères, un vecteur avec un nombre nul d'éléments n'est pas possible. Il correspond dans ce cas là à un pointeur nul.
- expressions : Les expressions RTL notée RTX sont des structures C, mais sont généralement des pointeurs sur des typedef nommés rtx.
Langage RTL, les expressions
Les expressions sont classées par leurs codes (notés RTX
pour "RTL eXpression"). Les codes que l'on a à disposition sont
énumérés dans le fichier rtl.def. Ils sont
équivalents à des énumérations du C. De
plus, les codes sont indépendants de la cible. Des macros de
manipulation de codes sont disponibles :
GET_CODE (x) pour l'extraction.
PUT_Code (x, newcode) pour l'insertion de nouveau code.
Le code RTX détermine le nombre et le type des opérandes
qui sont rattachés à l'expression. Un de ces
inconvénients est que, contrairement au Lisp, nous ne pouvons
pas déterminer la nature des opérandes d'un simple coup
d'oeil. Il faut d'abord déterminer le code RTX, puis en
déduire leur nature.
Voici un exemple d'un code RTX :
(set:SI (reg:SI 0) (plus:SI (reg:SI 1) (reg:SI 2)))
Cette expression représente l'addition du registre 1 au registre 2, et le stockage du résultat dans le registre 0.
(define_insn "addsi_regs"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "register_operand" "r")))]
""
"addn %2, %0"
)
Voici le pattern correspondant à
l'instruction décrite précédemment. On trouve en
fin d'expression le code assembleur qui sera
généré. Dans le cas de l'exemple présent:
addn r1, r2
Les patterns et le code assembleur à générer sont disponibles dans les fichiers ".md"
Le RTL produit par GCC est unique pour chaque
processeur cible, car faisant intervenir des optimisations propres
à chaque architecture. Cependant, la signification du RTL est
plus ou moins indépendante de la cible : il serait
habituellement possible de lire et comprendre un morceau de RTL sans
savoir pour quel processeur il a été produit. De
même, la signification du RTL ne dépend pas habituellement
du langage de haut niveau original du programme.
Lors de la compilation de GCC pour une cible
particulière celui-ci requiere qu'un autre ensemble de
composants soit présents et prets pour la cile, les binutils.