Convention d'appel

En informatique, une convention d'appel est un ensemble de règles qui décrit comment une fonction reçoit des paramètres et renvoie un résultat. Cela inclut des choix techniques sur où et comment les informations sont envoyées à la fonction appelée, et où et comment le résultat est renvoyé. En général, ces transferts se font soit par des registres, soit en utilisant une "pile" spéciale qui stocke temporairement des informations.

Il y a aussi des règles pour déterminer qui, entre l'appelant (celui qui appelle la fonction) et l'appelé (la fonction elle-même), est responsable de préparer et nettoyer après l'appel. Respecter la convention d'appel est essentiel pour que le programme fonctionne correctement et sans erreurs.

Introduction

[modifier | modifier le code]

Les conventions d’appel font généralement partie de l’interface binaire d’une application (ABI). Elles servent de contrat entre celui qui appelle la fonction et la fonction elle-même.

Concepts connexes

[modifier | modifier le code]

Les noms et significations des paramètres et des valeurs de retour sont définis dans l'interface de programmation d'application (API), qui est liée mais distincte de l'ABI et de la convention d'appel. Les noms des éléments à l'intérieur des structures et des objets transmis font aussi partie de l'API, pas de l'ABI. Parfois, les API incluent des mots-clés pour indiquer la convention d'appel des fonctions.

Les conventions d’appel n’incluent généralement pas d’informations sur la gestion de la durée de vie des structures et objets alloués dynamiquement. D'autres documentations complémentaires peuvent indiquer à qui incombe la responsabilité de libérer la mémoire allouée.

Il est peu probable que les conventions d'appel spécifient la disposition des éléments dans les structures et les objets, comme l'ordre des octets ou le regroupement des structures.

Pour certains langages, la convention d'appel inclut des détails sur la gestion des erreurs ou des exceptions (par exemple Go, Java ) et pour d'autres, ce n'est pas le cas (par exemple C++ ).

Pour les appels de procédure à distance, il existe un concept analogue appelé Marshalling.

Les conventions d'appel peuvent être influencées par la stratégie d'évaluation d'un langage de programmation, mais elles ne sont généralement pas considérées comme faisant partie du langage lui-même. La stratégie d'évaluation appartient à un niveau d'abstraction plus élevé, défini comme une caractéristique du langage, tandis que les conventions d'appel relèvent des détails d'implémentation à bas niveau du compilateur.

Différentes conventions d’appel

[modifier | modifier le code]

Les conventions d’appel peuvent différer dans :

  • Où sont placés les paramètres. Les options incluent des registres, sur la pile d'appels, un mélange des deux ou dans d'autres structures de mémoire.
  • L'ordre dans lequel les paramètres sont passés. Les options incluent l'ordre de gauche à droite, ou de droite à gauche, ou quelque chose de plus complexe.
  • Comment les fonctions qui prennent un nombre variable d'arguments ( fonctions variadiques ) sont gérées. Les options incluent simplement les éléments passés dans l'ordre (en supposant que le premier paramètre soit dans une position évidente) ou les parties variables d'un tableau.
  • Comment les valeurs de retour sont transmises de l'appelé à l'appelant. Les options incluent sur la pile, dans un registre ou une référence à quelque chose alloué sur le tas.
  • La longueur ou la complexité des valeurs traitées, peut-être en les répartissant sur plusieurs registres, dans le cadre de la pile ou en référence à la mémoire.
  • Quels registres sont garantis d'avoir la même valeur lorsque l'appelé revient que lorsqu'il a été appelé. Ces registres sont dits sauvegardés ou préservés, ils ne sont donc pas volatils.
  • Comment la tâche de configuration et de nettoyage après un appel de fonction est divisée entre l'appelant et l'appelé. En particulier, comment la trame de pile est restaurée afin que l'appelant puisse continuer une fois que l'appelé a terminé.
  • Si et comment les métadonnées décrivant les arguments sont transmises
  • Où la valeur précédente du pointeur de trame est stockée, qui est utilisée pour restaurer la trame de pile lorsque la sous-routine se termine. Les options sont incluses dans la pile d'appels ou dans un registre spécifique. Parfois, les pointeurs de trame ne sont pas utilisés du tout[1].
  • Où sont placés les liens de portée statique pour l'accès aux données non locales de la routine (généralement à une ou plusieurs positions dans le cadre de la pile, mais parfois dans un registre général ou, pour certaines architectures, dans des registres à usage spécial)
  • Pour les langages orientés objet, comment l'objet de la fonction est référencé

