30 votes

Pourquoi gcc ne peut-il pas devirtualiser cet appel de fonction?

#include <cstdio>
#include <cstdlib>
struct Interface {
    virtual void f() = 0;
};

struct Impl1: Interface {
    void f() override {
        std::puts("foo");
    }
};

// or __attribute__ ((visibility ("hidden")))/anonymous namespace
static Interface* const ptr = new Impl1 ;

int main() {
    ptr->f();
}

Lorsqu'il est compilé avec g++-7 -O3 -flto -fdevirtualize-at-ltrans -fipa-pta -fuse-linker-plugin, au-dessus de la ptr->f() appel ne peut pas être devirtualized.

Il semble qu'aucune bibliothèque externe peut modifier ptr. Est-ce une carence de GCC optimiseur, ou parce que certains autres sources de devirtualization indisponible, dans ce cas?

Godbolt lien

Mise à JOUR: Il semble que clang-7 -flto -O3 -fwhole-program-vtables -fvisibility=hidden est le seul compilateur+drapeaux (comme dans 2018/03) qui peuvent devirtualize ce programme.

11voto

Omnifarious Points 25666

Si vous déplacez le pointeur dans la fonction principale, le résultat est très révélateur et offre un fort soupçon pourquoi gcc ne veut pas de virtualiser le pointeur.

Le démontage de cette montre que si les "a la statique été initialisé drapeau" est faux, il initialise la statique et saute ensuite de retour à l'appel de fonction virtuelle, même si rien ne pouvait lui est arrivée entre les deux.

Cela me dit que gcc est câblé à croire que toute l'échelle mondiale persistante pointeur doit toujours être considéré comme un pointeur vers un type inconnu.

En fait, c'est même pire que cela. Si vous ajoutez dans une variable locale, il est important de savoir si l'appel à l' f sur le pointeur statique se produit entre la création de la variable locale et l'appel à l' f ou pas. L'assemblée montrant l' f interposés cas est ici: un Autre godbolt lien; et il est simple de ré-arranger vous-même sur le site pour voir comment l'assemblée se transforme en une ligne de f une fois de l'autre appel n'est pas interposé.

Donc, gcc doit supposer que le type réel d'un pointeur désigne peut changer à chaque fois que le contrôle de flux des feuilles de la fonction pour une raison quelconque. Et qu'il soit ou non déclarée const n'est pas pertinent. Il n'est ni pertinent si l'adresse est déjà pris, ou n'importe quel nombre d'autres choses.

clang fait la même chose. Cela semble trop prudent pour moi, mais je ne suis pas un compilateur écrivain.

4voto

Oliv Points 7148

Je viens de faire une autre expérience, comme dans Omnifarious réponse. Mais cette fois, je fais le pointeur pointer vers un objet statique:

Impl1 x;
static Interface* const ptr = &x ;

GCC ne devirtualize l'appel de la fonction, -O2 est suffisante. Je ne vois pas de règle dans la norme C++ qui ferait pointeur de stockage statique traités différemment pointeur de stockage dynamique.

Il est permis de changer l'objet à l'adresse pointée par ptr. Ainsi, le compilateur doit suivre de près ce qui se passe à cette adresse pour savoir quel est le type dynamique de l'objet. Donc, mon opinion est que l'optimiseur de maître d'œuvre peut avoir considéré que le suivi de ce qui se passe sur le tas pourrait être trop difficile dans un programme réel, de sorte que le compilateur ne le faites pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X