Les codes fantastiques : co-vide

Magazine
Marque
GNU/Linux Magazine
Numéro
268
Mois de parution
mars 2024
Spécialité(s)


Résumé

Continuons cette série sur les codes fantastiques avec les classes vides de C++


Body

Quand on apprend que la taille (au sens de sizeof) d’une structure vide en C++ est de un octet, on est souvent un peu surpris. Puis on comprend que tout objet pouvant avoir une adresse, un objet ne peut avoir une taille vide (imaginez le bazar : un tableau de structure vide serait lui-même vide et tous les objets auraient la même adresse).

La plupart du temps, le compilateur arrive à « optimiser » le code associé, principalement quand il peut prouver que l’adresse de l’objet n’est jamais prise. Mais ce n’est pas le cas de la situation suivante :

struct empty {
  void doit() const;
};
 
void pain() {
  empty e;
  e.doit();
}

Le code optimisé associé n’est pas extraordinaire, il comprend quelques manipulations de la pile :

pain():
  push rax
  mov rdi, rsp
  call empty::doit() const@PLT
  pop rax
  ret

Rien de trop surprenant si on y réfléchit à deux fois : le compilateur ne connaît rien de l’implémentation de empty::doit() qui peut très bien avoir pour effet de bord de stocker this dans une variable globale quelconque. Il faut donc laisser la possibilité à e d’avoir une adresse.

On peut très bien lui enlever cette possibilité en déclarant doit comme étant static. Mais dans ce cas-là, on pourra appeler cette méthode sans instance de l’objet, ce qui change le contrat avec l’utilisateur. Comment informer le compilateur que la méthode n’utilise pas l’adresse de l’objet ? Un ami m’a proposé cette élégante solution :

struct empty {
  void doit() const { really_doit(); }
  private:
  static void really_doit();
};
 
void pain() {
  empty e;
  e.doit();
}

Le code optimisé par Clang pour ce bout de code est d’une absolue simplicité :

pain():
    jmp empty::really_doit()@PLT

Le compilateur a supprimé l’appel de méthode pour directement invoquer la méthode statique, qui n’a par construction pas besoin de l’adresse de l’objet. Il devient inutile d’allouer l’objet sur la pile, et grâce au contrôle de la visibilité, l’interface publique de l’objet reste la même.

Cette optimisation peut paraître ridicule, mais elle permet de supprimer quelques manipulations de la pile, et épargne un store supplémentaire quand on active -ftrivial-auto-var-init=pattern, ce qui lui a valu d’atterrir dans Firefox avec https://phabricator.services.mozilla.com/D184083.



Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous