2016-11-25 2 views
2

Devons-nous initialiser les registres à 0 lors de l'écriture des fonctions en langage assembleur?Avez-vous besoin d'initialiser les registres dans MIPS?

Juste pour s'assurer qu'il n'y a pas de valeurs dans ces registres des programmes précédents.

+1

Ne pas avoir toutes les valeurs tautologiques, mais seulement les valeurs qui comptent. Donc toute question sur «dois-je initialiser cela» dépend intrinsèquement du contexte. – harold

Répondre

3

Non, vous n'avez pas besoin de les réinitialiser, mais vous devez supposer que chaque registre contient des données aléatoires à l'entrée de votre fonction/programme, sauf pour les registres qui contiennent les entrées (par exemple, args de fonction) ou la convention d'appel nécessite d'avoir une sorte de valeur utile (par exemple le pointeur de la pile).

Normalement, c'est bien; vous n'avez pas besoin de vider la poubelle au hasard "tôt". Si votre première utilisation d'un registre est d'écrire quelque chose, vous n'avez pas besoin d'écrire un zéro, puis d'écrire ce que vous voulez vraiment mettre là. Toutefois, si vous voulez l'utiliser comme un compteur (c'est-à-dire l'incrémenter en boucle), alors oui, vous devez le mettre à zéro avant la boucle. Il en va de même pour toute autre utilisation d'un registre comme opérande source, et pas seulement comme opérande de destination.


Notez que c'est l'une des rares choses en langage assembleur qui est le même pour tous les genre de langage assembleur pour chaque architecture.

Certaines architectures (comme ) ont des instructions avec des entrées implicites (comme DIV), mais zeroing EDX before DIV est toujours juste une affaire de la mise à zéro des registres avant de les lire.


Pour d'autres termes: inscrivez-vous toujours tous a une valeur, donc il n'y a rien de spécial au sujet de la première utilisation d'un registre comme opérande d'écriture seule.

Vous devez juste vous assurer que l'exactitude de votre code ne dépend pas du contenu des registres ou de la mémoire qui n'ont pas besoin d'avoir une valeur spécifique.


Bonus lecture:

Pour les performances, il y a des exceptions rares à cette règle: Dans certains micro-architectures, pas toutes les opérations d'écriture seulement sont les mêmes. Certains ont en fait une fausse dépendance à l'ancienne valeur de la sortie! L'utilisation d'une méthode peu coûteuse pour écrire le registre permet d'éviter l'attente d'une "entrée" dont il n'a pas besoin, au cas où votre code s'exécuterait après quelque chose qui pourrait utiliser ce registre à la fin d'un chaîne de dépendance longue (par exemple impliquant des échecs de cache).

Je ne connais pas d'exemples MIPS (espérons-il n'y en a pas), donc je vais utiliser x86 comme exemple:

Pour INT-> instructions de flotteur qui mettent leur résultat dans l'élément faible d'un registre vectoriel (par exemple, CVTSI2SS), Intel a adopté une approche à courte vue en la concevant pour se fondre dans le vecteur de destination, au lieu de réduire à zéro le reste de la destination. Les processeurs de première génération à avoir SSE étaient Intel Pentium III, qui avait seulement des unités d'exécution de vecteur 64 bits. Je pense que la réduction à zéro des 64 bits supérieurs d'un vecteur lui aurait coûté un surcoût, ou du moins exigé en interne l'instruction de conversion pour produire les deux moitiés du vecteur en sortie, au lieu de seulement la moitié basse modifiée. La grande majorité des cas d'utilisation pour la conversion int-> float ne veulent que le flottant en tant que scalaire, pas inséré dans un autre vecteur, donc la dépendance sur le registre de destination est potentiellement dangereuse. gcc l'évite avec an extra xor-zeroing instruction pour mettre à zéro le registre XMM de destination avant d'exécuter CVTSI2SS. Cette instruction de remise à zéro supplémentaire n'est pas totalement gratuite même sur les dernières CPU: elle prend la taille de code et la bande passante frontale. C'est donc un coût supplémentaire généralement très faible pour toute conversion int> float afin d'éviter des ralentissements rares mais potentiellement importants dans des cas imprévisibles lorsque deux chaînes de dépendances non indépendantes sont liées par des dépendances d'entrée sur le même registre.

En outre, le registre de destination de POPCNT/LZCNT/TZCNT est architecturellement en écriture seule, mais l'implémentation d'Intel est has a false dependency on the output register. Il est donc logique de mettre à zéro le registre de destination avant d'exécuter POPCNT, si nécessaire pour éviter de créer de façon inattendue une chaîne de dépendances transportée par une boucle.

Voir un exemple de gcc insérant une instruction xor-zeroing supplémentaire avant la conversion popcnt et int-> float sur le Godbolt Compiler Explorer.

4

En général, probablement oui.

L'état du registre au démarrage du programme dépend du système d'exploitation. Par exemple, si vous exécutez un OS qui honore le ELF psABI for MIPS, les registres $ 2, $ 29 et $ 31 ont des valeurs significatives au démarrage du programme, et les autres contiennent des valeurs non spécifiées - voir la section "Initialisation du processus".

On dirait que vous pourriez être confus au sujet de la différence entre un programme et une fonction . La fonction attendue lors de l'entrée est également documentée dans le psABI - voir la section «Séquence d'appel de fonction» dans le même document - la version courte étant: $ 4- $ 7, $ 25, $ 28, $ 29, $ 31, $ f12, $ f13, $ f14 et $ f15 peuvent contenir des valeurs utiles, tous les autres sont non spécifiés, et vous êtes obligé de vous assurer que les valeurs que vous trouvez dans $ 16- $ 23, $ 28, $ 29, $ 30 et $ f20- $ f31 sur En revanche, si vous appelez une fonction vous-même, vous devez supposer qu'elle a écrasé tous les autres registres avec des valeurs non spécifiées avant de les quitter. retour).

Si vous utilisez un système d'exploitation qui fait et non honore le psABI ELF, vous devez trouver le document équivalent pour votre système d'exploitation. Il y aura certaines spécifications, quelque part. En théorie, vous devrez faire de l'ingénierie inverse pour un compilateur.