Question

Dans le passé, j'ai déjà travaillé sur des projets de systèmes embarqués dans lesquels nous avions réarrangé l'ordre de déclaration des variables de pile afin de réduire la taille de l'exécutable obtenu. Par exemple, si nous avions:

void func()
{
    char c;
    int i;
    short s;
    ...
}

Nous réorganiserions cette commande comme suit:

void func()
{
    int i;
    short s;
    char c;
    ...
}

En raison de problèmes d'alignement, le premier entraînait l'utilisation de 12 octets d'espace de pile et le second, de 8 octets seulement.

Ce comportement standard pour les compilateurs C ou juste une lacune du compilateur que nous utilisions?

Il me semble qu’un compilateur devrait pouvoir réorganiser les variables de la pile de manière à privilégier une taille d’exécutable plus petite s’il le souhaite. On m'a suggéré que certains aspects de la norme C empêchent cela, mais je n'ai pas été en mesure de trouver une source fiable, de toute façon.

En prime, cela s'applique-t-il également aux compilateurs C ++?

Modifier

Si la réponse est oui, les compilateurs C / C ++ peuvent réorganiser les variables de pile, pouvez-vous donner un exemple de compilateur qui fait cela? J'aimerais voir la documentation du compilateur ou quelque chose de similaire qui sauvegarde cela.

Modifier à nouveau

Merci à tous pour votre aide. Pour la documentation, la meilleure chose que j’ai pu trouver est le document Attribution optimale des emplacements de pile dans GCC (pdf), par Naveen Sharma et Sanjiv Kumar Gupta, présentée lors de la réunion au sommet de GCC en 2003.

Le projet en question ici utilisait le compilateur ADS pour le développement ARM. Il est mentionné dans la documentation de ce compilateur que les déclarations de commande, comme je l’ai déjà montré, peuvent améliorer les performances, ainsi que la taille de la pile, en raison de la manière dont l’architecture ARM-Thumb calcule les adresses dans le cadre de pile local. Ce compilateur n'a pas automatiquement réorganisé les sections locales pour en tirer parti. Le document lié indique qu'en 2003, GCC n'avait pas non plus réorganisé le cadre de la pile pour améliorer la localité de référence des processeurs ARM-Thumb, mais cela implique que vous pouvez le faire.

Je ne trouve rien qui indique clairement que cela a déjà été mis en œuvre dans GCC, mais je pense que cet article constitue une preuve que vous avez tous raison. Merci encore.

Était-ce utile?

La solution

Comme rien dans la norme n'interdit cela aux compilateurs C ou C ++, le compilateur peut le faire.

