Que fait fondamentalement __asm__ __volatile__ ()
et quelle est la signification de "memory"
pour ARM architecture?
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 ).
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.
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.