2016-07-07 3 views
0

Un aliasing strict me rend paranoïaque. Il y a des moments où je place des valeurs avec un pointeur de * int et m'attends à ce que la mémoire ciblée lise les mêmes données quel que soit le type de pointeur de lecture. L'aliasing strict ne le garantit pas et parfois même ne le fait pas. Si je lis un char [] dans une boucle et qu'il y a un int intériorisant quelque chose dans ce tableau char [], je brise les règles d'alias parmi d'autres choses C standard.memcpy alias int à char donne UB?

Je fais un compilateur JIT et puisque j'utilise x86 je suis sûr que je n'ai pas à m'inquiéter d'int-alignement. Gardons cela hors de l'équation jusqu'à ce que nous ayons résolu le problème d'aliasing.

Considérez cet extrait:

unsigned char x86[] = {0x11, 0x44, 0x42, ... }; 
uint32_t *specific_imm = (x86+10); 

Maintenant, * specific_imm = 42; sur une plate-forme x86 est toujours UB parce que le compilateur est autorisé à supposer que * specific_imm n'est pas aliasing avec x86 []. En faisant cette supposition, il n'a pas besoin de définir ces octets tout de suite mais peut faire toutes sortes d'optimisations. Mettre x86 [] et * specific_imm à la fois comme volatile résoudrait mon problème mais ce n'est pas assez bon puisque je veux apprendre C correctement.

Nous avons résolu le problème d'aliasing maintenant. Certains suggèrent cette solution: memcpy (x86 + 10, specific_imm, 4);

Mais la norme C semble avoir un problème avec cela aussi concernant les pointeurs d'alias (si j'ai bien compris les choses) comme illustré par le code suivant.

/* naive implementation of memcpy */ 
inline void _memcpy(unsigned char *a, unsigned char *b){ 
    *a = *b; 
} 

int main(void) { 
    long i = 0xFFFFFFFF; 
    unsigned char c = 1; 
    ++i; 
    _memcpy(&c,&i); 
    return c; 
} 

Depuis le compilateur est libre de supposer que « i » ne touche pas les Ic en quelque sorte dans ce cas (?), Le principal est libre d'être optimisé pour revenir à seulement 1?

Je suis plus intéressé à résoudre le problème avant de passer directement aux solutions.

Merci d'avance

+1

Je vous suggère de vous corriger par exemple, vous avez probablement vu mes commentaires dans la réponse: La valeur de i n'est pas évidente. – 2501

Répondre

1

En faisant cette hypothèse, il n'a pas besoin de définir ces octets tout de suite, mais peut faire toutes sortes d'optimisations

Il n'a pas besoin de les mettre du tout. Il peut faire n'importe quoi.


Réglage x86 [] et * specific_imm serait aussi volatile résoudre mon problème

Pas vraiment. L'aliasing strict indique qu'une certaine variable peut ne pas être changée par des pointeurs vers des types non liés. Cela entraîne votre programme à faire des choses non spécifiées par la norme. Habituellement, cela se manifeste dans divers bogues liés à l'optimiseur, mais pas nécessairement.Le programme pourrait aussi bien ne rien faire, ou se briser et brûler.

volatile ne résoudra pas cela (d'autant plus que vous déclarez le pointeur comme quelque chose pointant vers volatile données, plutôt que de faire la variable de données réelles volatile).

Certains compilateurs comme GCC optimisent le code en supposant que votre programme ne violera jamais l'aliasing strict (et donc invoquera un comportement indéfini). Mais cela ne signifie pas que l'arrêt de l'optimisation supprimera le comportement indéfini lui-même, cela désactivera seulement la confiance de l'optimiseur qui suppose que votre programme n'invoque pas de comportement indéfini. Cela ne réglera pas le bug réel.


Certains suggèrent cette solution: memcpy

Cela permettra de résoudre le problème, en raison des règles de de type efficace. 6,5/6:

Si une valeur est copiée dans un objet ayant aucun type déclaré en utilisant memcpy ou memmove, ou est copié dans un tableau de type de caractère, puis du type efficace de la modification objet pour cet accès et pour les accès suivants qui ne modifient pas la valeur est le type effectif de l'objet à partir duquel la valeur est copiée, s'il en a un.

Cela répond à la première partie de la règle stricte de aliasing, 6.5/7:

Un objet doit avoir sa valeur stockée accessible uniquement par une expression lvalue qui est l'un des les types suivants:

- un type compatible avec le type effective de l'objet,


Mais la norme C semble avoir un problème avec cela aussi en ce qui concerne les pointeurs d'aliasing (si je comprends les choses correctement)

Non, ce n'est pas correct. La fonction memcpy réelle utilise des pointeurs vides et ne peut pas violer l'aliasing strict pour les raisons citées ci-dessus. Votre version maison utilise unsigned char*, ce qui est bien aussi, par 6.5/7:

- un type de caractère.

Veuillez lire What is the strict aliasing rule?, en particulier this answer.

+0

Si les auteurs de la norme avaient spécifié que lorsque l'opérande source de 'memcpy' est un type autre que 'void *', le type de pointeur doit être approprié à la source, et de même pour la destination, qui aurait répondu à la plupart des utilisations de memcpy pour le type punning sans exiger des suppositions aliasing pessimistes. Malheureusement, les règles actuelles pour 'memcpy' permettent des opportunités pour le méfait du compilateur sans permettre beaucoup d'opportunités pour des optimisations utiles. – supercat

1

Vous avez tord. Un compilateur C peut pas supposer qu'un pointeur arbitraire et un pointeur vers une variation de char ne sont pas aliasés. Il ne peut pas non plus supposer que deux pointeurs sur int sign et unsigned, ou deux pointeurs sur signed et unsigned long etc. ne sont pas alignés. Dans votre dernier exemple, n'importe quel développeur de logiciel sain a ses avertissements de compilateur configurés de telle sorte que cela ne soit pas compilé.

+0

'ne sont pas alignés' devrait-il être' ne pas avoir d'alias'? ou est-ce que je manque quelque chose d'important ici? – 4386427

+0

Vous pouvez obtenir UB sans avertissements avec GCC concernant les pointeurs d'alias. – jdoeblink33

+0

@ jdoeblink33: Vous pouvez également obtenir un comportement faux dans les cas où gcc ignore les aspects de la norme qu'ils n'aiment pas (par ex.si le code doit pouvoir accéder à la séquence initiale commune de plusieurs types de structure, la norme spécifie que déclarer un type d'union qui contient ces structures permettra à un tel type de lire les membres d'un autre type, mais puisque les auteurs de gcc n'aime pas cette règle, ils l'ignorent. – supercat