Conventions d'appel au sein d'une même plateforme

[modifier | modifier le code]

Sur une même plateforme, il peut exister plusieurs conventions d'appel, et une combinaison spécifique de plateforme et de langage peut proposer différents choix. Cela permet d’optimiser les performances, de s’adapter aux conventions d'autres langages populaires ou de respecter les exigences imposées par diverses plateformes informatiques.

De nombreuses architectures utilisent une seule convention d'appel, souvent recommandée par l'architecte du système. Pour les architectures RISC comme SPARC, MIPS et RISC-V, les registres sont nommés en fonction de cette convention. Par exemple, sur MIPS, les registres $4 à $7 sont nommés $a0 à $a3 pour indiquer qu'ils servent à passer des paramètres selon la convention d'appel standard. (Les processeurs RISC disposent de nombreux registres à usage général identiques, donc il n'y a généralement pas de raison matérielle de les nommer autrement que par des numéros.)

La convention d'appel d'un langage peut être différente de celle de la plateforme, du système d'exploitation ou des bibliothèques auxquelles il est lié. Par exemple, sous Windows 32 bits, les appels système utilisent la convention d'appel stdcall, tandis que de nombreux programmes C utilisent la convention cdecl. Pour gérer ces différences, les compilateurs permettent souvent d'ajouter des mots-clés aux déclarations de fonction pour spécifier la convention d'appel à utiliser. Cela permet au compilateur de générer le code nécessaire pour appeler les fonctions correctement.

Certains langages permettent de définir explicitement la convention d'appel d'une fonction, tandis que d'autres imposent une convention sans la rendre visible aux utilisateurs, de sorte que le programmeur n’a généralement pas à s'en soucier.

Architectures

[modifier | modifier le code]

x86 (32 bits)

[modifier | modifier le code]
push EAX      ; passer le contenu du registre
 push dword [EBP+20] ; passer une variable mémoire (syntaxe FASM/TASM)
 push 3       ; pass une constante
 call calc      ; La valeur retournée est dans EAX

L'architecture x86 en 32 bits utilise de nombreuses conventions d'appel. Étant donné le faible nombre de registres disponibles et la priorité historique donnée à la simplicité et à la compacité du code, de nombreuses conventions d'appel x86 passent les arguments via la pile, et renvoient la valeur de retour (ou un pointeur vers celle-ci) dans un registre. Certaines conventions passent les premiers paramètres dans des registres, ce qui peut améliorer les performances, notamment pour les routines simples et courtes appelées fréquemment (celles qui n'appellent pas d'autres fonctions). Exemple d'appel :

calc:
 push EBP      ; Sauvegarder l'ancien frame pointer
 mov EBP,ESP     ; Avoir un nouveau pointeur de frame
 sub ESP,localsize  ; Reserver de l'espace pour les variables locales
 .
 .          ; Faire des calculs, laisser le resultat dans EAX
 .
 mov ESP,EBP     ; Liberer l'espace utilisé par les variables locales
 pop EBP       ; Restaurer l'ancien frame pointer
 ret paramsize    ; Liberer l'espace utilisé par les parametres et return

Structure d'appel typique : (certaines ou toutes les instructions ci-dessous, sauf `ret`, peuvent être optimisées dans des procédures simples). Certaines conventions laissent l'espace pour les paramètres alloué, en utilisant `ret` simple au lieu de `ret imm16`. Dans ce cas, l'appelant pourrait utiliser `add esp, 12` dans cet exemple, ou ajuster autrement ESP.

 *         1130 subroutine example
   ENT SUB    Declare "SUB" an external entry point
 SUB DC  0     Reserved word at entry point, conventionally coded "DC *-*"
 *          Subroutine code begins here
 *          If there were arguments the addresses can be loaded indirectly from the return address
   LDX I 1 SUB   Load X1 with the address of the first argument (for example)
 ...
 *          Return sequence
   LD   RES   Load integer result into ACC
 *          If no arguments were provided, indirect branch to the stored return address
   B  I  SUB   If no arguments were provided
   END SUB

La version 64 bits de l'architecture x86, appelée x86-64, AMD64 ou Intel 64, utilise deux séquences d'appel courantes. La première, définie par Microsoft, est utilisée sous Windows ; la seconde, spécifiée dans l'ABI AMD64 System V, est utilisée par les systèmes de type Unix et, avec quelques modifications, par OpenVMS. Étant donné que le x86-64 dispose de plus de registres à usage général que le x86 en 32 bits, les deux conventions transmettent certains arguments directement dans des registres.

La convention d'appel ARM 32 bits standard alloue les 16 registres à usage général comme suit :

  • r15 : Compteur de programme (conformément à la spécification du jeu d'instructions).
  • r14 : Registre de liens. L'instruction BL, utilisée dans un appel de sous-programme, stocke l'adresse de retour dans ce registre.
  • r13 : pointeur de pile. Les instructions Push/Pop en mode de fonctionnement « Thumb » utilisent uniquement ce registre.
  • r12 : registre de travail d'appel intra-procédure.
  • r4 à r11 : Variables locales.
  • r0 à r3 : valeurs d’argument transmises à une sous-routine et résultats renvoyés par une sous-routine.

Si la valeur renvoyée est trop grande pour tenir dans les registres r0 à r3, ou si sa taille ne peut pas être déterminée statiquement lors de la compilation, l'appelant doit allouer de l'espace pour cette valeur pendant l'exécution et passer un pointeur vers cet espace dans r0.

Les sous-routines doivent conserver le contenu des registres r4 à r11 ainsi que le pointeur de pile. Cela peut être fait en les enregistrant dans la pile au début de la fonction (prologue), puis en les utilisant comme espace de travail, et enfin en les restaurant à partir de la pile à la fin de la fonction (épilogue). De plus, les sous-routines qui appellent d'autres sous-routines doivent sauvegarder l'adresse de retour dans le registre de liaison r14, en la plaçant dans la pile avant l'appel. Toutefois, ces sous-routines n'ont pas besoin de renvoyer cette valeur à r14 ; elles doivent simplement la charger dans r15, le compteur de programme, pour revenir.

La convention d'appel ARM exige l'utilisation d'une pile entièrement descendante. De plus, le pointeur de pile doit toujours être aligné sur 4 octets et sur 8 octets lors d'un appel de fonction avec une interface publique.

Cette convention d'appel amène une sous-routine ARM « typique » à :

  • Dans le prologue, poussez r4 vers r11 vers la pile, et poussez l'adresse de retour dans r14 vers la pile (cela peut être fait avec une seule instruction STM) ;
  • Copiez tous les arguments passés (de r0 à r3) dans les registres de travail locaux (r4 à r11) ;
  • Allouer d’autres variables locales aux registres de travail locaux restants (r4 à r11) ;
  • Effectuez des calculs et appelez d’autres sous-routines si nécessaire en utilisant BL, en supposant que r0 à r3, r12 et r14 ne seront pas préservés ;
  • Mettre le résultat dans r0 ;
  • Dans l'épilogue, tirez r4 à r11 de la pile et tirez l'adresse de retour vers le compteur de programme r15. Cela peut être fait avec une seule instruction LDM.

La convention d'appel ARM 64 bits ( AArch64) alloue les 31 registres à usage général comme suit [2]:

  • x31 (SP) : pointeur de pile ou registre zéro, selon le contexte.
  • x30 (LR) : Registre de lien de procédure, utilisé pour revenir des sous-routines.
  • x29 (FP) : Pointeur de cadre.
  • x19 à x28 : appelé enregistré.
  • x18 (PR) : Registre de la plateforme. Utilisé à des fins spécifiques au système d'exploitation ou comme registre supplémentaire enregistré par l'appelant.
  • x16 (IP0) et x17 (IP1) : registres de travail intra-appel de procédure.
  • x9 à x15 : variables locales, appelant enregistré.
  • x8 (XR) : Adresse de valeur de retour indirecte.
  • x0 à x7 : valeurs d’argument transmises et résultats renvoyés par une sous-routine.

Tous les registres commençant par x ont un registre 32 bits correspondant préfixé par w . Ainsi, un x0 32 bits est appelé w0.

De même, les 32 registres à virgule flottante sont alloués comme suit[3] :

  • v0 à v7 : valeurs d’argument transmises et résultats renvoyés par une sous-routine.
  • v8 à v15 : l'appelé est enregistré, mais seuls les 64 bits inférieurs doivent être conservés.
  • v16 à v31 : Variables locales, appelant enregistré.

Norme ISA RISC-V

[modifier | modifier le code]

RISC-V a une convention d'appel qui se décline en deux versions, avec ou sans support pour les nombres à virgule flottante. Elle transmet des arguments dans des registres chaque fois que cela est possible.

POWER, PowerPC et Power ISA

[modifier | modifier le code]

Les architectures POWER, PowerPC et Power ISA disposent d'un grand nombre de registres, ce qui permet à la plupart des fonctions de transmettre tous les arguments dans les registres lors des appels à un seul niveau. Les arguments supplémentaires sont passés sur la pile, et de l'espace pour les arguments basés sur des registres est toujours alloué sur la pile pour faciliter la fonction appelée, notamment en cas d'appels à plusieurs niveaux (récursifs ou autres), où les registres doivent être sauvegardés. Cela est également utile dans les fonctions variadiques, comme `printf()`, où les arguments doivent être accessibles sous forme de tableau. Une seule convention d'appel est utilisée pour tous les langages procéduraux.

Les instructions de branchement et de liaison stockent l'adresse de retour dans un registre de liaison spécial, distinct des registres à usage général. Une routine revient à son appelant en utilisant une instruction de branchement qui prend le registre de liaison comme adresse de destination. Les routines feuille n'ont pas besoin de sauvegarder ou de restaurer ce registre, tandis que les routines non feuille doivent sauvegarder l'adresse de retour avant d'appeler une autre routine et la restaurer avant de revenir. Cela se fait en utilisant l'instruction Move From Special Purpose Register pour copier le registre de liaison dans un registre à usage général, et si nécessaire, en le sauvegardant dans la pile. Pour restaurer, si l'adresse a été enregistrée dans la pile, on charge la valeur depuis la pile dans un registre à usage général, puis on utilise l'instruction Move To Special Purpose Register pour remettre cette valeur dans le registre de liaison.

Système de gestion des impulsions

[modifier | modifier le code]

L'ABI O32 est l'ABI la plus couramment utilisée, car c'est l'ABI System V d'origine pour MIPS. Elle est strictement basée sur la pile et ne dispose que de quatre registres ($a0 à $a3) pour passer des arguments. Cette limitation, ainsi qu'un ancien modèle à virgule flottante avec seulement 16 registres, ont favorisé le développement de nombreuses autres conventions d'appel. L'ABI a été établie en 1990 et n'a pas été mise à jour depuis 1994. Elle est définie uniquement pour les MIPS 32 bits, mais GCC a créé une version 64 bits appelée O64.

Pour les 64 bits, l'ABI N64 de Silicon Graphics est la plus couramment utilisée (sans lien avec la Nintendo 64). La principale amélioration est qu'elle permet de passer des arguments à l'aide de huit registres, et elle augmente également le nombre de registres à virgule flottante à 32. Il existe aussi une version ILP32 appelée N32, qui utilise des pointeurs de 32 bits pour un code plus compact, similaire à l'ABI x32. Les deux fonctionnent en mode 64 bits sur le processeur.

Quelques tentatives ont été réalisées pour remplacer O32 par un ABI 32 bits plus similaire à N32. Lors d'une conférence en 1995, le MIPS EABI a été présenté, dont la version 32 bits était assez semblable. L'EABI a ensuite inspiré MIPS Technologies à proposer une ABI « NUBI » plus radicale, qui utilise également les registres d'arguments pour renvoyer des valeurs. MIPS EABI est pris en charge par GCC, mais pas par LLVM, et aucun des deux ne prend en charge NUBI.

Pour les ABI O32 et N32/N64, l'adresse de retour est stockée dans le registre $ra. Cela est défini automatiquement lors de l'utilisation des instructions JAL (saut et liaison) ou JALR (registre de saut et de liaison). La pile grandit vers le bas.

L'architecture SPARC, contrairement à la plupart des architectures RISC, utilise des fenêtres de registre. Chaque fenêtre de registre comporte 24 registres : 8 registres « in » (%i0-%i7), 8 registres « local » (%l0-%l7) et 8 registres « out » (%o0-%o7). Les registres « in » sont utilisés pour transmettre des arguments à la fonction appelée, et tous les arguments supplémentaires doivent être placés sur la pile. Cependant, la fonction appelée alloue toujours de l'espace pour gérer un éventuel dépassement de la fenêtre de registre, des variables locales et, sur SPARC 32 bits, le renvoi d'une structure par valeur. Pour appeler une fonction, les arguments sont mis dans les registres « out » ; lorsque la fonction est appelée, ces registres deviennent les registres « in », permettant à la fonction appelée d'accéder aux arguments. À la fin de la fonction appelée, la valeur de retour est placée dans le premier registre « in », qui devient alors le premier registre « out » lors du retour de la fonction.

Le système V ABI[4], que suivent la plupart des systèmes modernes de type Unix, passe les six premiers arguments dans les registres « in » %i0 à %i5, réservant %i6 pour le pointeur de trame et %i7 pour l'adresse de retour.

IBM System/360 et successeurs

[modifier | modifier le code]

L'IBM System/360 est une autre architecture sans pile matérielle. Les exemples ci-dessous illustrent la convention d'appel utilisée par OS/360 et ses successeurs avant l'introduction de l'architecture z 64 bits ; d'autres systèmes d'exploitation pour System/360 peuvent avoir des conventions d'appel différentes.

Programme d'appel :

LA 1,ARGS Charger l'adresse de la liste des arguments
   L 15,=A(SUB) Charger l'adresse du sous-programme
   BALR 14,15 Branche vers la routine appelée 1
   ...
ARGS DC A(FIRST) Adresse du 1er argument
   DC A (SECOND)
   ...
   DC A(THIRD)+X'80000000' Dernier argument 2

Programme appelé :

SUB EQU * Ceci est le point d'entrée du sous-programme

Séquence d'entrée standard :

UTILISER *,15 3
   STM 14,12,12(13) Enregistrer les registres 4
   ST 13, SAVE+4 Enregistrer l'adresse de la zone de sauvegarde de l'appelant
   LA 12, Zones de sauvegarde de la chaîne SAVE
   ST 12,8(13)
   LR 13,12
   ...

Séquence de retour standard :

L 13, SAVE+45
   LM 14,12,12(13)
   L 15,RETVAL6
   BR 14 Retour à l'appelant
ENREGISTRER DS 18F Zone de sauvegarde 7

Remarques :

  1. L'instruction BALR stocke l'adresse de l'instruction suivante (adresse de retour) dans le registre spécifié par le premier argument — registre 14 — et se branche vers l'adresse du deuxième argument dans le registre 15.
  2. L'appelant transmet l'adresse d'une liste d'adresses d'arguments dans le registre 1. La dernière adresse a le bit d'ordre élevé défini pour indiquer la fin de la liste. Cela limite les programmes utilisant cette convention à l'adressage 31 bits.
  3. L'adresse de la routine appelée est dans le registre 15. Normalement, celui-ci est chargé dans un autre registre et le registre 15 n'est pas utilisé comme registre de base.
  4. L'instruction STM enregistre les registres 14, 15 et 0 à 12 dans une zone de 72 octets fournie par l'appelant, appelée zone de sauvegarde pointée par le registre 13. La routine appelée fournit sa propre zone de sauvegarde à l'usage des sous-routines qu'elle appelle ; l'adresse de cette zone est normalement conservée dans le registre 13 tout au long de la routine. Les instructions qui suivent STM mettent à jour les chaînes avant et arrière reliant cette zone de sauvegarde à la zone de sauvegarde de l'appelant.
  5. La séquence de retour restaure les registres de l'appelant.
  6. Le registre 15 est généralement utilisé pour transmettre une valeur de retour.
  7. Déclarer une savearea de manière statique dans la routine appelée la rend non réentrante et non récursive ; un programme réentrant utilise une savearea dynamique, acquise soit à partir du système d'exploitation et libérée au retour, soit dans la mémoire transmise par le programme appelant.

Dans l'ABI System/390 [5] et l'ABI z/Architecture [6] utilisés sous Linux :

  • Les registres 0 et 1 sont volatils
  • Les registres 2 et 3 sont utilisés pour le passage des paramètres et les valeurs de retour
  • Les registres 4 et 5 sont également utilisés pour le passage de paramètres
  • Le registre 6 est utilisé pour le passage des paramètres et doit être sauvegardé et restauré par l'appelé
  • Les registres 7 à 13 sont destinés à être utilisés par l'appelé et doivent être sauvegardés et restaurés par lui
  • Le registre 14 est utilisé pour l'adresse de retour
  • Le registre 15 est utilisé comme pointeur de pile
  • Les registres à virgule flottante 0 et 2 sont utilisés pour le passage des paramètres et les valeurs de retour
  • Les registres à virgule flottante 4 et 6 sont destinés à être utilisés par l'appelé et doivent être sauvegardés et restaurés par lui
  • Dans z/Architecture, les registres à virgule flottante 1, 3, 5 et 7 à 15 sont destinés à être utilisés par l'appelé
  • Le registre d'accès 0 est réservé à l'utilisation du système
  • Les registres d'accès 1 à 15 sont destinés à être utilisés par l'appelé

Des arguments supplémentaires sont passés sur la pile.

Registre Windows CE 5.0 gcc Renesas
R0 Valeurs de retour. Temporaire pour étendre les pseudo-instructions d'assemblage. Source/destination implicite pour les opérations 8/16 bits. Non conservé. Valeur de retour, l'appelant enregistre Variables/temporaires. Non garanti
R1.. R3 Sert de registres temporaires. Non conservé. L'appelant a sauvegardé le scratch. Adresse de structure (enregistrement de l'appelant, par défaut) Variables/temporaires. Non garanti
R4.. R7 Les quatre premiers mots des arguments entiers. La zone de construction d'arguments fournit un espace dans lequel les arguments R4 à R7 peuvent se déverser. Non conservé. Passage de paramètres, l'appelant enregistre Arguments. Non garanti.
R8.. R13 Sert de registres permanents. Conservé. L'appelé enregistre Variables/temporaires. Garanti.
R14 Pointeur de cadre par défaut. (R8-R13 peuvent également servir de pointeur de cadre et les routines feuille peuvent utiliser R1-R3 comme pointeur de cadre.) Préservé. Pointeur de trame, FP, l'appelé enregistre Variables/temporaires. Garanti.
R15 Sert de pointeur de pile ou de registre permanent. Conservé. Pointeur de pile, SP, sauvegardes de l'appelé Pointeur de pile. Garanti.

Remarque : "préservé" fait référence à la sauvegarde par la fonction appelée ; il en va de même pour "garanti".

La convention d'appel la plus courante pour la série Motorola 68000 est [7],[8],[9],[10]:

  • d0, d1, a0 et a1 sont des registres de travail
  • Tous les autres registres sont enregistrés par les appelés
  • a6 est le pointeur de trame, qui peut être désactivé par une option du compilateur
  • Les paramètres sont poussés sur la pile, de droite à gauche
  • La valeur de retour est stockée dans d0

L'IBM 1130 était une petite machine qui utilisait un adressage par mots de 16 bits. Elle ne disposait que de six registres, ainsi que d'indicateurs de condition, et n'avait pas de pile. Les registres incluent le registre d'adresses d'instructions (IAR), l'accumulateur (ACC), l'extension d'accumulateur (EXT) et trois registres d'index X1 à X3. Le programme appelant doit sauvegarder ACC, EXT, X1 et X2. Il existe deux pseudo-opérations pour appeler des sous-routines : CALL, pour coder des sous-routines non relocalisables directement liées au programme principal, et LIBF, pour appeler des sous-routines de bibliothèque relocalisables via un vecteur de transfert. Les deux pseudo-opérations se transforment en une instruction machine appelée Branch and Store IAR (BSI), qui stocke l'adresse de l'instruction suivante à son adresse effective (EA) et se branche sur EA+1.

Les arguments suivent le BSI — ce sont des adresses d'arguments d'un seul mot. La routine appelée doit savoir combien d'arguments elle doit attendre afin de pouvoir les ignorer lors du retour. Alternativement, les arguments peuvent être passés dans des registres. Les routines de fonction renvoient le résultat dans l'accumulateur (ACC) pour les arguments réels, ou dans un emplacement mémoire appelé pseudo-accumulateur de nombres réels (FAC). Les arguments et l'adresse de retour sont adressés à l'aide d'un décalage par rapport à la valeur IAR stockée dans le premier emplacement de la sous-routine.   Les sous-routines des IBM 1130, CDC 6600 et PDP-8 (les trois ordinateurs ont été introduits en 1965) stockent l'adresse de retour dans le premier emplacement d'une sous-routine[11].

Conventions d'appel en dehors des architectures de machines

[modifier | modifier le code]

  Le code threadé confie toute la responsabilité de la configuration et du nettoyage après un appel de fonction au code appelé. Le code d'appel se limite à indiquer les sous-routines à appeler. Cela centralise tout le code de configuration et de nettoyage de la fonction dans un seul endroit (le prologue et l'épilogue de la fonction), au lieu de le disperser dans les nombreux endroits où la fonction est appelée. Cela rend le code threadé la convention d'appel la plus compacte.

