2013-06-04 6 views
12

Si consideri il seguente:Questa ottimizzazione ridondante di carico/archivio è consentita in C99?

extern void bar(int *restrict); 

void foo(int *restrict p) { 
    int tmp; 
    bar(&tmp); 
    *p = tmp; 
} 

fa C99 permesso di specifiche per ottimizzare foo al seguente?

Ho provato gcc, Clang e Intel Compiler in modalità -O3 e nessuno dei due ha generato il codice che riflette l'ottimizzazione di cui sopra. Questo mi porta a sospettare che questa ottimizzazione rompa le specifiche. Se non è permesso, dove lo dice nelle specifiche?

Nota: la mia domanda è ispirato this SO question

+1

Vedo che questi frammenti potrebbero non essere equivalenti tra loro se non fossero indicatori "limitati". Ma non so perché questi non vengano ottimizzati nel caso "restrict". –

+0

Cosa succede se 'pippo' ottiene un puntatore alla memoria di sola scrittura (ad esempio, I/O mappato in memoria)? Senza l'ottimizzazione è OK, ma no con esso - 'bar' può scrivere-leggere-scrivere. – ugoren

+2

@ugoren: per tali puntatori, è assolutamente necessario utilizzare il qualificatore 'volatile'. Altrimenti, al compilatore è permesso di ottimizzarlo comunque (in alcuni casi, come conoscere il corpo di 'bar'). – jpalecek

risposta

18

La risposta è un NO definitivo, questo non è consentito.

Considerare cosa succede se foo e bar sono reciprocamente ricorsivi. Ad esempio, questa implementazione di bar:

void bar(int *restrict p) 
{ 
    static int q; 
    if (p == &q) { 
     printf("pointers match!\n"); 
    } else if (p == NULL) { 
     foo(&q); 
    } 
} 

bar mai dereferenzia p, quindi la qualificazione limitare è irrilevante. È ovvio che la variabile statica q non può avere lo stesso indirizzo della variabile automatica tmp in foo. Pertanto, foo non può riportare il parametro su bar e l'ottimizzazione indicata non è consentita.

+0

Ottima risposta, grazie. –

+1

Ottimo! Una "barra" più semplice che si comporta diversamente con l'ottimizzazione potrebbe essere "void bar (int * restrict p) {if (p == NULL) printf (" NULL! \ N ");}" –

+1

@GiuseppeGuerrini: ma sarebbe solo fare la differenza quando l'argomento di foo è NULL, che invocherà un comportamento indefinito. –

0

I due codici non sono equivalenti: nel primo caso, la funzione di "bar" riceve il puntatore (e probabilmente utilizza il valore) di "tmp", che è una variabile locale (e non initalizzata!). Nel secondo caso, "bar" funziona direttamente su "p", e in generale troverà un valore diverso in "* p". Le cose sarebbero diverse se la funzione "bar" dichiarasse il suo parametro come "solo output" (ad esempio in M ​​$ VS tramite la macro OUT), poiché il valore iniziale della variabile sarebbe (presumibilmente essere) ignorato. (NOTA:. Maggior parte delle versioni VS in realtà definiscono la macro OUT come niente affatto troppo triste ...)

+0

Perché pensi che la barra legga dal suo argomento prima di scriverlo? Vediamo solo i casi in cui non esiste un comportamento indefinito che copre le specifiche C99. –

+0

Il problema è che THE COMPILER non può assumere che "bar" scriva * p prima di leggerlo. Ecco perché non può rimuovere il riferimento a "tmp". –

+2

@GiuseppeGuerrini Penso che il compilatore possa supporre che se 'bar' usa il suo argomento, allora deve prima scriverlo. Se 'bar' legge prima che scriva, il comportamento non è definito, quindi tutto il compilatore è valido. Se 'bar' scrive per primo, entrambe le varianti producono lo stesso comportamento osservabile, per quanto posso vedere. –

1

per il compilatore di fare l'ottimizzazione, si deve essere sicuri che a prescindere da come bar è implementato, e come foo viene chiamato, il comportamento ben definito non cambierà.
Poiché l'implementazione di bar e la chiamata a foo sono sconosciute al compilatore, quando compila foo, l'esistenza teorica di tale caso è sufficiente per impedire l'ottimizzazione, anche se non si verifica nella realtà.

Ecco un esempio per una situazione del genere. I punti importanti sono: 1. Il parametro p punta alla memoria di sola scrittura (ad esempio, I/O mappato in memoria).
2. bar non è sicuro per l'utilizzo con un puntatore di sola scrittura (forse lo scrive e quindi lo legge di nuovo, in attesa dello stesso valore).
La funzione foo è sicura per l'uso con un puntatore di sola scrittura, poiché scrive solo p. Questo è vero anche se bar non è sicuro, perché bar non ottiene mai p. Con l'opzione suggerita, bar ottiene p, che potrebbe causare problemi.

Ecco un esempio per il file contenente bar e la chiamata a foo.

static int increment; 

void bar(int *restrict p) { 
    (*p)=0; 
    if (increment) (*p)++; 
} 

void foo(int *restrict p); 

int main(int ac, char **av) { 
    int *p = get_io_addr(); /* Get a write-only memory mapped I/O address */ 
    increment = atoi(av[1]); 
    foo(p); 
    return 0; 
} 
+1

Buon punto. Ma si noti che un compilatore (in realtà molto molto intelligente) potrebbe tradurre "bar" in qualcosa come "* p = incrementare? 1: 0;".A meno che il puntatore non sia "volatile", ma non è il nostro caso :-( –

+0

@GiuseppeGuerrini quindi GCC è un compilatore molto molto intelligente :) –

+0

@zr. ovviamente! :-) –

1

Una breve lettura di this SO question e this wikipedia enrty suggerisce che la parola limitare può avere effetto solo negli argomenti di una funzione. Tuttavia, leggendo the C99 standard, in particolare la sezione 6.7.3.1, è evidente che lo restrict si applica all'intero contesto in cui viene utilizzato lo restrict. Quindi, utilizzando

void foo(int *restrict p); 

si sta garantendo che l'unica lettura e la scrittura nel blocco di memoria puntato da p sarà via p.

Tuttavia, anche con queste informazioni, durante la compilazione di foo, il compilatore non ha idea di cosa farà bar con le informazioni che viene inviato.Ad esempio, si consideri:

void bar (unsigned long long int *p) { 
    *p = ((unsigned long long int) p) % 2000; 
    } 

Il risultato dipende dal valore del set puntatore, il che significa che durante la compilazione foo l'ipotesi di ottimizzazione suggerisci non può definitivamente essere fatta, come il risultato sarà diverso se l'ottimizzazione si suggerire è fatto.

+0

'restrict' non riguarda solo i parametri per la stessa funzione . Ad esempio, dando 'foo' un puntatore a una variabile globale, a cui si accede in' bar', viola il requisito di 'restrict'. – ugoren

+0

Punto giusto, la risposta verrà aggiornata. –

+0

Inoltre, il tuo ultimo esempio ha due problemi: in primo luogo, non puoi applicare '%' a un puntatore. Secondo, il comportamento non è ben definito con 'foo' non ottimizzato, quindi l'ottimizzazione non infrange nulla. – ugoren