|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | ||||||||
|
Membre régulier
![]() Inscription : mai 2010 Messages : 253 ![]() |
Bonjour,
Le programme en assembleur suivant : Code :
Code :
Code :
Code :
|
||||||||
|
|
00
|
|
|
#2 |
![]() ![]() Chercheur d'emploi Inscription : septembre 2007 Messages : 4 639 ![]() |
Bonjour,
Tout d'abord, il faut savoir qu'en notation A&AT (celle produite par défaut par GCC et à l'opposée de la syntaxe Intel habituelle) place l'opérande source avant la destination : « mov source, dest ». Ensuite, la pile est utilisée à tout et n'importe quoi, mais principalement à ces choses-là :
L'accumulateur A (donc « AX » sur Intel et ses déclinaisons AL, AH, EAX et RAX) est le seul registre dont tu es à peu près sûr qu'il soit disponible sur tous les micro-processeurs et qu'il soit accessible par l'ALU, et c'est à cela qu'il sert initialement (un peu comme l'écran d'une calculatrice quatre opérations qui sert à la fois à entrer la donnée et à recevoir le résultat de l'opération). Donc, dans cet esprit et sauf dispositions contraires, RAX est utilisé par défaut pour recevoir le résultat d'une évaluation. C'est aussi lui qui contiendra la valeur finale d'une fonction. En C et C++, on t'oblige en principe à utiliser « return » mais certains langages comme le Perl utilise la dernière valeur évaluée. C'est pour cela qu'on voit parfois une constante toute seule sur une ligne à la fin d'un programme. D'autre part, si la manière normale d'exécuter une fonction est donc de passer les arguments sur la pile et d'en recevoir le résultat dans EAX par défaut, il est possible de demander au compilateur de passer les premiers arguments dans des registres lorsqu'ils sont disponibles, et le reste dans pile, normalement. C'est intéressant parce que cela permet d'exploiter les ressources à disposition, d'éviter des transactions mémoire (et donc d'accélérer notablement les choses), et d'économiser la pile, spécialement dans le cas des fonctions récursives, encore une fois. Donc, fort de ces informations :
À noter que dans ce dernier cas, on aurait pu directement utiliser EAX, ce qui en plus aurait bien collé à la philosophie d'accumulateur de ce registre. Mais il arrive que les fonctions aient besoin d'un registre à tout faire en entrant dans la fonction et, surtout, la manière dont les registres vont être utilisés lors de ces optimisations doit nécessairement être normalisé. Notons enfin que si on ne s'intéresse qu'à la valeur d'une valeur et que l'on n'a aucune intention de s'y référer par adresse (en passant un pointeur dessus à quelque chose), on pouvait utiliser explicitement le mot-clé « register » devant la déclaration d'une variable. Ce mot-clé est cependant tombé en désuétude car il ne s'agit que d'une indication au compilateur, qui peut l'ignorer si quelqu'un récupère le pointeur de la variable ou s'il n'y a pas assez de registres disponibles et, a contrario, peut se permettre de faire directement l'optimisation même sans indication du programmeur s'il sait que cette variable ne sera jamais atteinte autrement que par la fonction concernée et sans utiliser son pointeur. |
|
|
10
|
|
|
#3 | |
|
Membre régulier
![]() Inscription : mai 2010 Messages : 253 ![]() |
Bonjour et merci pour cette réponse détaillée,
Citation:
edit : après je comprends que l'automatisation de la traduction du code source en code machine par un programme puisse générer des comportement automatique de la part du compilateur. Peut-être s'agit il de cela ? re edit: de plus il est nécessaire de stocker en mémoire et non pas de la laisser en registre la valeur de x. Donc l'écriture de 1 sur la pile se justifie. Ce que je ne comprend pas c'est le passage par eax alors que movl -4(%rbp), %edi aurait suffit... |
|
|
|
00
|
|
|
#4 |
|
Membre régulier
![]() Inscription : mai 2010 Messages : 253 ![]() |
ah je crois que j'ai compris ce que tu veux dire par évaluer en plaçant la valeur dans eax on peut évaluer x=1 par le résultat de cette opération sur le registre d'état (?)
ce que ne permet peut-être pas (movl $1, -4(%rbp) (?) |
|
|
00
|
|
|
#5 |
![]() ![]() Chercheur d'emploi Inscription : septembre 2007 Messages : 4 639 ![]() |
Voila. C'est tout cela à la fois.
Pour le reste, bien des optimisations peuvent être faites mais cela demande une heuristique qui consiste à analyser la fonction entière. On ne sait pas où se trouve « l'horizon » du compilateur en ce domaine. Par contre, il serait intéressant que tu passes des options d'optimisation explicites telles que « -O3 » à g++ et voir quel genre de code il produit dans ce cas. |
|
|
10
|
|
|
#6 | |||
|
Membre régulier
![]() Inscription : mai 2010 Messages : 253 ![]() |
Citation:
Code :
Merci encore pour tes explications edit : en fait même pas... la fonction n'est pas appelée... |
|||
|
|
00
|
|
|
#7 |
![]() ![]() Chercheur d'emploi Inscription : septembre 2007 Messages : 4 639 ![]() |
Non, effectivement. :-)
Le compilo s'est rendu compte que non seulement tu ne l'appelles qu'une fois, mais tu n'utilise jamais sa valeur de retour, même si tu l'affectes à x. Dans le même esprit, on aurait très bien pu se passer de compiler la fonction elle-même, mais ce n'était pas possible pour plusieurs raisons : — Il aurait fallu pousser l'heuristique au delà des frontières de la fonction pour la poursuivre dans la fonction appelée. C'est possible mais ça complique beaucoup les choses ; — Une fonction peut avoir des effets de bords même lorsque l'on utilise jamais sa valeur de retour. C'est le cas de toutes les fonctions que l'on utilise comme des commandes ; — Le programme compilé, même s'il s'agit d'un exécutable, reste une ressource qui peut être utilisée comme une bibliothèque sous certaines conditions. La fonction se doit donc d'exister quand même pour pouvoir éventuellement être exploitée par un projet externe. |
|
|
10
|
|
|
#8 |
|
Membre régulier
![]() Inscription : mai 2010 Messages : 253 ![]() |
Oui en effet l'utilisation de la fonction par une autre unité de compilation doit pouvoir être envisagée et cette information est hors de portée du compilateur.
L'optimisation permet dans cette exemple d'économiser plusieurs accès mémoire. Autant dire que les options d'optimisation de gcc sont indispensable à la création d'un programme efficace. En fait la compilation sans optimisation équivaut à une traduction "mot à mot" du programme source. Merci pour toutes ces précisions. |
|
|
00
|
Copyright © 2000-2013 - www.developpez.com