Appel de sous-routines à l’époque ancienne, quand les ordinateurs n’avaient ni pile ni tas
- En informatique moderne, on considère la pile et le tas comme allant de soi, mais aux débuts de l’informatique, les ordinateurs fonctionnaient sans pile ni tas.
- Il n’est pas difficile d’imaginer l’informatique sans allocation dynamique de mémoire. Il faut utiliser des tampons mémoire de taille fixe pour tout.
- Lorsqu’il faut traiter des données de taille variable, on réserve un tampon de taille fixe suffisamment grand pour contenir les données prévisibles.
- On peut fournir des réglages à la compilation pour permettre au client d’ajuster la capacité maximale, ou écrire un allocateur personnalisé capable d’« allouer » et de « libérer » de la mémoire dans un tampon de taille fixe.
Appeler des fonctions sans pile
- Le compilateur définit des variables globales secrètes pour les paramètres d’entrée, l’adresse de retour et les variables locales de chaque fonction.
- Pour générer un appel de fonction, le compilateur assigne les valeurs des paramètres à ces variables globales secrètes, assigne l’adresse de retour à la « variable d’adresse de retour » secrète de la fonction, puis saute au début de la fonction.
- La fonction lit ses paramètres depuis les variables globales secrètes et utilise des variables globales secrètes prédéfinies correspondant logiquement aux variables locales.
- À la fin de la fonction, elle saute à l’adresse contenue dans sa « variable d’adresse de retour » secrète.
Optimisation de l’ABI
- Pour optimiser l’ABI, certaines valeurs sont transmises dans des registres plutôt que dans des variables globales.
- La plupart des processeurs disposent d’un registre de « lien » et d’une instruction de « branchement avec lien », qui règle automatiquement le registre de lien à l’adresse suivant l’instruction de « branchement avec lien ».
- On optimise la convention d’appel pour transmettre les deux premiers paramètres dans des registres.
L’impossibilité des appels récursifs
- Les appels récursifs ne fonctionnent pas. Un appel récursif écrase la variable d’adresse de retour avec l’adresse de retour de l’appel récursif, si bien que l’appel extérieur saute au mauvais endroit une fois terminé.
- Les langages de programmation de l’époque résolvaient ce problème en déclarant qu’ils ne prenaient pas en charge la récursivité.
Bonus
- Certains compilateurs étaient encore plus astucieux en utilisant du code auto-modifiant : la variable spéciale d’adresse de retour est en réalité le champ d’adresse de l’instruction de saut située à la fin de la fonction.
- Lorsque le processeur ne prend pas en charge les sauts indirects, cette méthode devient une nécessité pratique.
- Une fois la valeur pratique des sous-routines reconnue, de nombreux processeurs ont ajouté des instructions d’appel de sous-routine qui stockent l’adresse de retour dans le premier mot de la sous-routine et commencent l’exécution au deuxième mot de celle-ci.
- Pour revenir depuis la sous-routine, on exécute un saut indirect via le label de début de la sous-routine.
L’avis de GN⁺
- Cet article aide à comprendre l’évolution des techniques de gestion mémoire utilisées dans le développement logiciel moderne en expliquant comment on programmait aux débuts de l’informatique, quand il n’y avait ni pile ni tas.
- La manière de programmer de cette époque, sans pile ni tas, peut sembler très étrangère et inefficace aux développeurs d’aujourd’hui, mais elle fournit des connaissances de fond essentielles pour comprendre comment la technologie a évolué au fil de l’histoire de l’informatique.
- Les contraintes de programmation d’une époque où les appels récursifs étaient impossibles offrent un fait historique intéressant aux développeurs qui utilisent aujourd’hui des algorithmes récursifs.
- D’un point de vue critique, ces méthodes de programmation initiales montrent aussi à quel point elles étaient limitées face aux exigences complexes et variées du monde moderne.
1 commentaires
Avis Hacker News
Évaluation positive du livre The Art of Computer Programming
Explication d’une méthode permettant à deux tableaux de partager dynamiquement un même espace
Partage d’un lien vers une histoire amusante sur le fait que l’introduction des fonctions récursives dans le langage ALGOL a été controversée
Partage d’une expérience d’écriture d’un interpréteur Forth pour une machine SUBLEQ et une machine bit-série
Explication de l’évolution technique liée aux appels de sous-routine sur le processeur PDP-8
Partage de la préférence pour la récursion d’un utilisateur ayant longtemps pratiqué la programmation fonctionnelle
Partage d’une expérience de conception d’un multiplexeur série RS232 vers 1991
Mention de la situation passée où, avant que le heap puisse être extensible, les programmeurs devaient réfléchir à la distribution probable des entrées et dimensionner correctement les tampons intermédiaires
Explication du fait qu’à l’époque où l’on ne pouvait pas utiliser la récursion, la tail recursion restait possible
branch_with_link.Explication de la manière dont le compilateur d’Enhanced GNU Awk alloue des variables globales secrètes pour les blocs @let situés hors des fonctions
Mention d’un post décrivant le monde visé par l’article Goto considered harmful