Assemblée: Y86 de la Pile et de l'appeler, pushl/popl et ret instructions
À moins que j'ai copié une erreur, le code ci-dessus a été écrit dans le tableau dans une classe par un élève, avec l'aide et/ou corrections de l'enseignant:
int array[100], sum, i;
void ini() {
for(i = 0; i < 100; i++)
array[i] = i;
}
int main() {
ini();
sum = 0;
for(i = 0; i < 100; i++)
sum += array[i];
}
.pos 0
irmovl Stack, %esp
rrmovl Stack, %ebp
jmp main
array:
.pos 430
sum: .long 0
i: .long 0
main:
call ini //
irmovl $0, %eax //%eax = 0
irmovl sum, %esi //%esi = 0xsum
rmmovl %eax, 0(%esi) //0(%esi) = %eax <=> 0(0xsum) = 0 [sum = 0]
rmmovl %eax, 4(%esi) //4(%esi) = %eax <=> 4(0xsum) = 0 [i = 0]
compare:
irmovl $100, %ebx //%ebx = 100
subl %eax, %ebx //%ebx = %ebx - %eax <=> %ebx = 100 - i
jle finish //Jumps to "finish" if SF=1 pr ZF=0
mrmovl 0(%esi), %edx //%edx = 0(%esi) <=> %edx = 0(0xsum) = sum
addl %eax, %edx //%edx = %edx + %eax <=> %edx = sum + i => sum
rmmovl %edx, 0($esi) //0(%esi) = %edx <=> 0(0xsum) = sum
irmovl $1, %ecx //%ecx = 1
addl %ecx, %eax //%eax = %eax + %ecx <=> %eax = i + 1 => i
rmmovl %eax, 4(%esi) //4($esi) = %eax <=> 4(0xsum) = i
jmp compare //Jumps unconditionally to "compare"
ini:
pushl %ebp //
rrmovl %esp, %ebp //
pushl %ebx //
pushl %eax //
irmovl $0, %eax //%eax = 0
rmmovl %eax, -8(%ebp) //
ini_compare:
irmovl $100, %ecx //%ecx = 100
subl %eax, %ecx //%ecx = %ecx - %eax <=> %ecx = 100 - i
jle ini_finish //Jumps to "ini_finish" if SF=1 pr ZF=0
rrmovl %eax, %ebx //%ebx = %eax <=> %ebx = i
addl %eax, $ebx //%ebx = %ebx + %eax <=> %ebx = i + i = 2i
addl %ebx, %ebx //%ebx = %ebx + %ebx <=> %ecx = 2i + 2i = 4i
rmmovl %eax, array(%ebx) //array(%ebx) = %eax <=> array(0x4i) = i
irmovl %1, %ecx //%ecx = 1
addl %ecx, %eax //%eax = %eax + %ecx <=> %eax = i + 1 => i
rmmovl %eax, -8(%ebp) //
jmp ini_compare //Jumps unconditionally to "ini_compare"
ini_finish:
irmovl $4, %ebx //
addl %ebx, %esp //
popl %ebx //
popl %ebp //
ret //
.pos 600
Stack .long 0
Comme vous pouvez le voir, il ya un tas de commentaires dans toutes les instructions et j'ai eu (je pense) la plupart d'entre eux, ce qui est déroutant moi, c'est l'appel, pushl/popl et ret instructions. Je n'arrive pas à comprendre et je ne comprends pas ce qui se passe dans la pile, et où tous les dossiers sont à pointer du doigt. En gros, les lignes de commentaires (//) qui n'ont rien écrit sur eux.
C'est vraiment important je comprends comment tout cela fonctionne, nous l'espérons, certains d'entre vous peut faire la lumière sur tout ce gâchis.
Quelques notes sur mes commentaires:
- 0xsum: Cela ne veut pas dire que l'adresse est "somme", ce serait impossible. Juste un moyen de comprendre de quoi je parle sans utilisant exactement la même adresse mémoire.
- [somme = 0]: Cela signifie que dans notre code en C, la variable somme sera réglée sur 0 à ce stade.
- i + 1 => i: Cela signifie que nous sommes en incrémentant la valeur de " i "par un et que, dans la ligne suivante" i " représente véritablement que la valeur incrémentée.
OriginalL'auteur Ricardo Amaral | 2009-06-20
Vous devez vous connecter pour publier un commentaire.
Regardons le code:
Cela va pousser la valeur du pointeur d'instruction de la pile (de sorte que vous pouvez revenir plus tard à cette position dans le code), et de sauter à l'adresse de l'ini de l'étiquette. Le 'ret' instruction utilise la valeur stockée dans la pile de retour de la sous-routine.
Ce qui suit est l'initialisation d'un sous-programme. Il enregistre les valeurs de certains registres sur la pile et met en place un cadre de pile en copiant le pointeur de pile (esp) à la base de registre de pointeur (ebp). Si la sous-routine a des variables locales, le pointeur de pile est décrémenté à faire de la place pour les variables sur la pile, et le pointeur de la base est utilisé pour accéder à des variables locales dans le cadre de la pile. Dans l'exemple de la seule variable locale est la (non utilisé) valeur de retour.
L'instruction push décrémente le pointeur de pile (esp) avec la taille des données de ce qui va être poussé, puis stocke la valeur à cette adresse. L'instruction pop fait le contraire, d'abord obtenir la valeur, puis incrémente le pointeur de pile. (Notez que la pile grandit vers le bas, de sorte que le pointeur de pile, l'adresse devient plus faible lorsque la pile augmente.)
Le code suit un modèle standard, de sorte qu'il semble un peu maladroit quand il n'y a pas de variables locales, et il y a un retour non utilisé valeur. Si il y a des variables locales d'une soustraction serait utilisé pour décrémenter le pointeur de pile, au lieu de pousser eax.
Ce qui suit est la sortie de séquence d'un sous-programme. Il restaure la pile de la position avant le frame de pile a été créé, puis la retourne sur le code qui appelle la sous-routine.
En réponse à vos commentaires:
Le registre ebx est poussé et sauté de préserver la valeur. Le compilateur apparemment toujours en place ce code là, sans doute parce que le registre est très couramment utilisé, tout simplement pas dans ce code. De même, un cadre de pile est toujours créé par la copie de l'esp pour ebp même si elle n'est pas vraiment nécessaire.
L'instruction qui pousse eax n'est là que pour décrémenter le pointeur de pile. Il est fait de cette façon pour les petites décrémente que c'est plus court et plus rapide que soustrayant le pointeur de pile. L'espace qu'il se réserve est pour la valeur de retour, le compilateur apparemment le fait toujours, même si la valeur de retour n'est pas utilisée.
Dans votre schéma, le registre esp est toujours en pointant quatre octets trop élevé en mémoire. Rappelez-vous que le pointeur de pile est décrémenté après avoir poussé une valeur, de sorte qu'il pointe vers la valeur poussé, non pas à la valeur suivante. (Les adresses mémoire sont aussi, c'est quelque chose comme 0x600 plutôt que 0x20, où la Pile d'étiquette est déclarée.)
J'ai essayé de faire un schéma pour voir si j'ai tout compris correctement, je l'espère, tout est droit et je l'ai eu, mais j'ai un sentiment que je n'ai pas, elle est ici: images.nazgulled.net/tmp/stack.jpg - Encore, le a) et b) questions dans le premier commentaire, continuent de s'appliquer. Et j'ai de nouveau un c) Pourquoi avons-nous besoin de push/pop %ebp et faire "rrmovl %esp, %ebp"? Il ne semble pas que cela change quoi que ce soit...
J'ai ajouté une réaction à la réponse.
Je ne suis toujours pas sûr que la "valeur de retour n'est pas utilisé". Let's go à partir de la ligne où nous avons créer la trame de pile, BP = SP: Le premier poussoir pour décrémenter SP et enregistrer la valeur de %ebx sur la pile. La seconde va faire la même chose mais pour %eax, l'un de vous dire que c'est une valeur de retour n'est pas utilisé. Maintenant, rappelez-vous que la SP est de 8 octets ci-dessous BP en raison de la 2 pushs. Ensuite, j'utilise "irmovl $0, %eax" et "rmmovl %eax, -8(%ebp)" qui aura pour effet de remplacer le précédent, %eax valeur enregistrée (qui n'a pas d'importance parce que nous avons tout simplement poussé à économiser de l'espace pour elle) avec 0. Pourquoi dites-vous que la valeur n'est pas utilisée, alors?
Le code de l'initialisation de la séquence de l'espace alloué pour la valeur de retour, puis il l'annule, mais après ce point, la valeur n'est jamais utilisé. Le code de la méthode (entre l'initialisation et à la sortie des séquences) ne contiennent pas tout ce qui définit la valeur de retour et la sortie de la séquence de ne pas faire n'importe quoi pour retourner la valeur de la sous-routine.
OriginalL'auteur Guffa