Le code threadé transmet tous les arguments sur la pile et renvoie toutes les valeurs de retour de la même manière. Cela rend les implémentations naïves plus lentes que les conventions d'appel qui conservent davantage de valeurs dans les registres. Cependant, les implémentations de code threadé qui mettent en cache plusieurs des valeurs de la pile supérieure dans les registres (en particulier l'adresse de retour) sont généralement plus rapides que celles des conventions d'appel de sous-routines qui poussent et retirent toujours l'adresse de retour de la pile.

La convention d'appel par défaut pour les programmes écrits en langage PL/I transmet tous les arguments par référence, bien que d'autres conventions puissent être spécifiées. Les arguments sont traités différemment selon les compilateurs et les plates-formes, mais généralement, les adresses des arguments sont transmises via une liste d'arguments en mémoire. Une adresse finale, cachée, peut également être transmise pour pointer vers une zone où la valeur de retour doit être stockée. Étant donné la grande variété de types de données supportés par PL/I, un descripteur de données peut également être transmis pour définir, par exemple, les longueurs des chaînes de caractères ou de bits, la dimension et les limites des tableaux (appelés vecteurs dope), ou la structure et le contenu d'une structure de données. Des arguments factices sont créés pour les arguments qui sont des constantes ou qui ne correspondent pas au type d'argument attendu par la procédure appelée.

Liens externes

[modifier | modifier le code]

 

Références

[modifier | modifier le code]
  1. (en-US) « /Oy (Frame-Pointer Omission) », learn.microsoft.com, (consulté le )
  2. « Parameters in general-purpose registers », ARM Cortex-A Series Programmer’s Guide for ARMv8-A (consulté le )
  3. « Parameters in NEON and floating-point registers », developer.arm.com (consulté le )
  4. System V Application Binary Interface SPARC Processor Supplement, 3 (lire en ligne)
  5. « S/390 ELF Application Binary Interface Supplement »
  6. « zSeries ELF Application Binary Interface Supplement »
  7. Smith, « SHARC (21k) and 68k Register Comparison »
  8. XGCC: The Gnu C/C++ Language System for Embedded Development, Embedded Support Tools Corporation, (lire en ligne), p. 59
  9. « COLDFIRE/68K: ThreadX for the Freescale ColdFire Family » [archive du ]
  10. Moshovos, « Subroutines Continued: Passing Arguments, Returning Values and Allocating Local Variables » : « all registers except d0, d1, a0, a1 and a7 should be preserved across a call. »
  11. Smotherman, « Subroutine and procedure call support: Early history »,