web-dev-qa-db-fra.com

Fonctionnement de __asm__ __volatile__ (""::: "mémoire")

Que fait fondamentalement __asm__ __volatile__ () et quelle est la signification de "memory" pour ARM architecture?

42
vnr1992
asm volatile("" ::: "memory");

crée une barrière de mémoire au niveau du compilateur forçant l'optimiseur à ne pas réorganiser les accès à la mémoire à travers la barrière.

Par exemple, si vous devez accéder à une adresse dans un ordre spécifique (probablement parce que cette zone de mémoire est en fait sauvegardée par un périphérique différent plutôt qu'une mémoire), vous devez être en mesure de le dire au compilateur, sinon il peut simplement optimiser vos étapes pour par souci d'efficacité.

Supposons que dans ce scénario, vous devez incrémenter une valeur d'adresse, lire quelque chose et incrémenter une autre valeur dans une adresse adjacente.

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        d[1] += 1;
        return r;
}

Le problème est que le compilateur (gcc dans ce cas) peut réorganiser votre accès mémoire pour obtenir de meilleures performances si vous le demandez (-O). Menant probablement à une séquence d'instructions comme ci-dessous:

00000000 <c>:
   0:   4603        mov r3, r0
   2:   c805        ldmia   r0, {r0, r2}
   4:   3001        adds    r0, #1
   6:   3201        adds    r2, #1
   8:   6018        str r0, [r3, #0]
   a:   6808        ldr r0, [r1, #0]
   c:   605a        str r2, [r3, #4]
   e:   4770        bx  lr

Les valeurs ci-dessus pour d[0] Et d[1] Sont chargées en même temps. Supposons que c'est quelque chose que vous voulez éviter, vous devez alors dire au compilateur de ne pas réorganiser les accès à la mémoire et d'utiliser asm volatile("" ::: "memory").

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        asm volatile("" ::: "memory");
        d[1] += 1;
        return r;
}

Ainsi, vous obtiendrez votre séquence d'instructions comme vous le souhaitez:

00000000 <c>:
   0:   6802        ldr r2, [r0, #0]
   2:   4603        mov r3, r0
   4:   3201        adds    r2, #1
   6:   6002        str r2, [r0, #0]
   8:   6808        ldr r0, [r1, #0]
   a:   685a        ldr r2, [r3, #4]
   c:   3201        adds    r2, #1
   e:   605a        str r2, [r3, #4]
  10:   4770        bx  lr
  12:   bf00        nop

Il convient de noter qu'il ne s'agit que d'une barrière de mémoire de temps de compilation pour éviter que le compilateur réorganise les accès à la mémoire, car il ne met aucune instruction de niveau matériel supplémentaire pour vider les mémoires ou attendre que le chargement ou les magasins soient terminés. Les CPU peuvent toujours réorganiser les accès mémoire s'ils ont les capacités architecturales et si les adresses mémoire sont de type normal au lieu de strongly ordered Ou device ( ref ).

65
auselen

Cette séquence est une barrière de programmation d'accès à la mémoire du compilateur, comme indiqué dans l'article référencé par Udo. Celui-ci est spécifique à GCC - d'autres compilateurs ont d'autres façons de les décrire, certains avec des instructions plus explicites (et moins ésotériques).

__asm__ est une extension gcc permettant de saisir des instructions de langage d'assemblage imbriquées dans votre code C - utilisé ici pour sa propriété de pouvoir spécifier des effets secondaires qui empêchent le compilateur d'effectuer certains types d'optimisations (qui dans ce cas pourraient finir par génération de code incorrect).

__volatile__ est nécessaire pour garantir que l'instruction asm elle-même n'est pas réorganisée avec d'autres accès volatiles (une garantie en langage C).

memory est une instruction pour GCC qui (en quelque sorte) dit que la séquence asm en ligne a des effets secondaires sur la mémoire globale, et donc pas seulement les effets sur les variables locales doivent être pris en compte.

18
unixsmurf

Le sens est expliqué ici:

http://en.wikipedia.org/wiki/Memory_ordering

Fondamentalement, cela implique que le code d'assembly sera exécuté là où vous l'attendez. Il indique au compilateur de ne pas réorganiser les instructions qui l'entourent. C'est ce qui est codé avant que ce morceau de code ne soit exécuté avant et ce qui est codé après sera exécuté après.

9
Udo Klein