Perché LLVM alloca una variabile ridondante?


9

Ecco un semplice file C con una definizione enum e una mainfunzione:

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    return 0;
}

Traspila al seguente IR LLVM:

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 2, i32* %2, align 4
  ret i32 0
}

%2è evidentemente la dvariabile, che gli viene assegnata 2. A cosa %1corrisponde se viene restituito direttamente zero?


1
Quali bandiere hai usato per produrre questo IR?
arrowd

@arrowd, ho installato l'ultima suite LLVM stabile e clang-9 -S -emit-llvm simple.c
ho

1
Penso che abbia a che fare con l'inizializzazione prima main( godbolt.org/z/kEtS-s ). Il collegamento mostra come l'assemblaggio è mappato alla sorgente
Pradeep Kumar l'

2
@PradeepKumar: In effetti, se cambi il nome della funzione in qualcosa di diverso main, la misteriosa variabile aggiuntiva scompare. È interessante notare che scompare anche se si omette completamente la returndichiarazione (che è legale per mainin C ed equivalente a return 0;).
Nate Eldredge

1
@macleginn: non ne sono così sicuro. Se dichiari maincome int main(int argc, char **argv)vedi argce argvcopi sullo stack, ma la misteriosa variabile zero è ancora lì oltre a loro.
Nate Eldredge,

Risposte:


3

Questo %1registro è stato generato da clang per gestire più istruzioni return in una funzione . Immagina di avere una funzione per calcolare il fattoriale di un numero intero. Invece di scriverlo in questo modo

int factorial(int n){
    int result;
    if(n < 2)
      result = 1;
    else{
      result = n * factorial(n-1);
    }
    return result;
}

Probabilmente lo faresti

int factorial(int n){
    if(n < 2)
      return 1;
    return n * factorial(n-1);
}

Perché? Perché Clang inserirà quella resultvariabile che contiene il valore restituito per te. Sìì. Questo è lo scopo esatto di quello %1. Guarda l'ir per una versione leggermente modificata del tuo codice.

Codice modificato,

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    if(d) return 1;
    return 0;
}

IR,

define dso_local i32 @main() #0 !dbg !15 {
    %1 = alloca i32, align 4
    %2 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    store i32 2, i32* %2, align 4, !dbg !22
    %3 = load i32, i32* %2, align 4, !dbg !23
    %4 = icmp ne i32 %3, 0, !dbg !23
    br i1 %4, label %5, label %6, !dbg !25

 5:                                                ; preds = %0
   store i32 1, i32* %1, align 4, !dbg !26
   br label %7, !dbg !26

 6:                                                ; preds = %0
  store i32 0, i32* %1, align 4, !dbg !27
  br label %7, !dbg !27

 7:                                                ; preds = %6, %5
  %8 = load i32, i32* %1, align 4, !dbg !28
  ret i32 %8, !dbg !28
}

Ora vedi che si sta %1rendendo utile eh? Come hanno sottolineato gli altri, per le funzioni con una sola istruzione return, questa variabile verrà probabilmente rimossa da uno dei passaggi ottimizzati di llvm.


1

Perché è importante: qual è il problema reale?

Penso che la risposta più profonda che stai cercando potrebbe essere: l'architettura di LLVM si basa su frontend abbastanza semplici e molti passaggi. I frontend devono generare il codice corretto, ma non deve essere un buon codice. Possono fare la cosa più semplice che funziona.

In questo caso, Clang genera un paio di istruzioni che risultano non essere utilizzate per nulla. Questo non è generalmente un problema, perché alcune parti di LLVM elimineranno le istruzioni superflue. Clang confida che ciò accada. Clang non ha bisogno di evitare di emettere codice morto; la sua implementazione può focalizzarsi su correttezza, semplicità, testabilità, ecc.


1

Perché Clang ha finito con l'analisi della sintassi ma LLVM non ha nemmeno iniziato con l'ottimizzazione.

Il front-end Clang ha generato IR (rappresentazione intermedia) e non codice macchina. Tali variabili sono SSA (Single Static Assignments); non sono ancora stati vincolati ai registri e in realtà dopo l'ottimizzazione, non lo saranno mai perché sono ridondanti.

Quel codice è una rappresentazione in qualche modo letterale della fonte. È ciò che clang passa a LLVM per l'ottimizzazione. Fondamentalmente, LLVM inizia con quello e ottimizza da lì. Infatti, per la versione 10 e x86_64, llc -O2 alla fine genererà:

main: # @main
  xor eax, eax
  ret

Capisco il processo a questo livello. Volevo sapere perché questo IR è stato generato per cominciare.
macleginn,

Potresti pensare a un compilatore come a un singolo passaggio. C'è una conduttura di passaggi che iniziano con il front-end di Clang che genera IR. Non ha nemmeno generato questo IR testuale che invece qualcuno ha richiesto con clang -emit-llvm -S file.cpp Clang ha effettivamente generato una versione binaria serializzabile di bitcode dell'IR. LLVM è strutturato in più passaggi, ciascuno prendendo e ottimizzando IR. Il primo passaggio LLVM riceve IR da Clang. Ci vuole IR perché è possibile sostituire Clang con Fortran FE per supportare un'altra lingua con lo stesso ottimizzatore + generatore di codice.
Olsonist
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.