Le compilateur est-il autorisé à plier constamment un volatile local?

25

Considérez ce code simple:

void g();

void foo()
{
    volatile bool x = false;
    if (x)
        g();
}

https://godbolt.org/z/I2kBY7

Vous pouvez voir que gccni clangoptimiser l'appel potentiel à g. Ceci est correct dans ma compréhension: la machine abstraite est de supposer que les volatilevariables peuvent changer à tout moment (en raison, par exemple, d'une cartographie matérielle), donc le pliage constant de l' falseinitialisation dans la ifvérification serait faux.

Mais MSVC élimine gcomplè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 volatileest 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 xune variable non locale pour contourner le problème. Cette question est plus par curiosité.

Max Langhof
la source
3
Cela ressemble à un bug de compilation évident pour moi.
Sam Varshavchik
1
Pour autant que je sache, cela est légal en vertu de la règle comme si. Le compilateur peut prouver que même si l'objet est volatile, il n'y a aucun moyen de modifier son état afin qu'il puisse être déplié. Je ne suis pas assez confiant pour mettre cela dans une réponse, mais je pense que c'est correct.
NathanOliver
3
Mais je pense que l'argument de l'OP selon lequel la variable peut être modifiée par un débogueur est raisonnable. Peut-être que quelqu'un devrait déposer un rapport de bogue avec MSVC.
Brian
2
@curiousguy Même si vous rejetez le résultat et / ou assumez la valeur exacte, vous l'avez quand même lu.
Déduplicateur
2
Fait intéressant, il ne le fait que pour x64. La version x86 appelle toujours g () godbolt.org/z/nc3Y-f
Jerry Jeremiah

Réponses:

2

Je pense que [intro.execution] (le numéro de paragraphe varie) pourrait être utilisé pour expliquer le comportement de MSVC:

Une instance de chaque objet avec une durée de stockage automatique est associée à chaque entrée dans son bloc. Un tel objet existe et conserve sa dernière valeur stockée lors de l'exécution du bloc et pendant la suspension du bloc ...

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

Un objet existe, a une adresse constante et conserve sa dernière valeur stockée pendant toute sa durée de vie. 34


34) Dans le cas d'un objet volatil, la dernière mémoire n'a pas besoin d'être explicite dans le programme.

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.

Avocat en langues
la source
Acceptez que le compilateur puisse savoir quand des magasins non explicites sont possibles sur la plate
MM
1
Donc, si cela est vrai, les objets volatils locaux sont (au moins sur MSVC) entièrement inutiles? Y a-t-il quelque chose que l'ajout volatilevous achète (autre que des lectures / écritures superflues) s'il est ignoré à des fins d'optimisation?
Max Langhof
1
@MaxLanghof Il y a une différence entre être totalement inutile et ne pas avoir tout à fait l'effet que vous voulez / attendez.
Déduplicateur
1
@MaxLanghof Les résultats intermédiaires des calculs en virgule flottante sont parfois promus dans un registre 80 bits, ce qui entraîne des problèmes de précision. Je crois que c'est un gcc-isme et est évité en déclarant tous ces doubles comme volatils. Voir: gcc.gnu.org/bugzilla/show_bug.cgi?id=323
ForeverLearning
1
@philipxy PS Voir ma réponse Je sais que l'accès est défini par l'implémentation. La question ne concerne pas l'accès (l'objet est accédé), mais la prédiction de la valeur.
Law Lawyer
2

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):

Ébauche de travail, Norme pour le langage de programmation C ++
Numéro de document: N4659
Date: 2017-03-21

§ 10.1.7.1 Les cv-qualifiers

5 La sémantique d'un accès via une valeur gl volatile est définie par l'implémentation. […]

De même pour les appareils interactifs (mon accent gras):

§ 4.6 Exécution du programme

5 Une implémentation conforme exécutant un programme bien formé doit produire le même comportement observable que l'une des exécutions possibles de l'instance correspondante de la machine abstraite avec le même programme et la même entrée. [...]

7 Les exigences minimales pour une implémentation conforme sont:

