Exigences relatives au comportement du pointeur sur la volatilité pointant sur un object non volatile

C11 6.7.3 Qualificatifs de type, le paragraphe 7 est libellé comme suit:

Un object de type qualifié volatile peut être modifié de manière inconnue de l’implémentation ou avoir d’autres effets secondaires inconnus. Par conséquent, toute expression faisant référence à un tel object doit être évaluée ssortingctement selon les règles de la machine abstraite, telles que décrites au 5.1.2.3.

Dans l’exemple suivant, l’object accédé dans la troisième ligne est-il soumis à la règle ci-dessus?

int x; volatile int *p = &x; *p = 42; 

En d’autres termes, le fait que lvalue *p soit de type volatile int signifie-t-il qu’un object volatile est en cours d’access, ou le fait que p pointe-t-il sur un object non volatile x signifie-t-il que le compilateur peut optimiser avec cette connaissance et omettre l’access volatile?

Comme cela peut présenter un intérêt, le cas d’utilisation particulier qui m’intéresse n’entre pas dans le champ d’application de C en clair; cela implique des atomes pour la synchronisation des threads en utilisant des constructions pré-C11 (qui pourraient être inline asm ou simplement considérées comme une boîte noire) pour la comparaison et le swap atomiques, avec l’idiome suivant:

 do { tmp = *p; new = f(tmp); } while (atomic_cas(p, tmp, new) != success); 

Ici, le pointeur p aurait le type volatile int * , mais je suis préoccupé par ce qui se passe lorsque l’object réellement pointé est non volatile, notamment si le compilateur peut transformer l’access unique en *p de tmp = *p en deux. access de la forme suivante:

 do { new = f(*p); } while (atomic_cas(p, *p, new) != success); 

ce qui rendrait évidemment le code incorrect. L’objective est donc de déterminer si tous les objects pointés doivent réellement être volatile int .

Mise à jour 18 février 2017

La réponse ci-dessous cite et discute du libellé de la norme, du libellé contradictoire du raisonnement et des commentaires de gnu.cc au sujet de la contradiction. Il y a un rapport de défaut qui a essentiellement un accord de comité (bien que toujours ouvert) que la norme devrait indiquer, et que l’intention a toujours été, et que les mises en œuvre ont toujours reflété, que ce n’est pas la volatilité d’ un object qui compte (par le Standard) mais de la volatilité de (la valeur de) un access (selon la justification). (Nous remercions Olaf d’ avoir mentionné ce DR.)

Résumé du rapport de défauts pour C11 version 1.10 Date: avril 2016 DR 476 Sémantique volatile pour lvalues 04/2016 Ouvrir


Non, parce que l’object auquel on a accédé n’est pas volatile.

L’object p est de type pointeur sur volatile int. Mais x n’est pas un object d’un type qualifié de volatil. Les qualifications sur p affectent les access qui peuvent être faits à travers elle, mais n’affectent pas le type d’object qu’il pointe. Il n’y a aucune ressortingction sur l’access à un object de type non qualifié via une valeur volatile. Donc, accéder à x à travers p n’est pas l’access d’un object de type qualifié volatile.

(Voir 6.7.3 Les qualificatifs de types pour les ressortingctions d’access aux objects de types qualifiés. Cela indique simplement que vous ne pouvez pas accéder à un object qualifié volatil via une lvalue non qualifiée.)

Par ailleurs, cet article cite le 6.7.3 de la justification pour la norme internationale – Langages de programmation – C:

Une conversion d’une valeur en un type qualifié n’a pas d’effet; la qualification (volatile, par exemple) ne peut avoir d’effet sur l’access, car elle s’est produite avant le cas. S’il est nécessaire d’accéder à un object non volatil à l’aide d’une sémantique volatile, la technique consiste à convertir l’adresse de l’object en un type de pointeur en qualifiant qualifié, puis à déréférencer ce pointeur.

Cependant, je ne trouve pas dans la norme de langage qui dit que la sémantique est basée sur le type lvalue. De gnu.org :

