Un projet intégré sur lequel je travaille nécessite la lecture d'un emplacement spécifique en mémoire, mais la valeur de cet emplacement mémoire n'est pas nécessaire. Actuellement, je lis la variable volatile dans une variable factice comme dans foo1() ci-dessous, mais je suis curieux de connaître la méthode dans foo2().

void foo1(void) {
    volatile uint32_t *a = (volatile uint32_t *)0xdeadbeef;
    volatile uint32_t discard = *a;
}
void foo2(void) {
    volatile uint32_t *a = (volatile uint32_t *)0xdeadbeef;
    *a;
}

Voir le désassemblage (compilé avec gcc 4.7.2 et -O3):

  foo1:
movl      0xdeadbeef, %eax
movl      %eax, -0x4(%rsp)
ret
  foo2:
movl      0xdeadbeef, %eax
ret

La méthode dans foo2() semble fonctionner, mais je veux savoir si elle est garantie de fonctionner et n'est pas un effet secondaire de la version du compilateur et des optimisations que j'utilise.

6
Tim 30 déc. 2015 à 04:21

3 réponses

Meilleure réponse

Le mot clé volatile indique au compilateur qu'un objet peut changer en dehors de la portée du flux de programme normal (c'est-à-dire visible par le compilateur). Par conséquent, le compilateur effectue cet accès en général . La dernière phrase fait référence à la manière dont l'accès est effectué, par ex. lecture d'octets, lectures non alignées, etc.

De plus, le compilateur doit exécuter tous les accès à ces objets dans l'ordre donné par le déroulement du programme. Notez, cependant, qu'il peut réorganiser librement les accès aux objets non volatils et que le matériel sous-jacent peut également penser différemment (le compilateur peut ne pas le savoir).

Le compilateur peut toujours optimiser les accès aux objets volatile qui existent et ne sont modifiés que dans le code visible. Cela est vrai pour les variables locales où l'adresse n'est pas prise (il peut y avoir d'autres situations), car elles ne peuvent pas être atteintes en dehors de la portée. Pour les pointeurs que vous utilisez, ce n'est pas vrai, car le compilateur ne connaît pas l'objet vers lequel ils pointent.

Pour supprimer le résultat d'une expression sans avertissement du compilateur, il suffit de le convertir en void:

volatile uint32_t *vi = ...;
(void)*vi;            // this still might be optimized if vi is local

(Si la cible est en lecture seule, ajoutez const.)

Consultez la documentation pour plus de détails sur volatile accède. Une implémentation conforme à la norme C doit fournir ces informations.

Notez également que le matériel sous-jacent peut toujours réorganiser les accès, sauf si la zone mémoire utilise une politique d'accès strictement ordonnée / non mise en cache. Ceci est typique des registres périphériques mappés en mémoire. Si un cache / MMU est utilisé, les zones devront peut-être être configurées en conséquence.

2
too honest for this site 30 déc. 2015 à 03:29

Il n'y a aucune garantie que le déréférencement d'un objet volatil entraîne un accès en lecture, voir ISO 9899: 2011 §6.7.3 ¶7:

Un objet qui a un type qualifié volatil 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 objet doit être évaluée strictement selon les règles de la machine abstraite, comme décrit en 5.1.2.3. En outre, à chaque point de séquence, la dernière valeur stockée dans l'objet doit correspondre à celle prescrite par la machine abstraite, sauf si elle est modifiée par les facteurs inconnus mentionnés précédemment. 134) Ce qui constitue un accès à un objet de type volatile qualifié est défini par l'implémentation.

134) Une déclaration volatile peut être utilisée pour décrire un objet correspondant à un port d'entrée / sortie mappé en mémoire ou à un objet accédé par une fonction d'interruption asynchrone. Les actions sur les objets ainsi déclarés ne doivent pas être «optimisées» par une implémentation ou réorganisées, sauf dans la mesure permise par les règles d'évaluation des expressions.

En pratique, les implémentations du langage de programmation C définissent couramment unaire * pour constituer un accès à un objet, garantissant ainsi que *a provoque un accès en lecture à une variable volatile a.

1
fuz 30 déc. 2015 à 01:51

C'est standard en C, mais pas en C ++. Voir: https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Volatiles .html

MAIS, vous n'avez certainement pas besoin de faire l'écriture volatile sur discard. Ceux-ci seront probablement tous compilés de la même manière que foo2:

  • utilisez foo1, mais supprimez le qualificatif volatile de discard; ou
  • utilisez foo2, mais utilisez !*a ou * a + 0 au lieu de *a. La valeur doit être accédée afin d'évaluer l'expression, même en C ++
2
Matt Timmermans 30 déc. 2015 à 01:56