(7.1) - Les accès via des valeurs de glissement volatiles sont évalués strictement selon les règles de la machine abstraite.
(7.2) - À la fin du programme, toutes les données écrites dans des fichiers doivent être identiques à l'un des résultats possibles que l'exécution du programme selon la sémantique abstraite aurait produit.
(7.3) - La dynamique d'entrée et de sortie des dispositifs interactifs doit se dérouler de telle manière que la sortie de la demande soit réellement délivrée avant qu'un programme n'attende l'entrée. Ce qui constitue un appareil interactif est défini par l'implémentation.

Ceux-ci sont collectivement appelés le comportement observable du programme. [...]

(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.

philipxy
la source
1
Je veux dire, cela se résume à "si je trouve une machine où tous les effets secondaires mentionnés sont sans opération, alors j'ai une implémentation C ++ légale en compilant chaque programme en sans opération" . Bien sûr, nous nous intéressons aux effets pratiquement observables, car les effets secondaires abstraits des machines sont tautologiquement abstraits. Cela nécessite un concept de base d'effets secondaires, et je dirais que "les accès volatils conduisent à des instructions d'accès à la mémoire explicites" en fait partie (même si la norme s'en fiche), donc je n'achète pas vraiment le "disons si vous voulez du code au lieu de la sémantique abstraite ". Pourtant, +1.
Max Langhof
Oui, la qualité de la mise en œuvre est pertinente. Néanmoins, à part l'écriture dans des fichiers, les "observables" [sic] sont définis par l'implémentation. Si vous voulez pouvoir définir des points d'arrêt, accéder à certaines mémoires réelles, ignorer «volatile», etc. sur les lectures et écritures volatiles abstraites, alors vous devez demander à votre rédacteur de compilateur de produire le code approprié . PS En C ++, «effet secondaire» n'est pas utilisé pour observer le comportement en soi, il est utilisé pour décrire l'évaluation d'ordre partiel des sous-expressions.
philipxy
Vous voulez expliquer le [sic]? Quelle source citez-vous et quelle est l'erreur?
Max Langhof
En résumé, je veux simplement dire qu'une machine abstraite observable n'est observable dans le monde réel que si & comment l'implémentation le dit.
philipxy
1
Êtes-vous en train de dire qu'une implémentation pourrait prétendre qu'il n'y a pas de périphérique interactif, donc n'importe quel programme peut faire n'importe quoi, et ce serait toujours correct? (Remarque: je ne comprends pas votre emphase sur les appareils interactifs.)
curiousguy
-1

Je crois qu'il est légal de sauter le chèque.

Le paragraphe que tout le monde aime citer

34) Dans le cas d'un objet volatil, la dernière mémoire n'a pas besoin d'être explicite dans le programme

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.

n. «pronoms» m.
la source
Pouvez-vous expliquer pourquoi c'est mal? Dans le code montre, la fonction ne peut littéralement jamais être appelée.
David Schwartz
@DavidSchwartz Vous ne pouvez conclure qu'après avoir spécifié la sémantique des variables volatiles locales (voir le paragraphe cité ci-dessus). La norme elle-même note que cela volatileest censé être un indice de l'implémentation que la valeur peut changer par des moyens inconnus de l'implémentation.
Max Langhof
@MaxLanghof Il n'y a aucun moyen que l'implémentation puisse gérer correctement quelque chose qui lui est inconnu. Ce que les plates-formes utiles font, c'est de spécifier ce que vous pouvez et ne pouvez pas utiliser volatilesur cette plate-forme et en dehors de cette spécification, ce sera toujours une séance de merde.
David Schwartz
@DavidSchwartz Bien sûr, cela peut - en suivant la sémantique (en particulier, les lectures et les écritures) de la machine abstraite. Il pourrait ne pas être en mesure d'optimiser correctement - c'est le point de la norme. Maintenant, c'est une note et donc non normative, et comme nous l'avons dit tous les deux, la mise en œuvre peut spécifier ce qui volatilefait 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'il gpeut être appelé.
Max Langhof
@DavidSchwartz Le code n'affiche rien de tel, car l'absence d'écritures implicites ne découle pas du code.
n. «pronoms» m.