Question sur les pointeurs restreints
Le
Taurre
Bonjour à tous,
Je me suis récemment penché sur la notion de pointeurs restreints
introduite par la Norme C99 et il y a plusieurs questions auxquelles
je n'ai trouvé aucune réponse:
- la première porte sur ce code:
#include <stdlib.h>
void
test (int *a, int *b, int * restrict v)
{
*a = *v;
*b = *v;
}
int
main (void)
{
int a;
int b;
int v = 10;
test (&a, &b, &v);
return EXIT_SUCCESS;
}
si j'ai bien saisit la notion de pointeur restreint cela signifie,
dans le cas de la fonction "test", que l'objet sur lequel pointe la
variable "v" ne peut-être accéder que via cette dernière. Donc, en
toute logique, on peut supposer que l'objet pointé par la variable "v"
ne sera pas modifié lors de l'exécution de cette fonction. Or, si je
compile ce code, j'obtiens ces lignes en assembleur pour la fonction
"test":
movl (%rdx), %eax
movl %eax, (%rdi)
movl (%rdx), %eax
movl %eax, (%rsi)
la valeur de l'objet pointé par la variable "v" est chargé deux fois
dans le registre eax alors qu'elle ne devrait l'être qu'une seule
fois l'optimisation n'aura lieu que si un ou les deux autres
pointeurs sont également déclaré avec le qualificatif "restrict".
Quelqu'un pourrait-il m'expliquer pourquoi?
- la deuxième porte sur le même code, mais avec des variables de type
char. Dans cette hypothèse, même si les trois pointeurs sont déclar=
és
avec le qualificatif "restrict", aucune optimisation n'est effectuée
et j'obtiens donc:
movzbl (%rdx), %eax
movb %al, (%rdi)
movzbl (%rdx), %eax
movb %al, (%rsi)
Je précise que je compile avec GCC et l'option d'optimisation -O2
Merci d'avance pour vos réponses.
Je me suis récemment penché sur la notion de pointeurs restreints
introduite par la Norme C99 et il y a plusieurs questions auxquelles
je n'ai trouvé aucune réponse:
- la première porte sur ce code:
#include <stdlib.h>
void
test (int *a, int *b, int * restrict v)
{
*a = *v;
*b = *v;
}
int
main (void)
{
int a;
int b;
int v = 10;
test (&a, &b, &v);
return EXIT_SUCCESS;
}
si j'ai bien saisit la notion de pointeur restreint cela signifie,
dans le cas de la fonction "test", que l'objet sur lequel pointe la
variable "v" ne peut-être accéder que via cette dernière. Donc, en
toute logique, on peut supposer que l'objet pointé par la variable "v"
ne sera pas modifié lors de l'exécution de cette fonction. Or, si je
compile ce code, j'obtiens ces lignes en assembleur pour la fonction
"test":
movl (%rdx), %eax
movl %eax, (%rdi)
movl (%rdx), %eax
movl %eax, (%rsi)
la valeur de l'objet pointé par la variable "v" est chargé deux fois
dans le registre eax alors qu'elle ne devrait l'être qu'une seule
fois l'optimisation n'aura lieu que si un ou les deux autres
pointeurs sont également déclaré avec le qualificatif "restrict".
Quelqu'un pourrait-il m'expliquer pourquoi?
- la deuxième porte sur le même code, mais avec des variables de type
char. Dans cette hypothèse, même si les trois pointeurs sont déclar=
és
avec le qualificatif "restrict", aucune optimisation n'est effectuée
et j'obtiens donc:
movzbl (%rdx), %eax
movb %al, (%rdi)
movzbl (%rdx), %eax
movb %al, (%rsi)
Je précise que je compile avec GCC et l'option d'optimisation -O2
Merci d'avance pour vos réponses.

Poser une question


[...]
En fait ce sont "a" et "v" dont tu veux indiquer l'indépendance au
compilateur.
char c[42];
test(c,b,c+1);
--> l'écriture dans *a modifie la valeur *v.
(sur une archi où les int sont forcément alignés, ça ne serait pas
absurde de faire l'optimisation sans restrict j'ai l'impression, mais
je peux rater quelque chose)
Essaie un gcc plus récent alors.
Tout d'abord, merci pour cette réponse :)
Oui, mais n'est ce pas un comportement interdit? Normalement, en
qualifiant le pointeur "v" de restreint, on indique au compilateur que
l'objet sur lequel il pointe ne sera accéder que via lui-même ou un
pointeur dérivé de lui. Or, dans cet exemple ce n'est pas le cas, les
pointeurs "a" et "v" accèdent au même objet (enfin pas tout à fait,
mais une partie de l'objet pointé par "v" est accéder via "a" ce qui
constitue un comportement indéterminé il me semble).
C'est vrai que je suis sous Debian Squeeze et que ma version n'est pas
la plus récente (4-4), je vais essayer avec une autre, merci du
conseil ;)
C'est aussi comme ça que j'avais compris restrict au début, mais
ensuite j'ai regardé ce que faisait gcc, et les exemples donnés par le
standard, et ça ne collait pas bien.
exemple 1:
int * restrict a;
int * restrict b;
extern int c[];
exemple 2:
void f(int n, int * restrict p, int * restrict q)
{
while (n-- > 0)
*p++ = *q++;
}
autre exemple:
void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
À chaque fois (sauf pour la variable extern) restrict est mis en
double. Le texte n'a pas changé dans C1X, c'est toujours aussi peu
clair.
Il est possible aussi que gcc soit trop conservateur.
Je vais tuer le suspens, mais j'avais essayé avant de poster, et ça
fait la même optimisation que pour int.
Voilà qui répond parfaitement à mes interrogations!
Merci beaucoup :)
Dans ton exemple, v est à la fois une variable int de valeur 10, et un
pointeur restreint (vers la première.) Je ne suis pas sûr que cela
facilite la compréhension pour nous autres humains.
Oui, puisque le code ne modifie pas le pointeur, et le propos de
restrict est d'affirmer que d'autres modifications n'ont pas lieu par
ailleurs.
« devrait » : pas d'accord. La norme est limpide, restrict est une
opportunité d'optimisation, pas une obligation imposée au compilateur.
Il faudrait demander aux développeurs de GCC.
Là encore, c'est un sujet pour GCC. À mon sens, tu peux avoir le cas
théorique où les pointeurs pointent vers le même mot mémoire (un mot
pouvant être constitué de plusieurs bytes), et GCC veux peut-être éviter
les problèmes liés à des accès concurrents à un même mot.
Cela étant, ce peut être aussi un bête bogue dans le code de GCC...
Dans tous les cas, c'est totalement conforme à la norme, en vertu de ce
qui est rappelé ci-dessus: restrict n'est pas une obligation.
Antoine