De temps en temps, le code C ++ ne fonctionnera pas lorsqu'il est compilé avec un certain niveau d'optimisation. Il peut s'agir d'un compilateur effectuant une optimisation qui rompt le code ou d'un code contenant un comportement non défini qui permet au compilateur de faire ce qu'il ressent.
Supposons que j'ai un morceau de code qui se casse lorsqu'il est compilé avec un niveau d'optimisation supérieur uniquement. Comment savoir si c'est le code ou le compilateur et que dois-je faire si c'est le compilateur?
optimization
compiler
acéré
la source
la source
Réponses:
Je dirais que c'est une valeur sûre que, dans la grande majorité des cas, c'est votre code, pas le compilateur, qui est cassé. Et même dans le cas extraordinaire où il s'agit du compilateur, vous utilisez probablement une fonctionnalité de langage obscur d'une manière inhabituelle, pour laquelle le compilateur spécifique n'est pas préparé; en d'autres termes, vous pourriez très probablement changer votre code pour être plus idiomatique et éviter le point faible du compilateur.
En tout cas, si vous pouvez prouver que vous avez trouvé un bogue de compilateur (basé sur les spécifications du langage), signalez-le aux développeurs du compilateur, afin qu'ils puissent le corriger un certain temps.
la source
Comme d'habitude, comme pour tout autre bogue: effectuez une expérience contrôlée. Limitez la zone suspecte, désactivez les optimisations pour tout le reste et commencez à varier les optimisations appliquées à ce morceau de code. Une fois que vous obtenez une reproductibilité à 100%, commencez à varier votre code, en introduisant des éléments qui pourraient briser certaines optimisations (par exemple, introduisez un alias de pointeur possible, insérez des appels externes avec des effets secondaires potentiels, etc.). La consultation du code assembleur dans un débogueur peut également être utile.
la source
Examinez le code d'assembly qui en a résulté et voyez s'il fait ce que votre source appelle. N'oubliez pas que les chances sont très élevées que ce soit vraiment votre code en faute d'une manière non évidente.
la source
En plus de 30 ans de programmation, le nombre de bogues de compilation (génération de code) authentiques que j'ai trouvés n'est toujours que de 10. Le nombre de mes bogues (et ceux d'autres personnes) que j'ai trouvés et corrigés au cours de la même période est probablement > 10 000. Ma "règle générale" est alors que la probabilité qu'un bogue donné soit dû au compilateur est <0,001.
la source
J'ai commencé à écrire un commentaire, puis j'ai décidé que c'était trop long et trop précis.
Je dirais que c'est votre code qui est cassé. Dans le cas peu probable où vous auriez découvert un bogue dans le compilateur - vous devriez le signaler aux développeurs du compilateur, mais c'est là que la différence s'arrête.
La solution consiste à identifier le construit incriminé et à le refactoriser pour qu'il fasse différemment la même logique. Cela résoudrait très probablement le problème, que le bogue soit de votre côté ou dans le compilateur.
la source
la source
int + int
du débordement exactement comme s'il était compilé en une instruction ADD matérielle. Cela fonctionnait très bien lors de la compilation avec une ancienne version de GCC, mais pas lors de la compilation avec le nouveau compilateur. Apparemment, les gens sympathiques de GCC ont décidé que, comme le résultat d'un débordement d'entier n'est pas défini, leur optimiseur pourrait fonctionner en supposant que cela ne se produise jamais. Il a optimisé une branche importante dès la sortie du code.Si vous voulez savoir s'il s'agit de votre code ou du compilateur, vous devez parfaitement connaître la spécification de C ++.
Si le doute persiste, vous devez parfaitement connaître l'assemblage x86.
Si vous n'êtes pas d'humeur à apprendre les deux à la perfection, c'est certainement un comportement indéfini que votre compilateur résout différemment selon le niveau d'optimisation.
la source
Obtenir une erreur de compilation sur du code standard ou une erreur de compilation interne est plus probable que les optimiseurs se trompent. Mais j'ai entendu parler de compilateurs optimisant les boucles en oubliant de manière incorrecte certains effets secondaires dus à une méthode.
Je n'ai aucune suggestion sur la façon de savoir si c'est vous ou le compilateur. Vous pouvez essayer un autre compilateur.
Un jour, je me demandais si c'était mon code ou non et quelqu'un m'a suggéré valgrind. J'ai passé les 5 ou 10 minutes pour exécuter mon programme avec lui (je pense que je l'
valgrind --leak-check=yes myprog arg1 arg2
ai fait mais j'ai joué avec d'autres options) et il m'a immédiatement montré UNE ligne qui est exécutée sous un cas spécifique, ce qui était le problème. Ensuite, mon application s'est bien déroulée depuis, sans accidents, erreurs ou comportements étranges. valgrind ou un autre outil comme celui-ci est un bon moyen de savoir si c'est votre code.Note latérale: je me demandais une fois pourquoi les performances de mon application étaient nulles. Il s'est avéré que tous mes problèmes de performances étaient également sur une seule ligne. J'ai écrit
for(int i=0; i<strlen(sz); ++i) {
. Le sz était de quelques mb. Pour une raison quelconque, le compilateur a exécuté strlen à chaque fois, même après l'optimisation. Une ligne peut être un gros problème. Des performances aux plantagesla source
Une situation de plus en plus courante est que les compilateurs cassent le code écrit pour les dialectes de C qui prennent en charge les comportements non prescrits par la norme et permettent au code ciblant ces dialectes d'être plus efficace que le code strictement conforme. Dans un tel cas, il serait injuste de décrire comme un code "cassé" qui serait fiable à 100% sur les compilateurs qui ont implémenté le dialecte cible, ou de décrire comme "cassé" le compilateur qui traite un dialecte qui ne prend pas en charge la sémantique requise . Au lieu de cela, les problèmes proviennent simplement du fait que le langage traité par les compilateurs modernes avec les optimisations activées diffère des dialectes qui étaient autrefois populaires (et sont toujours traités par de nombreux compilateurs avec les optimisations désactivées, ou par certains même avec les optimisations activées).
Par exemple, beaucoup de code est écrit pour les dialectes qui reconnaissent comme légitimes un certain nombre de modèles d'alias de pointeur non requis par l'interprétation de la norme par gcc, et utilise ces modèles pour permettre une traduction simple du code pour être plus lisible et efficace que ce qui serait possible selon l'interprétation de la norme C par la gcc. Un tel code peut ne pas être compatible avec gcc, mais cela ne signifie pas qu'il est cassé. Il s'appuie simplement sur des extensions que gcc ne prend en charge qu'avec les optimisations désactivées.
la source
Isolez l'endroit problématique et comparez le comportement observé à ce qui devrait se produire selon les spécifications de la langue. Certainement pas facile, mais c'est ce que vous devez faire pour savoir (et pas seulement assumer ).
Je ne serais probablement pas si méticuleux. Je demanderais plutôt au forum de support du compilateur / à la liste de diffusion. Si c'est vraiment un bogue dans le compilateur, ils pourraient le corriger. Ce serait probablement mon code de toute façon. Par exemple, les spécifications de langue concernant la visibilité de la mémoire dans le filetage peuvent être assez contre-intuitives, et elles ne peuvent devenir apparentes que lors de l'utilisation de drapeaux d'optimisation spécifiques, sur un matériel spécifique (!). Certains comportements pourraient être indéfinis par la spécification, donc cela pourrait fonctionner avec certains compilateurs / certains indicateurs, et ne pas fonctionner avec d'autres, etc.
la source
Votre code a très probablement un comportement non défini (comme d'autres l'ont expliqué, vous êtes beaucoup plus susceptible d'avoir des bogues dans votre code que dans le compilateur, même si les compilateurs C ++ sont si complexes qu'ils ont des bogues; même la spécification C ++ a des bogues de conception) . Et UB peut être là même si l'exécutable compilé se trouve fonctionner (par malchance).
Vous devriez donc lire le blog de Lattner Ce que chaque programmeur C devrait savoir sur le comportement non défini (la plupart s'applique également à C ++ 11).
L' outil valgrind et les
-fsanitize=
options d'instrumentation récentes de GCC (ou Clang / LLVM ) devraient également être utiles. Et bien sûr, activez tous les avertissements:g++ -Wall -Wextra
la source