Blog 4 min de lecture

Bidouiller Clang pour démystifier les objets temporaires

Share this article
Bidouiller Clang pour démystifier les objets temporaires

Il est parfois nécessaire pour le compilateur C++ de créer des objets temporaires. Ils sont utilisés lors de :

  • L'initialisation de références.
  • L'évaluation d'expressions incluant des conversions de types standard.
  • Le passage d'arguments.
  • Les retours de fonctions.
  • L'évaluation de l'expression throw.

Pour les classes non triviales, la création et la destruction d'objets temporaires peuvent être coûteuses en temps de traitement et en utilisation mémoire. Dans ce cas, il faut minimiser leur introduction. Le compilateur C++ élimine certains objets temporaires, mais il ne peut pas tous les éliminer.

Il n'est pas toujours facile de détecter où les objets temporaires sont introduits, comme l'explique Herb Sutter dans son exemple. Le compilateur possède cette information, mais est-il possible d'obtenir de lui les endroits où les objets temporaires sont introduits ? Et sinon, est-il facile de le modifier pour qu'il les signale ?

Dans notre cas, nous utiliserons Clang : il est très flexible et offre de nombreuses façons de personnaliser son comportement. En effet, un concept de conception majeur de Clang est son architecture basée sur des bibliothèques. Dans cette conception, les différentes parties du front end peuvent être proprement divisées en bibliothèques séparées, qui peuvent ensuite être combinées selon différents besoins et usages. De plus, l'approche par bibliothèques encourage de bonnes interfaces et facilite l'implication de nouveaux développeurs (car ils n'ont besoin de comprendre que de petites parties de l'ensemble).

Le compilateur Clang a trois phases :

  • Le front end, qui analyse le code source, vérifie les erreurs et construit un arbre syntaxique abstrait (AST) spécifique au langage pour représenter le code d'entrée.
  • L'optimiseur : son objectif est d'effectuer certaines optimisations sur l'AST généré par le front end.
  • Le back end, qui génère le code final à exécuter par la machine ; il dépend de la cible.

Dans notre cas, nous nous concentrerons sur la phase front end. L'objectif est d'obtenir l'arbre syntaxique abstrait (AST) d'un fichier source et de vérifier s'il rapporte des informations utiles sur les objets temporaires.

Explorons l'AST de ce code source minimal :

ast2

Pour générer l'AST, nous pouvons exécuter le parseur du front end Clang en utilisant l'option -cc1.

clang -cc1 -ast-dump test.cpp

Voici l'AST généré pour la fonction GetTest :

ast1

Dans cet AST, deux informations sont liées aux objets temporaires : nrvo et elidable.

La Named Return Value Optimization (NRVO) est une technique d'optimisation du compilateur qui consiste à éliminer l'objet temporaire créé pour contenir la valeur de retour d'une fonction. La NRVO élimine le constructeur de copie et le destructeur d'une valeur de retour sur la pile. Cela supprime les appels redondants au constructeur de copie et au destructeur et améliore ainsi les performances globales. Pour plus de détails, vous pouvez consulter sa page wiki.

L'élision de copie (copy elision) est une technique d'optimisation du compilateur qui élimine les copies d'objets inutiles. Pour plus de détails, vous pouvez consulter sa page wiki.

Il est intéressant que Clang nous indique où une NRVO est appliquée, mais il ne signale pas explicitement où les objets temporaires sont créés. Entrons dans le code source de l'AST dumper et essayons de les signaler.

Comment fonctionne ASTDumper ?

Le compilateur analyse un programme et représente le programme analysé sous forme d'arbre syntaxique abstrait (AST). L'AST comporte de nombreux types de nœuds différents, comme les nœuds d'affectation, de référence de variable et d'expression arithmétique. Après avoir généré l'AST, Clang invoque des actions du front end qui le parcourent et effectuent certains traitements ; ASTDumpAction en fait partie, et elle affiche l'AST sur la console.

ASTDumper est déclaré comme ceci :

  class ASTDumper
      : public ConstDeclVisitor, public ConstStmtVisitor,
        public ConstCommentVisitor 

Le pattern visiteur est le pattern recommandé lorsqu'on doit parcourir une structure et appliquer un traitement spécifique à chaque nœud de cette structure.

Voici quelques méthodes invoquées lors du parcours de l'AST ; chacune est liée à un nœud AST spécifique.

void VisitNamespaceDecl(const NamespaceDecl *D);
void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D);
void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D);
void VisitTypeAliasDecl(const TypeAliasDecl *D);
void VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D);
void VisitCXXRecordDecl(const CXXRecordDecl *D);
void VisitStaticAssertDecl(const StaticAssertDecl *D);

Bidouiller l'AST dumper de Clang

Notre objectif est de signaler les objets temporaires qui sont créés ; nous devons donc suivre la création des objets et identifier quel nœud AST est impliqué dans la construction de chaque objet.

Dans Clang, VisitCXXConstructExpr est invoquée lorsqu'un objet C++ doit être créé ; voici son implémentation :

ast5

Comme on peut le constater, il n'y a pas de test pour savoir si l'objet créé est temporaire ou non, mais la bonne nouvelle est que CXXConstructExpr possède la méthode IsTemporaryObject, qui détermine si le résultat de cette expression est un objet temporaire du type de classe donné.

Modifions l'implémentation pour ajouter une autre condition :

ast6

Voici le nouvel AST produit après la modification :

ast3

Après l'ajout de quelques lignes de code, l'ASTDumper signale désormais explicitement où les objets temporaires sont créés.

Conclusion

LLVM/Clang n'est pas seulement un compilateur ; c'est aussi une infrastructure puissante pour développer vos propres outils C/C++/Objective-C. Son architecture est relativement facile à comprendre, et elle peut être personnalisée selon vos besoins. N'hésitez pas à télécharger le code source de Clang, à y apporter quelques modifications et à le recompiler — cela peut être particulièrement utile pour les étudiants qui veulent apprendre comment fonctionnent les compilateurs.

Share this article