Il en va différemment pour les agrégats (c'est-à-dire les structures), où l'ordre relatif doit être maintenu, mais le compilateur peut néanmoins insérer des octets de protection pour obtenir un alignement préférable.

Les nouveaux compilateurs MSVC du IIRC utilisent cette liberté dans leur lutte contre les débordements de tampon des locaux.

Remarque: en C ++, l'ordre de destruction doit être un ordre de déclaration inverse, même si le compilateur réorganise la structure de la mémoire.

(Je ne peux pas citer le chapitre et le verset, cependant, cela vient de la mémoire.)

Autres conseils

Non seulement le compilateur peut réorganiser la pile des variables locales, il peut les affecter à des registres, les assigner à vivre parfois dans des registres et parfois sur la pile, il peut affecter deux locaux à un même emplacement en mémoire (si leurs plages actives ne se chevauchent pas) et il peut même éliminer complètement les variables.

La pile n’a même pas besoin d’exister (en fait, le mot "pile" n’est pas mentionné dans la norme C99). Alors oui, le compilateur est libre de faire ce qu'il veut tant que cela préserve la sémantique des variables avec une durée de stockage automatique.

Comme pour un exemple: j'ai souvent rencontré une situation dans laquelle je ne pouvais pas afficher de variable locale dans le débogueur car elle était stockée dans un registre.

Le compilateur est même libre de retirer la variable de la pile et de ne l'enregistrer que si l'analyse montre que l'adresse de la variable n'est jamais prise / utilisée.

Un compilateur peut même ne pas utiliser de pile de données. Si vous êtes sur une plate-forme si petite que vous vous inquiétez de 8 vs 12 octets de pile, il est probable que des compilateurs auront des approches assez spécialisées. (Certains compilateurs PIC et 8051 me viennent à l’esprit)

Pour quel processeur compilez-vous?

Le compilateur de la série 62xx de DSP Texas Instruments est capable de, et ne "Optimisation de l'ensemble du programme". (vous pouvez l'éteindre)

C’est là que votre code est réorganisé, pas seulement les locaux. Donc, l'ordre d'exécution finit par ne pas être tout à fait à la hauteur de vos attentes.

Les langages C ++ et C ++ ne pas promettent en réalité un modèle de mémoire (au sens de la JVM, par exemple), les choses peuvent donc être très différentes et toujours légales.

Pour ceux qui ne les connaissent pas, la famille 62xx compte 8 instructions par cycle DSP; à 750Mhz, ils culminent à 6e + 9 instructions. Une partie du temps quand même. Ils effectuent une exécution en parallèle, mais la commande des instructions se fait dans le compilateur, pas dans la CPU, comme dans un Intel x86.

Les cartes intégrées PIC et Rabbit ne n'ont pas de piles, sauf si vous leur demandez très gentiment.

ce sont les spécificités du compilateur, on peut faire son propre compilateur qui ferait l'inverse s'il le voulait ainsi.

Un compilateur décent mettra des variables locales dans des registres s’il le peut. Les variables ne doivent être placées dans la pile que s'il existe une pression de registre excessive (espace insuffisant) ou si l'adresse de la variable est prise, ce qui signifie qu'elle doit vivre en mémoire.

Pour autant que je sache, rien n’indique que les variables doivent être placées à un emplacement ou un alignement spécifique sur la pile pour C / C ++; le compilateur les placera là où les performances sont les meilleures et / ou ce qui convient aux rédacteurs du compilateur.

Pour autant que je sache, rien dans la définition de C ou C ++ ne spécifie comment le compilateur doit ordonner les variables locales sur la pile. Je dirais que se fier à ce que le compilateur peut faire dans ce cas est une mauvaise idée, car la prochaine version de votre compilateur peut le faire différemment. Si vous passez du temps et vous efforcez-vous d’ordonner à vos variables locales de ne conserver que quelques octets de pile, il est préférable que ces quelques octets soient réellement essentiels au fonctionnement de votre système.

Inutile de spéculer sur ce que le standard C demande ou n’exige pas: les versions les plus récentes sont disponibles gratuitement en ligne sur le site Web Groupe de travail ANSI / ISO .

Cela ne répond pas à votre question, mais voici mes 2 centimes sur un problème connexe ...

Je n’ai pas eu le problème de l’optimisation de l’espace de la pile mais j’ai eu le problème du mauvais alignement des variables doubles sur la pile. Une fonction peut être appelée à partir de n'importe quelle autre fonction et la valeur du pointeur de pile peut avoir n'importe quelle valeur non alignée. J'ai donc eu l'idée ci-dessous. Ce n'est pas le code original, je viens de l'écrire ...

#pragma pack(push, 16)

typedef struct _S_speedy_struct{

 double fval[4];
 int64  lval[4];
 int32  ival[8];

}S_speedy_struct;

#pragma pack(pop)

int function(...)
{
  int i, t, rv;
  S_speedy_struct *ptr;
  char buff[112]; // sizeof(struct) + alignment

  // ugly , I know , but it works...
  t = (int)buff;
  t +=  15; // alignment - 1
  t &= -16; // alignment
  ptr = (S_speedy_struct *)t;

  // speedy code goes on...
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top