Considérez ce code simple:
void g();
void foo()
{
volatile bool x = false;
if (x)
g();
}
Vous pouvez voir que gcc
ni clang
optimiser l'appel potentiel à g
. Ceci est correct dans ma compréhension: la machine abstraite est de supposer que les volatile
variables peuvent changer à tout moment (en raison, par exemple, d'une cartographie matérielle), donc le pliage constant de l' false
initialisation dans la if
vérification serait faux.
Mais MSVC élimine g
complètement l'appel à (en conservant les lectures et les écritures volatile
!). Ce comportement est-il conforme aux normes?
Contexte: J'utilise parfois ce type de construction pour pouvoir activer / désactiver la sortie de débogage à la volée: le compilateur doit toujours lire la valeur de la mémoire, donc changer cette variable / mémoire pendant le débogage devrait modifier le flux de contrôle en conséquence . La sortie MSVC relit la valeur mais l'ignore (probablement en raison du pliage constant et / ou de l'élimination du code mort), ce qui, bien sûr, annule mes intentions ici.
Modifications:
L'élimination des lectures et écritures
volatile
est discutée ici: Est-il permis à un compilateur d'optimiser une variable volatile locale? (merci Nathan!). Je pense que la norme est très claire: ces lectures et écritures doivent avoir lieu. Mais cette discussion ne couvre pas s'il est légal pour le compilateur de prendre les résultats de ces lectures pour acquis et d'optimiser en fonction de cela. Je suppose que cela est sous / non spécifié dans la norme, mais je serais heureux si quelqu'un me prouvait le contraire.Je peux bien sûr créer
x
une variable non locale pour contourner le problème. Cette question est plus par curiosité.
la source
Réponses:
Je pense que [intro.execution] (le numéro de paragraphe varie) pourrait être utilisé pour expliquer le comportement de MSVC:
La norme ne permet pas d'éliminer une lecture à travers une valeur gl volatile, mais le paragraphe ci-dessus pourrait être interprété comme permettant de prédire la valeur
false
.BTW, la norme C (N1570 6.2.4 / 2) dit que
Il n'est pas clair s'il pourrait y avoir un stockage non explicite dans un objet avec une durée de stockage automatique dans la mémoire C / modèle d'objet.
la source
volatile
vous achète (autre que des lectures / écritures superflues) s'il est ignoré à des fins d'optimisation?TL; DR Le compilateur peut faire ce qu'il veut sur chaque accès volatile. Mais la documentation doit vous le dire .-- "La sémantique d'un accès via une valeur gl volatile est définie par l'implémentation."
La norme définit pour un programme des séquences autorisées d '"accès volatils" et d'autres "comportements observables" (obtenus via des "effets secondaires") qu'une implémentation doit respecter selon "la règle" comme si ".
Mais la norme dit (mon accent gras):
De même pour les appareils interactifs (mon accent gras):
(Quoi qu'il en soit, le code spécifique généré pour un programme n'est pas spécifié par la norme.)
Donc, bien que la norme dise que les accès volatils ne peuvent pas être éliminés des séquences abstraites d'effets secondaires de la machine abstraite et des comportements observables conséquents que certains codes (peut-être) définissent, vous ne pouvez pas vous attendre à ce que quoi que ce soit soit reflété dans le code objet ou le monde réel sauf si la documentation de votre compilateur vous indique ce qui constitue un accès volatil . Idem pour les appareils interactifs.
Si vous êtes intéressé par volatile vis - à - vis des séquences abstraites des effets secondaires de la machine abstraite et / ou des comportements observables conséquentes que certains définit de code (peut - être) puis le disent . Mais si vous êtes intéressé par le code objet correspondant généré, vous devez l'interpréter dans le contexte de votre compilateur et de votre compilation .
Chroniquement, les gens croient à tort que pour les accès volatils, une évaluation / lecture abstraite de la machine provoque une lecture implémentée et une affectation / écriture abstraite de la machine provoque une écriture implémentée. Il n'y a aucune base pour cette croyance en l'absence de documentation de mise en œuvre le disant. Lorsque / si l'implémentation indique qu'elle fait réellement quelque chose lors d'un "accès volatile", les gens sont justifiés de s'attendre à ce que quelque chose - peut-être, la génération d'un certain code objet.
la source
Je crois qu'il est légal de sauter le chèque.
Le paragraphe que tout le monde aime citer
n'implique pas qu'une implémentation doit supposer que de tels magasins sont possibles à tout moment ou pour toute variable volatile. Une implémentation sait quels magasins sont possibles. Par exemple, il est tout à fait raisonnable de supposer que de telles écritures implicites ne se produisent que pour les variables volatiles qui sont mappées aux registres de périphérique et que ce mappage n'est possible que pour les variables avec une liaison externe. Ou une implémentation peut supposer que de telles écritures ne se produisent que sur des emplacements de mémoire de taille de mot et alignés sur un mot.
Cela dit, je pense que le comportement de MSVC est un bug. Il n'y a aucune raison réelle d'optimiser l'appel. Une telle optimisation peut être conforme, mais elle est inutilement mauvaise.
la source
volatile
est censé être un indice de l'implémentation que la valeur peut changer par des moyens inconnus de l'implémentation.volatile
sur cette plate-forme et en dehors de cette spécification, ce sera toujours une séance de merde.volatile
fait et s'y tenir. Mon point est que le code lui-même (selon la machine standard / abstraite C ++) ne vous permet pas de déterminer s'ilg
peut être appelé.