web-dev-qa-db-fra.com

Optimisation inattendue de SHLEN lors de l'aliasing 2-D gray

Voici mon code:

#include <string.h>
#include <stdio.h>

typedef char BUF[8];

typedef struct
{
    BUF b[23];
} S;

S s;

int main()
{
    int n;

    memcpy(&s, "1234567812345678", 17);

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

    n = strlen((char *)&s) / sizeof(BUF);
    printf("%d\n", n);
}

En utilisant GCC 8.3.0 ou 8.2.1 avec tout niveau d'optimisation sauf -O0, cette sortie 0 2 quand je m'attendais à 2 2. Le compilateur a décidé que le strlen est limité à b[0] Et donc ne peut jamais égaler ou dépasser la valeur divisée par.

Est-ce un bogue dans mon code ou un bug dans le compilateur?

Cela n'est pas clairement défini dans la norme, mais je pensais que l'interprétation traditionnelle de la provenance du pointeur était que pour tout objet X, le code (char *)&X Devrait générer un pointeur capable de se déplacer sur l'ensemble de X - ce concept devrait tenir même si X arrive à avoir des sous-tableaux en tant que structure interne.

(Question bonus, existe-t-il un drapeau de la GCC pour désactiver cette optimisation spécifique?)

36
M.M

J'ai vérifié cela et reproduit avec -O1 On GCC 8.3, donc je viens d'ouvrir la liste des drapeaux d'optimisation GCC ici et a commencé à expérimenter avec eux par un. Il s'est avéré que la désactivation seulement propagation constante conditionnelle clause avec -fno-tree-ccp Faites disparaître le problème (OH Luck, j'ai prévu de tester des couples de drapeaux si le test d'un à un ne donne aucun résultat).

Ensuite, je suis passé à -O2 Mais n'a pas effacé -fno-tree-ccp Drapeau. Il reproduit à nouveau. J'ai dit "OK" et je viens de commencer à tester des drapeaux supplémentaires -O2. Il est à nouveau apparu que désactivation de la propagation de la gamme simple Value Plage Datay est confiée à la sortie 2 2. J'ai ensuite effacé que le premier drapeau -fno-tree-ccp, Mais cela a commencé à reproduire à nouveau. Donc, pour -O2 Vous pouvez spécifier -O2 -fno-tree-ccp -fno-tree-vrp Pour faire fonctionner le programme YOR comme prévu.

Je n'ai pas effacé ces drapeaux, mais je suis passé à -O3 Alors. Problème n'a pas reproduit.

Donc, ces deux techniques d'optimisation de GCC 8.3 conduisent à un comportement aussi étrange (peut-être qu'ils utilisent quelque chose de commun en interne):

  • Propagation constante conditionnelle séparée sur les arbres
  • Plage de valeurs propagation sur les arbres

Je ne suis pas pro dans tout ce genre de choses pour expliquer quoi et pourquoi se passe-t-il là-bas, peut-être que quelqu'un d'autre pourrait expliquer. Mais assurez-vous que vous pouvez spécifier des indicateurs de -fno-tree-ccp -fno-tree-vrp Pour désactiver ces techniques Optimizaton pour que votre code fonctionne comme prévu.

"Le plus fort que je travaille, le plus chanceux que je reçois." - Samuel Goldwyn

edit

AS @ kamilcuk noté dans les commentaires de la question, -fno-builtin-strlen Conduit également à un comportement entité aussi, il existe donc probablement un bogue de compilateur en combinaison de intégré strlen et une autre optimisation, qui est destiné à couper le code mort, déterminez statilement des valeurs d'expression possibles et propager des constantes à travers un programme. Je pensais que le compilateur considérait très probablement à tort quelque chose, qui détermine la longueur de la chaîne dans son strlen _ Mise en œuvre (peut-être en combinaison avec Division entière et/ou Archauffes bidimensionnels) en tant que code mort et coupez-le ou calculés comme 0 au moment de la compilation. J'ai donc décidé de jouer un peu avec le code pour vérifier les théories et éliminer d'autres "participants" possibles du bogue. Je suis arrivé à cet exemple minimum du comportement qui a confirmé mes pensées:

int main()
{
    // note that "7" - inner arrays size, you can put any other number here
    char b[23][7]; // local variable, no structs, no typedefs
    memcpy(&b[0][0], "12345678123456781234", 21);

    printf("%d\n", strlen(&b[0][0]) / 8); // greater than that "7" !!!
    printf("%d\n", strlen(&b[0][0]) / 7);
    printf("%d\n", strlen(&b[0][0]) / 6); // less than that "7" !!!
    printf("%d\n", strlen(&b[0][0])); // without division
}

3

20

Je pense que nous pouvons considérer cela un bug in GCC.

Je pense que -fno-builtin-strlen Est une meilleure solution pour le problème, car cela fonctionne pour tous les niveaux d'optimisation seul et intégré strlen semble être une technique d'optimisation moins puissante, surtout si votre programme n'utilise pas strlen() beaucoup. Toujours -fno-tree-ccp -fno-tree-vrp Est également une option.

1
Oliort

La façon dont vous avez défini la structure m'a confondue au début parce que je ne pense pas avoir jamais essayé de créer un type de tableau. Le fait de cette façon peut être dangereux aussi parce que si quelqu'un a essayé de la transmettre à une fonction, ils pourraient penser qu'ils passent de valeur mais réussiraient par référence. Quel que soit le style, si je devais créer un type comme ça, je ferais quelque chose comme:

//typedef char BUF[8];

//do it this way instead
typedef struct
{
    char x[8];
} BUF;

typedef struct
{
    BUF b[23];
} S;

Si je définis cela de cette façon, il retourne la valeur attendue de chaque sens. Voir - ICI .

0
PiMaker0