Domanda

In passato ho lavorato a progetti per sistemi embedded in cui abbiamo riordinato l'ordine di dichiarazione delle variabili dello stack per ridurre la dimensione dell'eseguibile risultante. Ad esempio, se avessimo:

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

Riordiniamo questo per essere:

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

A causa di problemi di allineamento, il primo ha comportato l'utilizzo di 12 byte di spazio stack e il secondo ha comportato solo 8 byte.

Questo comportamento standard è per i compilatori C o è solo un difetto del compilatore che stavamo usando?

Mi sembra che un compilatore dovrebbe essere in grado di riordinare le variabili dello stack per favorire dimensioni eseguibili più piccole, se lo desidera. Mi è stato suggerito che alcuni aspetti dello standard C impediscono ciò, ma non sono stato in grado di trovare una fonte affidabile in entrambi i casi.

Come domanda bonus, questo vale anche per i compilatori C ++?

Modifica

Se la risposta è sì, i compilatori C / C ++ possono riorganizzare le variabili dello stack, puoi fare un esempio di un compilatore che lo fa sicuramente? Mi piacerebbe vedere la documentazione del compilatore o qualcosa di simile che lo supporta.

Modifica di nuovo

Grazie a tutti per il vostro aiuto. Per la documentazione, la cosa migliore che sono riuscito a trovare è il documento Assegnazione ottimale dello slot di stack in GCC (pdf), di Naveen Sharma e Sanjiv Kumar Gupta, presentata al vertice GCC del 2003.

Il progetto in questione qui stava usando il compilatore ADS per lo sviluppo ARM. Nella documentazione per quel compilatore viene menzionato che l'ordinazione di dichiarazioni come ho mostrato può migliorare le prestazioni, nonché le dimensioni dello stack, a causa del modo in cui l'architettura ARM-Thumb calcola gli indirizzi nel frame dello stack locale. Quel compilatore non riorganizza automaticamente i locali per trarne vantaggio. Il documento qui linkato afferma che a partire dal 2003 GCC non ha riorganizzato il frame dello stack per migliorare la località di riferimento per i processori ARM-Thumb, ma implica che è possibile.

Non riesco a trovare nulla che dica definitivamente che questo sia mai stato implementato in GCC, ma penso che questo documento valga come prova che tutti voi avete ragione. Grazie ancora.

È stato utile?

Soluzione

Poiché non esiste nulla nello standard che vieti che per i compilatori C o C ++, sì, il compilatore può farlo.

È diverso per gli aggregati (cioè le strutture), in cui l'ordine relativo deve essere mantenuto, ma il compilatore può comunque inserire byte pad per ottenere un allineamento preferibile.

I più recenti compilatori MSVC di IIRC usano questa libertà nella loro lotta contro i buffer overflow dei locali.

Come nota a margine, in C ++, l'ordine di distruzione deve essere in ordine inverso di dichiarazione, anche se il compilatore riordina il layout di memoria.

(Non posso citare capitoli e versi, però, questo è dalla memoria.)

Altri suggerimenti

Non solo il compilatore può riordinare il layout dello stack delle variabili locali, ma può assegnarle ai registri, assegnarle per vivere a volte nei registri e talvolta nello stack, ma può assegnare due locali allo stesso slot in memoria (se le loro gamme live non si sovrappongono) e può persino eliminare completamente le variabili.

Lo stack non deve nemmeno esistere (in effetti, lo standard C99 non ha una singola occorrenza della parola "stack"). Quindi sì, il compilatore è libero di fare tutto ciò che vuole purché conservi la semantica delle variabili con durata di memorizzazione automatica.

Ad esempio: ho riscontrato molte volte una situazione in cui non potevo visualizzare una variabile locale nel debugger perché era memorizzata in un registro.

Il compilatore è persino libero di rimuovere la variabile dallo stack e di farla registrare solo se l'analisi mostra che l'indirizzo della variabile non viene mai preso / usato.

Un compilatore potrebbe non utilizzare nemmeno uno stack per i dati. Se sei su una piattaforma così piccola da preoccuparti di circa 8 vs 12 byte di stack, è probabile che ci saranno compilatori con approcci piuttosto specializzati. (Vengono in mente alcuni compilatori PIC e 8051)

Per quale processore stai compilando?

Il compilatore per la serie 62xx di DSP di Texas Instruments è capace e lo fa "ottimizzazione dell'intero programma". (puoi disattivarlo)

Qui è dove il tuo codice viene riorganizzato, non solo i locali. Quindi l'ordine di esecuzione non è proprio quello che ti aspetteresti.

C e C ++ non in realtà promettono un modello di memoria (nel senso di dire la JVM), quindi le cose possono essere abbastanza diverse e ancora legali.

Per coloro che non li conoscono, la famiglia 62xx è composta da 8 istruzioni per DSP del ciclo di clock; a 750Mhz, fanno picco a 6e + 9 istruzioni. Qualche volta comunque. Eseguono un'esecuzione parallela, ma l'ordinamento delle istruzioni viene eseguito nel compilatore, non nella CPU, come un Intel x86.

Le schede madri PIC e Rabbit incorporate non hanno pile a meno che tu non le chieda particolarmente bene.

sono specifiche del compilatore, si può creare il proprio compilatore che farebbe il contrario se lo volesse in questo modo.

Un compilatore decente inserirà le variabili locali nei registri, se possibile. Le variabili devono essere posizionate nello stack solo se c'è una pressione eccessiva nel registro (spazio insufficiente) o se viene preso l'indirizzo della variabile, il che significa che deve vivere nella memoria.

Per quanto ne so, non c'è nulla che dica che le variabili debbano essere posizionate in qualsiasi posizione specifica o allineamento nello stack per C / C ++; il compilatore li metterà dove è meglio per le prestazioni e / o qualunque cosa sia conveniente per gli autori di compilatori.

AFAIK non c'è nulla nella definizione di C o C ++ che specifichi come il compilatore dovrebbe ordinare le variabili locali nello stack. Direi che basarsi su ciò che il compilatore può fare in questo caso è una cattiva idea, perché la prossima versione del compilatore potrebbe farlo diversamente. Se impieghi tempo e sforzi per ordinare alle variabili locali di risparmiare qualche byte di stack, è meglio che quei pochi byte siano davvero fondamentali per il funzionamento del tuo sistema.

Non c'è bisogno di speculazioni inattive su ciò che lo standard C richiede o non richiede: le bozze recenti sono disponibili gratuitamente online dal gruppo di lavoro ANSI / ISO .

Questo non risponde alla tua domanda ma ecco i miei 2 centesimi su un problema correlato ...

Non avevo il problema dell'ottimizzazione dello spazio dello stack, ma avevo il problema di un errato allineamento delle doppie variabili nello stack. Una funzione può essere chiamata da qualsiasi altra funzione e il valore del puntatore dello stack può avere qualsiasi valore non allineato. Quindi ho avuto l'idea qui sotto. Questo non è il codice originale, l'ho appena scritto ...

#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...
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top