Fonctionnement de ASM volatile ( ""::: "mémoire")
qu'est-ce que fondamentalement __asm__ __volatile__ ()
et quelle est la signification de "memory"
pour l'architecture ARM?
4 réponses
asm volatile("" ::: "memory");
crée une barrière de mémoire au niveau du compilateur forçant l'optimiseur à ne pas réordonner les accès de mémoire à travers la barrière.
par exemple, si vous avez besoin d'accéder à une adresse dans un ordre précis (probablement parce que cette zone de mémoire est en fait soutenue par un périphérique différent plutôt qu'une mémoire), vous devez être capable de le dire au compilateur sinon il se peut que cela optimise vos étapes pour des raisons d'efficacité.
supposons que dans ce scénario vous devez augmenter un valeur dans l'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;
}
Problème de compilateur (gcc
dans ce cas) peut réarranger votre accès mémoire pour obtenir de meilleures performances si vous le demandez (-O
). Probablement conduisant à 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
valeurs ci-Dessus concernant d[0]
et d[1]
sont chargés en même temps. Supposons que c'est quelque chose que vous voulez éviter, alors vous devez dire à compilateur de ne pas réorganiser les accès à la mémoire et qui est 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 faut noter qu'il ne s'agit que de la barrière de mémoire de temps de compilation pour éviter que le compilateur ne réordonne les accès mémoire, car il ne met pas d'instructions de niveau de matériel supplémentaire pour vider les mémoires ou attendre que la charge ou les stocks soient remplis. Les CPU peuvent toujours réordonner les accès mémoire s'ils ont les capacités architecturales et les adresses mémoire sont sur normal
Tapez 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, tel que noté dans L'article cité par Udo. Celui - ci est spécifique au GCC-d'autres compilateurs ont d'autres façons de les décrire, certains avec des déclarations plus explicites (et moins ésotériques).
__asm__
est une extension de gcc permettant aux déclarations en langage assembleur d'être entrées imbriquées dans votre code C-utilisé ici pour sa propriété de pouvoir spécifier les effets secondaires qui empêchent le compilateur d'effectuer certains types d'optimisations (qui dans ce cas pourrait générer un code incorrect).
__volatile__
est nécessaire pour s'assurer que l' asm la déclaration elle-même n'est pas réordonnée avec d'autres accès volatiles (une garantie en langage C).
memory
est une instruction pour GCC qui dit (en quelque sorte) que la séquence asm inline a des effets secondaires sur la mémoire globale, et donc pas seulement des 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'assemblage sera exécuté là où vous l'attendez. Il dit au compilateur de ne pas réordonner les instructions autour de lui. C'est ce qui est codé avant ce morceau de code sera exécuté avant et ce qui est codé après sera exécuté après.
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags;
asm volatile(
" mrs %0, cpsr @ arch_local_irq_save\n"
" cpsid i" //disabled irq
: "=r" (flags) : : "memory", "cc");
return flags;
}