2014-10-13 10 views
10

Si consideri il seguente codice:`const T * restrict` garantisce che l'oggetto puntato non venga modificato?

void doesnt_modify(const int *); 

int foo(int *n) { 
    *n = 42; 
    doesnt_modify(n); 
    return *n; 
} 

in cui la definizione di doesnt_modify non è visibile per il compilatore. Pertanto, è necessario supporre che doesnt_modify modifichi l'oggetto n punti e debba leggere *n prima dello return (l'ultima riga non può essere sostituita da return 42;).

Assumendo, doesnt_modify non modifica *n. Ho pensato a quanto segue per consentire l'ottimizzazione:

int foo_r(int *n) { 
    *n = 42; 
    { /* New scope is important, I think. */ 
     const int *restrict n_restr = n; 
     doesnt_modify(n_restr); 
     return *n_restr; 
    } 
} 

Questo ha lo svantaggio che il chiamante di doesnt_modify deve dire al compilatore *n non viene modificato, piuttosto che la funzione stessa potrebbe dire al compilatore tramite il suo prototipo . Semplicemente restrict -qualificare il parametro su doesnt_modify nella dichiarazione non è sufficiente, cfr. “Is top-level volatile or restrict significant [...]?”.

Durante la compilazione con gcc -std=c99 -O3 -S (o Clang con le stesse opzioni), tutte le funzioni sono compilate per l'assemblaggio equivalente, tutte rilette lo da *n.

  1. Sarebbe un compilatore essere consentito di fare questa ottimizzazione (sostituire l'ultima riga da return 42;) per foo_r? In caso contrario, c'è un modo (portatile, se possibile) per dire al compilatore doesnt_modify non modifica a cosa punta il suo argomento? C'è un modo in cui i compilatori capiscono e fanno uso di?

  2. C'è qualche funzione in UB (purché doesnt_modify non modifichi il punto d'impatto del suo argomento)?

Perché penso, restrict potrebbe aiuto qui (da C11 (n1570) 6.7.3.1 “Definizione formale di restrict”, p4 [emph mio.]):

[In questo caso, è B il blocco interno di foo_r, P è n_restr, T è const int, e X è l'oggetto denotato da *n, credo.]

Durante ogni exec ution di B, lasciare L un qualsiasi valore che ha &L basato su P. Se L è utilizzato per accedere al valore dell'oggetto X che designa, e X è anche modificato (con qualsiasi mezzo), quindi i seguenti requisiti si applicano: T non sarà const qualificati. [...]

$ clang --version 
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) (based on LLVM 3.5.0) 
Target: x86_64-pc-linux-gnu 

versione di gcc 4.9.2 è, su un target x86 a 32 bit.

+0

È legale per 'doesnt_modify2' per escludere la costanza e modificare l'oggetto puntato, a condizione che l'oggetto stesso non sia dichiarato' const'. –

+0

@ T.C. Sì, questo è il punto. La domanda è, se 'restrict' fa la differenza qui. – mafso

+1

Interessante: prometti che nient'altro lo alias e che questo puntatore non può modificarlo. Sono molto curioso di vedere cosa significa. – templatetypedef

risposta

8

La versione 1 sembra chiaramente specificata dalla definizione formale di restrict (C11 6.7.3.1).Per il codice seguente:

const int *restrict P = n; 
doesnt_modify(P); 
return *P; 

i simboli utilizzati in 6.7.3.1 sono:

  • B - quel blocco di codice
  • P - la variabile P
  • T - il tipo di *P che è const int
  • X - il (non-const) int essere puntato da P
  • L - il valore assegnabile *P è ciò che ci interessa

6.7.3.1/4 (parziale):

Durante ogni esecuzione di B, lasciate L essere qualsiasi Ivalue che ha &L basato su P. Se L è utilizzato per accedere al valore dell'oggetto X che designa, e X è anche modificato (con qualsiasi mezzo), allora i seguenti requisiti si applicano: T non sarà const qualificati [...] se questi requisiti non sono soddisfatti, quindi il comportamento non è definito.

Si noti che T è const-qualificato. Pertanto, se X viene modificato in qualsiasi modo durante questo blocco (che include durante la chiamata a una funzione in quel blocco), il comportamento non è definito.

Pertanto il compilatore può ottimizzare come se doesnt_modify non abbia modificato X.


La versione 2 è un po 'più difficile per il compilatore. 6.7.6.3/15 afferma che i qualificatori di primo livello non sono considerati nella compatibilità dei prototipi, anche se non vengono ignorati completamente.

Quindi, anche se il prototipo dice:

void doesnt_modify2(const int *restrict p); 

potrebbe ancora essere che il corpo della funzione è dichiarata come void doesnt_modify2(const int *p) e quindi potrebbe modificare *p.

La mia conclusione è che, se e solo se il compilatore può vedere la definizione per doesnt_modify2 e confermare che p viene dichiarata restrict nella lista dei parametri della definizione di allora sarebbe in grado di eseguire l'ottimizzazione.

+0

Potrei sbagliarmi, ma penso che 'const' possa essere gettato via legalmente. Il puntatore risultante sarebbe ancora "basato su" il puntatore originale e quindi non sarebbe vincolato da 'restrict'. L'unico caso in cui ciò provocherebbe UB sarebbe se l'oggetto originale (puntato) fosse dichiarato 'const'. – davmac

+0

Strike that. Mi era sfuggito il significato di "T non dovrebbe essere const-qualificato" in 6.7.3.1. – davmac

+0

Minore: 6.7.6.3/13 riguarda gli identificatori della classe di archiviazione (l'unico consentito negli argomenti della funzione è 'register'), non riguarda i qualificatori, e quindi non si applica (sebbene io sia d'accordo con il punto, livello principale i qualificatori di solito vengono ignorati qui, non mi è chiaro dove lo standard lo prescrive, ma è per lo meno un'interpretazione comune). – mafso

1

In genere, restrict significa che il puntatore non è aliasato (ovvero solo esso o un puntatore derivato da esso può essere utilizzato per accedere all'oggetto puntato).

Con const, ciò significa che l'oggetto puntato non può essere modificato da un codice ben formato.

Tuttavia, non c'è nulla che impedisca al programmatore di violare le regole utilizzando una conversione di tipo esplicito per rimuovere const ness. Quindi il compilatore (essendo stato battuto in sottomissione dal programmatore) consentirà un tentativo di modificare l'oggetto puntato senza alcun reclamo. Questo, in senso stretto, si traduce in un comportamento indefinito, quindi qualsiasi risultato immaginabile è quindi consentito, incluso - eventualmente - la modifica dell'oggetto puntato.

1

In caso contrario, esiste un modo (portatile, se possibile) per indicare al compilatore doesnt_modify non modifica a cosa punta il suo argomento?

Nessun modo.

Gli ottimizzatori del compilatore hanno difficoltà di ottimizzazione quando sono coinvolti i parametri di funzione di puntatore e riferimento. Poiché l'implementazione di tale funzione può escludere i compilatori di constness, supponiamo che T const* sia scadente come T*.

Quindi, nel tuo esempio, dopo la chiamata doesnt_modify(n), è necessario ricaricare *n dalla memoria.

Vedere 2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++. Si applica anche a C.

L'aggiunta della parola chiave restrict qui non modifica quanto sopra.

Problemi correlati