Est-ce que le compilateur a le droit d'optimiser le "return f->a" en
"return 0" ?
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
[…]
- an aggregate or union type that includes one of the
aforementioned types among its members (including, recursively,
a member of a subaggregate or contained union), or a character
type.
Du coup vu que 'struct foo' contient des unsigned short ce n'est pas
légal d'optimiser le load de f->a après le store dans
container_of(...)->a vu qu'ils pourraient s'aliaser.
j'ai bon ?
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter
les restricts suivants:
int foo(struct foo *restrict f)
{
unsigned *restrict p = get_ptr(f);
int foo(struct foo *f) { unsigned *p = get_ptr(f);
f->a = 0; container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a; }
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en "return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut pas faire d'hypothèse sur la fonction externe get_ptr(), et les éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types: […] - an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la garantie du contraire...
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter les restricts suivants:
int foo(struct foo *restrict f) { unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit) que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas valoir f. Ce qui autorise l'optimisation.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est pas le cas:
int foo(struct foo *restrict f) { unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction pour forcer le compilateur à faire des optimisations douteuses.
Si la fonction ne retourne pas 0, c'est : - soit que le compilateur génère du code faux (j'ai pas lu l'assembleur mais je suppose que ce n'est pas le cas) - soit que le processeur est bogué ou vicié d'une manière ou d'une autre (genre mettre un point d'arrêt et forcer f->a à valoir 42) - soit que le résultat est 1 et que la fonction get_ptr() est du genre void * get_ptr(struct foo * f) { return &(f->b); } (en violation flagrante des intentions indiquées par restrict).
Maintenant, si ta question est, « en supposant que get_ptr soit la fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans le second ? », la réponse est probablement parce que dans le premier cas il y a deux objets restreints (f et p), et que dans le second il n'y en plus qu'un, f : le qualificatif ajouté par le transtypage ne créant pas d'objet nouveau. (Et chapeau au passage aux programmeurs du dit compilateur qui ont su faire le distinguo.)
int foo(struct foo *f)
{
unsigned *p = get_ptr(f);
f->a = 0;
container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a;
}
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en
"return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut
pas faire d'hypothèse sur la fonction externe get_ptr(), et les
éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
[…]
- an aggregate or union type that includes one of the
aforementioned types among its members (including, recursively,
a member of a subaggregate or contained union), or a character
type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même
modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la
garantie du contraire...
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter
les restricts suivants:
int foo(struct foo *restrict f)
{
unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit)
que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas
valoir f. Ce qui autorise l'optimisation.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par
contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est
pas le cas:
int foo(struct foo *restrict f)
{
unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction
pour forcer le compilateur à faire des optimisations douteuses.
Si la fonction ne retourne pas 0, c'est :
- soit que le compilateur génère du code faux (j'ai pas lu l'assembleur
mais je suppose que ce n'est pas le cas)
- soit que le processeur est bogué ou vicié d'une manière ou d'une
autre (genre mettre un point d'arrêt et forcer f->a à valoir 42)
- soit que le résultat est 1 et que la fonction get_ptr() est du genre
void * get_ptr(struct foo * f) { return &(f->b); }
(en violation flagrante des intentions indiquées par restrict).
Maintenant, si ta question est, « en supposant que get_ptr soit la
fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce
qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans
le second ? », la réponse est probablement parce que dans le premier cas
il y a deux objets restreints (f et p), et que dans le second il n'y en
plus qu'un, f : le qualificatif ajouté par le transtypage ne créant pas
d'objet nouveau.
(Et chapeau au passage aux programmeurs du dit compilateur qui ont su
faire le distinguo.)
int foo(struct foo *f) { unsigned *p = get_ptr(f);
f->a = 0; container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a; }
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en "return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut pas faire d'hypothèse sur la fonction externe get_ptr(), et les éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types: […] - an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la garantie du contraire...
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter les restricts suivants:
int foo(struct foo *restrict f) { unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit) que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas valoir f. Ce qui autorise l'optimisation.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est pas le cas:
int foo(struct foo *restrict f) { unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction pour forcer le compilateur à faire des optimisations douteuses.
Si la fonction ne retourne pas 0, c'est : - soit que le compilateur génère du code faux (j'ai pas lu l'assembleur mais je suppose que ce n'est pas le cas) - soit que le processeur est bogué ou vicié d'une manière ou d'une autre (genre mettre un point d'arrêt et forcer f->a à valoir 42) - soit que le résultat est 1 et que la fonction get_ptr() est du genre void * get_ptr(struct foo * f) { return &(f->b); } (en violation flagrante des intentions indiquées par restrict).
Maintenant, si ta question est, « en supposant que get_ptr soit la fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans le second ? », la réponse est probablement parce que dans le premier cas il y a deux objets restreints (f et p), et que dans le second il n'y en plus qu'un, f : le qualificatif ajouté par le transtypage ne créant pas d'objet nouveau. (Et chapeau au passage aux programmeurs du dit compilateur qui ont su faire le distinguo.)
int foo(struct foo *f) { unsigned *p = get_ptr(f);
f->a = 0; container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a; }
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en "return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut pas faire d'hypothèse sur la fonction externe get_ptr(), et les éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types: […] - an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la garantie du contraire...
Oui pardon, je voulais dire "me garantit que l'optimisation est impossible". Je ne me suis pas relu :)
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter les restricts suivants:
int foo(struct foo *restrict f) { unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit) que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas valoir f. Ce qui autorise l'optimisation.
Ah oui anéfé, container_of() est une expression dérivée de 'p' qui est restrict, 'f' aussi. facile.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est pas le cas:
int foo(struct foo *restrict f) { unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction pour forcer le compilateur à faire des optimisations douteuses.
Je sais bien :)
Si la fonction ne retourne pas 0, c'est : - soit que le compilateur génère du code faux (j'ai pas lu l'assembleur mais je suppose que ce n'est pas le cas)
Non il recharche f->a et le retourne. De toute façon il a le choix essentiellement entre ça et retourner 0 dans les options qui ont du sens[0].
- soit que le processeur est bogué ou vicié d'une manière ou d'une autre (genre mettre un point d'arrêt et forcer f->a à valoir 42)
cf mon [0] oui :)
Maintenant, si ta question est, « en supposant que get_ptr soit la fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans le second ? »,
Dans le premier cas elle n'est pas incorrecte au vu de mes annotations! Ce sont les annotations qui le sont éventuellement.
la réponse est probablement parce que dans le premier cas il y a deux objets restreints (f et p), et que dans le second il n'y en plus qu'un, f : le qualificatif ajouté par le transtypage ne créant pas d'objet nouveau. (Et chapeau au passage aux programmeurs du dit compilateur qui ont su faire le distinguo.)
Il serait plus logique en fait après réflexion que gcc s'apercoit que je dis que une expression dérivée d'un pointeur restrict (p) est restrict, ce qui est invalide. Et que gcc décide dans le doute d'ignorer les attributs en question sur p et cette expression :)
Mais -Wstrict-aliasing=2 n'a rien montré malheureusement.
[0] Bien sur vu que ce que je fais est invalide (j'assigne un pointeur restrict dans un autre qui s'aliasent ce qui est interdit!) il *aurait le droit* de compiler ça en return system("rm -rf /")… mais on va dire que les devs de GCC sont rationnels (quoi que…) -- ·O· Pierre Habouzit ··O OOO http://www.madism.org
On 2011-05-26, Antoine Leca <root@localhost.invalid> wrote:
int foo(struct foo *f)
{
unsigned *p = get_ptr(f);
f->a = 0;
container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a;
}
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en
"return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut
pas faire d'hypothèse sur la fonction externe get_ptr(), et les
éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
[…]
- an aggregate or union type that includes one of the
aforementioned types among its members (including, recursively,
a member of a subaggregate or contained union), or a character
type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même
modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la
garantie du contraire...
Oui pardon, je voulais dire "me garantit que l'optimisation est
impossible". Je ne me suis pas relu :)
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter
les restricts suivants:
int foo(struct foo *restrict f)
{
unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit)
que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas
valoir f. Ce qui autorise l'optimisation.
Ah oui anéfé, container_of() est une expression dérivée de 'p' qui est
restrict, 'f' aussi. facile.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par
contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est
pas le cas:
int foo(struct foo *restrict f)
{
unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction
pour forcer le compilateur à faire des optimisations douteuses.
Je sais bien :)
Si la fonction ne retourne pas 0, c'est :
- soit que le compilateur génère du code faux (j'ai pas lu l'assembleur
mais je suppose que ce n'est pas le cas)
Non il recharche f->a et le retourne. De toute façon il a le choix
essentiellement entre ça et retourner 0 dans les options qui ont du
sens[0].
- soit que le processeur est bogué ou vicié d'une manière ou d'une
autre (genre mettre un point d'arrêt et forcer f->a à valoir 42)
cf mon [0] oui :)
Maintenant, si ta question est, « en supposant que get_ptr soit la
fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce
qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans
le second ? »,
Dans le premier cas elle n'est pas incorrecte au vu de mes annotations!
Ce sont les annotations qui le sont éventuellement.
la réponse est probablement parce que dans le premier cas il y a deux
objets restreints (f et p), et que dans le second il n'y en plus
qu'un, f : le qualificatif ajouté par le transtypage ne créant pas
d'objet nouveau.
(Et chapeau au passage aux programmeurs du dit compilateur qui ont su
faire le distinguo.)
Il serait plus logique en fait après réflexion que gcc s'apercoit que
je dis que une expression dérivée d'un pointeur restrict (p) est
restrict, ce qui est invalide. Et que gcc décide dans le doute d'ignorer
les attributs en question sur p et cette expression :)
Mais -Wstrict-aliasing=2 n'a rien montré malheureusement.
[0] Bien sur vu que ce que je fais est invalide (j'assigne un pointeur
restrict dans un autre qui s'aliasent ce qui est interdit!) il
*aurait le droit* de compiler ça en return system("rm -rf /")… mais
on va dire que les devs de GCC sont rationnels (quoi que…)
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
int foo(struct foo *f) { unsigned *p = get_ptr(f);
f->a = 0; container_of(p, struct foo, b)->a = 1;
Une façon tarabiscotée d'écrire
fonction_pouvant_retouner_f()->a = 1;
return f->a; }
Est-ce que le compilateur a le droit d'optimiser le "return f->a" en "return 0" ?
Je dirais non, dans les conditions ci-dessus qui font que l'on ne peut pas faire d'hypothèse sur la fonction externe get_ptr(), et les éventuelles relations entre f et p.
Je dirais que §6.5.7 me le garantit via:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types: […] - an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or a character type.
Euh.... f->a est donc bien un objet qui peut être accédé, et même modifié. J'ai peur de ne pas suivre ton raisonnement, je lis plutôt la garantie du contraire...
Oui pardon, je voulais dire "me garantit que l'optimisation est impossible". Je ne me suis pas relu :)
D'ailleurs pour faire en sorte que gcc génère 'return 0' il faut ajouter les restricts suivants:
int foo(struct foo *restrict f) { unsigned *restrict p = get_ptr(f);
Là c'est différent, les restrictions spécifient (au moins dans l'esprit) que p ne peut pas valoir &(f->b), et donc que container_of() ne peut pas valoir f. Ce qui autorise l'optimisation.
Ah oui anéfé, container_of() est une expression dérivée de 'p' qui est restrict, 'f' aussi. facile.
J'avoue que j'ai du mal à rationaliser le pourquoi du comment par contre. Je m'attendais à ce que ce code retourne 0 aussi, mais ce n'est pas le cas:
int foo(struct foo *restrict f) { unsigned *p = get_ptr(f);
Rappel : restrict n'est jamais qu'une indication, pas une instruction pour forcer le compilateur à faire des optimisations douteuses.
Je sais bien :)
Si la fonction ne retourne pas 0, c'est : - soit que le compilateur génère du code faux (j'ai pas lu l'assembleur mais je suppose que ce n'est pas le cas)
Non il recharche f->a et le retourne. De toute façon il a le choix essentiellement entre ça et retourner 0 dans les options qui ont du sens[0].
- soit que le processeur est bogué ou vicié d'une manière ou d'une autre (genre mettre un point d'arrêt et forcer f->a à valoir 42)
cf mon [0] oui :)
Maintenant, si ta question est, « en supposant que get_ptr soit la fonction ci-dessus et que donc je mentes au compilateur, pourquoi est-ce qu'il fait l'optimisation (incorrecte) dans le premier cas et pas dans le second ? »,
Dans le premier cas elle n'est pas incorrecte au vu de mes annotations! Ce sont les annotations qui le sont éventuellement.
la réponse est probablement parce que dans le premier cas il y a deux objets restreints (f et p), et que dans le second il n'y en plus qu'un, f : le qualificatif ajouté par le transtypage ne créant pas d'objet nouveau. (Et chapeau au passage aux programmeurs du dit compilateur qui ont su faire le distinguo.)
Il serait plus logique en fait après réflexion que gcc s'apercoit que je dis que une expression dérivée d'un pointeur restrict (p) est restrict, ce qui est invalide. Et que gcc décide dans le doute d'ignorer les attributs en question sur p et cette expression :)
Mais -Wstrict-aliasing=2 n'a rien montré malheureusement.
[0] Bien sur vu que ce que je fais est invalide (j'assigne un pointeur restrict dans un autre qui s'aliasent ce qui est interdit!) il *aurait le droit* de compiler ça en return system("rm -rf /")… mais on va dire que les devs de GCC sont rationnels (quoi que…) -- ·O· Pierre Habouzit ··O OOO http://www.madism.org