Quelle est la direction de la croissance de la stack dans la plupart des systèmes modernes?

Je prépare des supports de formation en C et je souhaite que mes exemples correspondent au modèle de stack typique.

Dans quelle direction une stack C se développe-t-elle sous Linux, Windows, Mac OSX (PPC et x86), Solaris et les plus récents Unix?

La croissance de la stack ne dépend généralement pas du système d’exploitation lui-même, mais du processeur sur lequel il s’exécute. Solaris, par exemple, fonctionne sous x86 et SPARC. Mac OSX (comme vous l’avez mentionné) s’exécute sur PPC et x86. Linux fonctionne sur tout, de mon grand système Honkin ‘System z au travail à une petite montre-bracelet .

Si le processeur offre un choix quelconque, la convention ABI / appel utilisée par le système d’exploitation spécifie le choix que vous devez effectuer si vous souhaitez que votre code appelle le code de tout le monde.

Les processeurs et leur direction sont:

  • x86: vers le bas.
  • SPARC: sélectionnable. Le standard ABI utilise vers le bas.
  • PPC: vers le bas, je pense.
  • System z: dans une liste liée, je ne vous en prends pas (mais toujours en panne, au moins pour zLinux).
  • ARM: sélectionnable, mais Thumb2 a des encodages compacts uniquement pour down (LDMIA = incrémenter après, STMDB = décrémenter avant).
  • 6502: vers le bas (mais seulement 256 octets).
  • RCA 1802A: comme vous le souhaitez, sous réserve de la mise en œuvre de SCRT.
  • PDP11: vers le bas.
  • 8051: en hausse.

En montrant mon âge sur ces dernières, le 1802 était la puce utilisée pour contrôler les premières navettes (en détectant si les portes étaient ouvertes, je suppose, en fonction de la puissance de traitement 🙂 et de mon deuxième ordinateur, le COMX-35 ( suivant mon ZX80 ).

Les détails de PDP11 ont glané d’ ici , 8051 détails d’ ici .

L’architecture SPARC utilise un modèle de registre à fenêtre glissante. Les détails visibles du sharepoint vue de l’architecture incluent également un tampon circulaire de fenêtres de registre valides et mises en cache en interne, avec des interruptions lorsque celles-ci sont dépassées. Voir ici pour plus de détails. Comme l’explique le manuel SPARCv8 , les instructions SAVE et RESTORE sont comme les instructions ADD plus la rotation des fenêtres de registre. Utiliser une constante positive au lieu du négatif habituel donnerait une stack croissante.

La technique SCRT mentionnée ci-dessus en est une autre: le 1802 utilisait certains ou ses seize registres 16 bits pour la technique SCRT (technique standard d’appel et de retour). L’un était le compteur de programme, vous pouvez utiliser n’importe quel registre comme PC avec l’instruction SEP Rn . L’un était le pointeur de stack et deux étaient toujours définis pour indiquer l’adresse du code SCRT, un pour l’appel, un pour le retour. Aucun registre n’a été traité de manière particulière. Gardez à l’esprit que ces détails proviennent de la mémoire, ils peuvent ne pas être totalement corrects.

Par exemple, si R3 était le PC, R4 était l’adresse d’appel du SCRT, R5 l’adresse de retour du SCRT et R2 la “stack” (citation telle qu’elle est implémentée dans le logiciel), le SEP R4 le code d’appel SCRT.

Il stockerait alors R3 sur la “stack” R2 (je pense que R6 était utilisé pour le stockage temporaire), en l’ajustant vers le haut ou vers le bas, attrape les deux octets après R3, charge-les dans R3, puis SEP R3 adresse.

Pour revenir, il faudrait SEP R5 qui extrairait l’ancienne adresse de la stack R2, en appendait deux (pour sauter les octets d’adresse de l’appel), chargez-le dans R3 et SEP R3 pour lancer le code précédent.

Très difficile d’envelopper votre tête après tout le code basé sur la stack 6502/6809 / z80, mais toujours élégant dans un style bang-votre-tête contre le mur. L’une des grandes caractéristiques de la puce est une suite complète de 16 registres 16 bits, bien que vous en ayez immédiatement perdu 7 (5 pour SCRT, deux pour DMA et des interruptions depuis la mémoire). Ahh, le sortingomphe du marketing sur la réalité 🙂

System z est en fait assez similaire, en utilisant ses registres R14 et R15 pour l’appel / le retour.

En C ++ (adaptable à C) stack.cc :

 static int find_stack_direction () { static char *addr = 0; auto char dummy; if (addr == 0) { addr = &dummy; return find_stack_direction (); } else { return ((&dummy > addr) ? 1 : -1); } } 

L’avantage de la croissance est que dans les systèmes plus anciens, la stack était généralement en tête de la mémoire. Les programmes remplissaient généralement la mémoire à partir du bas, ce qui réduisait le besoin de mesurer et de placer le bas de la stack quelque part.

Stack se développe sur x86 (défini par l’architecture, le pointeur de la stack incrémente la pop, les décréments par push).

Dans MIPS, il n’y a pas d’instruction push / pop . Tous les push / pops sont explicitement effectués par load / store par rapport au pointeur de la stack, puis ajustez manuellement le pointeur $sp . Cependant, comme tous les registres (à l’exception de $0 ) ont un but général, en théorie tout registre peut être un pointeur de stack et la stack peut croître dans la direction souhaitée par le programmeur. Les ABI MIPS se développent généralement vers le bas.

Dans Intel 8051, la stack grandit, probablement parce que l’espace mémoire est si petit (128 octets dans la version originale) qu’il n’ya pas de tas et que vous n’avez pas besoin de placer la stack dessus pour la séparer du tas. du bas.

Il diminue car la mémoire allouée au programme possède les “données permanentes”, c’est-à-dire le code du programme lui-même en bas, puis le tas au milieu. Vous avez besoin d’un autre point fixe pour référencer la stack, ce qui vous laisse le meilleur. Cela signifie que la stack diminue, jusqu’à ce qu’elle soit potentiellement adjacente aux objects du tas.

Sur la plupart des systèmes, la stack diminue, et mon article sur https://gist.github.com/cpq/8598782 explique POURQUOI il se développe. La raison en est que c’est la disposition optimale de deux régions de mémoire en croissance (tas et stack).