Un domaine de confusion est la distinction entre les objects définis avec des types volatiles et les valeurs volatiles. Du sharepoint vue de la norme C, un object défini avec un type volatile a un comportement visible de l’extérieur. Vous pouvez imaginer que de tels objects sont associés à de petites sondes d’oscilloscope, de sorte que l’utilisateur puisse observer certaines propriétés d’access, tout comme l’utilisateur peut observer les données écrites dans les fichiers de sortie. Cependant, la norme n’indique pas clairement si les utilisateurs peuvent observer les access par des valeurs volatiles aux objects ordinaires.

[..] la norme ne précise pas si les valeurs volatiles offrent plus de garanties en général que les valeurs non volatiles, si les objects sous-jacents sont ordinaires.

Non, car il n’y a pas d’effets secondaires:

Même si la sémantique de *p doit être celle d’un volatile, le standard dit néanmoins:

5.1.2.3 Exécution du programme 4 Dans la machine abstraite, toutes les expressions sont évaluées comme spécifié par la sémantique. Une implémentation réelle n’a pas besoin d’évaluer une partie d’une expression si elle peut en déduire que sa valeur n’est pas utilisée et qu’aucun effet secondaire nécessaire n’est produit (y compris tout effet provoqué par l’appel d’une fonction ou l’access à un object volatile).

Encore une fois, votre code ne contient aucun object volatile. Bien qu’une unité de compilation qui ne voit que p ne puisse effectuer cette optimisation.

Gardez aussi à l’esprit

6.7.3 Qualificatifs de type 7 […] Ce qui constitue un access à un object de type qualifié volatile est défini par l’implémentation.

5.1.2.3 Exécution du programme 8 Chaque instance peut définir des correspondances plus ssortingctes entre la sémantique abstraite et la sémantique réelle.

Ainsi, la simple apparition de valeurs volatiles ne vous dit pas quels sont les “access” existants. Vous n’avez pas le droit de parler de “l’access unique à *p partir de tmp = *p “, sauf pour le comportement d’implémentation documenté.

Pas tout à fait sûr, mais je pense que le point est la différence entre le type d’un object et le type avec lequel un object est défini .

C11 (n1570), 6.3.2.1 p1 (note de bas de page omise, source emph.):

Une lvalue est une expression (avec un type d’object autre que void ) qui désigne potentiellement un object. Si une valeur ne désigne pas un object lors de son évaluation, le comportement n’est pas défini. Lorsqu’un object est dit avoir un type particulier, le type est spécifié par la lvalue utilisée pour désigner l’object. […]

C’est la valeur qui définit le type d’object d’un object pour un access particulier. En revanche, *p ne désigne pas un object défini comme volatile. Par exemple, ibid. 6.7.3 p6 (emph. Mine) lit

[…] Si l’on tente de faire référence à un object défini avec un type qualifié volatil en utilisant une valeur lvalue avec un type qualifié non volatile, le comportement est indéfini. 133)

133) Ceci s’applique aux objects qui se comportent comme s’ils étaient définis avec des types qualifiés, même s’ils ne sont jamais définis en tant qu’objects dans le programme (comme un object situé à une adresse d’entrée / sortie mappée en mémoire).

Si l’intention était de permettre l’optimisation du code affiché, la citation de la question se lirait probablement comme suit: Un object qui a [est défini avec un] type qualifié volatile peut être modifié […]

La “définition” d’un identifiant *) est définie au 6.7, paragraphe 5.

Ibid. 6.7.3 p7 ( ce qui constitue un access à un object de type qualifié volatile est défini par l’implémentation. ) Laisse une marge de manœuvre aux développeurs, mais pour moi, l’intention semble être que l’effet secondaire de la modification de l’object désigné par n doit être considéré comme observable par une mise en œuvre conforme.

*) Après que le standard ne définisse “un object défini avec (un type)” nulle part, je le lis comme “un object, désigné par un identifiant déclaré avec (un type) par une définition”.