2011-08-22 16 views
7

Ho bisogno di alcuni consigli su "inoltro" argomenti a un chiamato (nel LLVM-IR).Inoltro di argomenti in LLVM

Supponiamo di avere una funzione F che viene richiamata all'inizio di tutte le altre funzioni nel modulo. Da F ho bisogno di accedere (leggere) gli argomenti passati al suo immediato chiamante.

In questo momento per fare questo I box tutti gli argomenti del chiamante all'interno di una struct e passare un puntatore i8* alla struttura per F, accanto ad un racconto identificativo chiamante che F viene chiamato da. F ha quindi un interruttore gigante che si dirama al codice di unboxing appropriato. Questo deve essere fatto perché le funzioni nel modulo hanno firme diverse (argomento diverso/conteggio del valore di ritorno e tipi, anche convenzioni di chiamata diverse), ma è ovviamente subottimale (sia dal punto di vista delle prestazioni che del formato del codice) perché io è necessario allocare la struttura nello stack, copiare gli argomenti al suo interno, passare un puntatore aggiuntivo a F e quindi eseguire l'unboxing.

Mi chiedevo se c'è un modo migliore per fare questo, cioè un modo per accedere da una funzione lo stack frame del suo immediato chiamante (conoscere, grazie alla identificativo, che chiama la funzione è stata chiamata da) o , più in generale, valori arbitrari definiti nel suo immediato chiamante. Eventuali suggerimenti?

nota: il punto di ciò che sto lavorando su sta avendo una funzione Fsingolo che fa tutto questo; splitting/inlining/specializing/templating F non è un'opzione.


per chiarire, supponiamo di avere le seguenti funzioni FuncA e FuncB (nota: Ciò che segue è solo pseudo-codice C, sempre ricordare che stiamo parlando di LLVM-IR!)

Type1 FuncA(Type2 ArgA1) { 
    F(); 
    // ... 
} 

Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) { 
    F(); 
    // ... 
} 

quello che mi serve è un modo efficace per la funzione F per effettuare le seguenti operazioni:

void F() { 
    switch (caller) { 
    case FuncA: 
     // do something with ArgA1 
     break; 
    case FuncB: 
     // do something with ArgB1, ArgB2, ArgB3 
     break; 
    } 
} 

come ho spiegato nella prima parte, in questo momento il mio F assomiglia a questo:

struct Args_FuncA { Type2 ArgA1 }; 
struct Args_FuncB { Type4 ArgB1, Type5 ArgB2, Type6 ArgB3 }; 

void F(int callerID, void *args) { 
    switch (callerID) { 
    case ID_FuncA: 
     Args_FuncA *ArgsFuncA = (Args_FuncA*)args; 
     Type2 ArgA1 = ArgsFuncA->ArgA1; 
     // do something with ArgA1 
     break; 
    case ID_FuncB: 
     Args_FuncB *ArgsFuncB = (Args_FuncB*)args; 
     Type4 ArgB1 = ArgsFuncB->ArgB1; 
     Type5 ArgB2 = ArgsFuncB->ArgB2; 
     Type6 ArgB3 = ArgsFuncB->ArgB3; 
     // do something with ArgB1, ArgB2, ArgB3 
     break; 
    } 
} 

e le due funzioni diventano:

Type1 FuncA(Type2 ArgA1) { 
    Args_FuncA args = { ArgA1 }; 
    F(ID_FuncA, (void*)&args); 
    // ... 
} 

Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) { 
    Args_FuncB args = { ArgB1, ArgB2, ArgB3 }; 
    F(ID_FuncB, (void*)&args); 
    // ... 
} 

risposta

1

IMHO che hai fatto bene. Sebbene esistano soluzioni nell'assemblaggio del codice macchina, temo che non ci possa essere alcuna soluzione nell'assemblaggio LLVM, poiché si tratta di un "livello superiore". Se si desidera eseguire una funzione all'inizio di alcune funzioni hanno pensato a controllare

  • fonti debugger (come gdb)
  • binario Strumentazione con Valgrind

io so che non è direttamente rispondi, ma spero possa essere utile in qualche modo;).

+1

dtrace ha fatto esattamente lo stesso di CAFxX. – osgx

1

Non sono sicuro se questo aiuta, ma ho avuto un problema simile e ho aggirato i limiti dell'analisi di TBAA di LLVM usando un vettore di llvm per memorizzare i valori intermedi. I passaggi di ottimizzazione LLVM sono stati in seguito in grado di ottimizzare il carico/gli archivi vettoriali nei registri scalari.

C'erano alcuni avvertimenti come ricordo. Fammi sapere se esplori questa rotta e posso recuperare un po 'di codice.