Inserire nella libreria condivisa esterna .so anche codice Assembly

Da Gambas-it.org - Wikipedia.

E' possibile inserire in una libreria esterna dinamica condivisa (.so), da noi realizzata, anche codice Assembly, al fine di ottenere prestazioni ancor più veloci.

In particolare sono possibili due modalità:

  • con la funzione asm();
  • con la funzione __asm__() .



Riepilogo schematico dei tipi di registri di memoria in base alla quantità di memoria resa da ciascuno disponibile

Di seguito mostriamo un breve e schematico ripilogo dei tipi di registri di memoria (registri del processore) utilizzabili nel linguaggio Assembly su un sistema a 64-bit in base alla quantità di memoria resa da ciascuno di essi disponibile:

================ rax (64 bit)
        ======== eax (32 bit)
            ====  ax (16 bit)
            ==    ah  (8 bit)
              ==  al  (8 bit)

o visto anche così:

|__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___|
|__________________________RAX__________________________|
|                           |____________EAX____________|
|                                         |_____AX______|
|                                         |__AH__|__AL__|



Uso della funzione asm()

Una prima modalità mostra come è possibile integrare il codice Assembly all'interno del linguaggio C in un sistema a 32-bit mediante la funzione asm():

static unsigned int car asm("raxregistro");
static unsigned int cbr asm("rbxregistro");
 cbr = 15;
 asm("mov rbxregistro,%ax");
 asm("mov %ax,raxregistro");
printf("%d",car);

alla fine, alla riga con printf, la variabile car avrà valore 15.


Problemi di compilazione nei sistemi a 64-bit durante la creazione di una Libreria .so

Si è riscontrato nei sistemi a 64-bit che durante la compilazione, qualora nel codice vi siano una o più variabili, viene restituito il seguente avviso di errore:

relocation R_X86_64_32S against `.bss' can not be used when making a shared object; recompile with -fPIC

Ciò avviene, ad esempio, nel tentativo di compilazione di un codice di questo tipo:

//libprova.c file:

static unsigned int car asm("raxregistro"); 	
static unsigned int cbr asm("rbxregistro"); 	

int prova(int numero) {
 cbr=numero;
 asm("mov rbxregistro,%eax");
 asm("ciclo:");
 asm("dec %eax");
 asm("jne ciclo");
 asm("mov %eax,raxregistro");
 return car;
}

Il problema è nel trasferimento del valore contenuto nelle variabili "raxregistro" e "rbxregistro" alle righe appunto in Assembly:

asm ("mov rbxregistro, eax%");
asm("mov %eax,raxregistro");

e sembra essere legato alle differenze nel codice pic tra i386 e amd64.
Per risolvere il problema è necessario utilizzare uno spostamento relativo di indirizzo; altrimenti si finisce con il codice dipendente di posizione che non funziona per le librerie condivise.
Cosicché, data ad esempio le variabili "raxregistro" e "rbxregistro" in queste righe:

static unsigned int car asm("raxregistro");
static unsigned int cbr asm("rbxregistro");

per richiamarle bisogna usare l'address-relative, così:

asm("mov %eax,raxregistro(%rip)");
asm("mov rbxregistro(%rip),%eax");

Pertanto, l'esempio di codice, sopra mostrato, dovrà - per i sistemi a 64-bit - essere scritto come segue:

static unsigned int car asm("raxregistro"); 	
static unsigned int cbr asm("rbxregistro"); 	

int prova(int numero) {
 cbr=numero;
 asm("mov rbxregistro(%rip),%eax");
 asm("ciclo:");
 asm("dec %eax");
 asm("jne ciclo");
 asm("mov %eax,raxregistro(%rip)");
 return car;
}


Esempio pratico di creazione ed uso di una libreria dinamica condivisa, contenente codice Assembly, con Gambas

Mostriamo di seguito un semplice esempio (in sistema a 64-bit), nel quale sarà creata una libreria dinamica condivisa esterna, contenente codice Assembly, e sarà quindi utilizzata con Gambas:

Private Extern Elabora(valore As Integer) As Integer In "/tmp/C_Asm"


Public Sub Main()

 Dim i As Integer
 
  CreaSo()
   
  i = Elabora(160)
   
  Print i

End


Private Procedure CreaSo()

 Dim s As String = "static unsigned int car asm(\"raxregistro\");\n" &
                   "static int cbr asm(\"rbxregistro\");\n" &
                   "static short cbr2 asm(\"rbxregistro2\");\n\n" &
                   "int Elabora(int cbr_C) {\n\n" &
                   "cbr=cbr_C;\n" &
                   "cbr2 = 100;\n\n" &
                   "asm(\"mov rbxregistro(%rip),%eax\");\n" &
                   "asm(\"inc %eax\");\n" &
                   "asm(\"add rbxregistro2(%rip), %eax\");\n" &
                   "asm(\"mov %eax,raxregistro(%rip)\");" &
                   "return car;\n\n}"
                   
  File.Save("/tmp/C_Asm.c", s)
 
  Shell "gcc -o /tmp/C_Asm.so /tmp/C_Asm.c -shared" Wait
  
End



Uso della funzione __asm__()

Quest'altra modalità utilizza le risorse Assembly ugualmente tramite il codice C, ma fa uso della funzione __asm__().

Ne mostriamo un semplice esempio:

Private Extern C_asm(valore As Integer) As Integer In "/tmp/C_asm"


Public Sub Main()
 
 Dim i As Integer
  
  Creaso()
 
  i = C_asm(99)
   
  Print "i = "; i

End


Private Procedure Creaso()

 Dim s As String
 
  s = "int C_asm(int b) {" &
      "\n\n   int a, c, d;" &
      "\n\n   __asm__(\"xor %%rax, %%rax;\"" &
      "\n           \"mov %%rbx, %%rax;\"" &
      "\n           \"inc %%rax;\"" &
      "\n" &
      "\n           : \"=a\" (a)" &
      "\n           : \"b\" (b), \"c\" (c), \"d\" (d)" &
      "\n   );"
      "\n\n}"

  File.Save("/tmp/C_asm.c", s)
 
  Shell "gcc -o /tmp/C_asm.so /tmp/C_asm.c -shared" Wait
 
End


Altri esempi particolari: effettuare un ciclo

Nel seguente esempio all'interno del codice Assembly verrà effettuato un ciclo. Ad ogni giro avverrà un operazione. Al termine del ciclo verrà restituito al codice C il valore finale dell'operazione, e quindi all'applicativo Gambas.

Private Extern C_Asm(bI As Integer, cI As Integer) As Integer In "/tmp/ciclo"


Public Sub Main()
 
 Dim i As Integer
 
  CreaSo()
  
  i = C_Asm(1000000000, 2)
  
  Print i

End


Private Procedure CreaSo()
  
 File.Save("/tmp/ciclo.c", "int C_Asm(int b, int c) {" &
           "\n\nint a, d;" &
           "\n\n__asm__(\"xor %%eax, %%eax;\"     /* Azzera il registro 'eax' */" &
           "\n\"ciclo: inc %%ecx;\"               /* Ha inizio il ciclo e viene incrementato il registro 'ecx' di una unità */" &
           "\n\"add %%ecx, %%eax;\"               /* Si somma l'attuale valore del registro 'ecx' al valore del registro 'eax' */" &
           "\n\"dec %%ebx;\"                      /* Si decrementa il registro 'ebx' di una unità (quando avrà valore zero, si esce dal ciclo) */" &
           "\n\"jne ciclo;\"                      /* Salta alla riga ove è presente la parola 'ciclo': si compie un nuovo giro del ciclo */" &
           "\n\n: \"=a\" (a)" &
           "\n: \"b\"  (b), \"c\" (c), \"d\" (d)" &
           "\n);\n\n}")
           
 Shell "gcc -o /tmp/ciclo.so /tmp/ciclo.c -shared" Wait
 
End


Altri esempi particolari: effettuare un'addizione

Nel seguente esempio all'interno del codice Assembly verrà effettuata una semplice addizione. Il risultato verrà restituito al codice C, e da esso all'applicativo Gambas.

Private Extern C_Asm(bI As Integer, cI As Integer) As Integer In "/tmp/addiz"


Public Sub Main()
 
 Dim i As Integer
 
  CreaSo()
  
  i = C_Asm(1000000000, 2)
  
  Print i

End


Private Procedure CreaSo()
  
 File.Save("/tmp/addiz.c", "int C_Asm(int b, int c) {" &
           "\n\nint a, d;" &
           "\n\n__asm__(\"xor %%eax, %%eax;\"     /* Azzera il registro 'eax' */" &
           "\n\"add %%ebx, %%ecx;\"               /* Si somma il valore del registro 'ebx' al valore del registro 'ecx' */" &
           "\n\"mov %%ecx, %%eax;\"               /* Si sposta il valore del registro 'ecx' nel registro 'eax', affinché venga restituito al codice C */" &
           "\n\n: \"=a\" (a)" &
           "\n: \"b\"  (b), \"c\" (c), \"d\" (d)" &
           "\n);\n\n}")
           
 Shell "gcc -o /tmp/addiz.so /tmp/addiz.c -shared" Wait
 
End


Altri esempi particolari: effettuare una sottrazione

Nel seguente esempio all'interno del codice Assembly verrà effettuata una semplice sottrazione. Il risultato verrà restituito al codice C, e da esso all'applicativo Gambas.

Private Extern C_Asm(bI As Integer, cI As Integer) As Integer In "/tmp/sottr"


Public Sub Main()
 
 Dim i As Integer
 
  CreaSo()
  
  i = C_Asm(1000000000, 2)
  
  Print i

End


Private Procedure CreaSo()
  
 File.Save("/tmp/sottr.c", "int C_Asm(int b, int c) {" &
           "\n\nint a, d;" &
           "\n\n__asm__(\"xor %%eax, %%eax;\"     /* Azzera il registro 'eax' */" &
           "\n\"sub %%ecx, %%ebx;\"               /* Si sottrae il valore del registro 'ecx' al valore del registro 'ebx' */" &
           "\n\"mov %%ebx, %%eax;\"               /* Si sposta il valore del registro 'ebx' nel registro 'eax', affinché venga restituito al codice C */" &
           "\n\n: \"=a\" (a)" &
           "\n: \"b\"  (b), \"c\" (c), \"d\" (d)" &
           "\n);\n\n}")
           
 Shell "gcc -o /tmp/sottr.so /tmp/sottr.c -shared" Wait
 
End


Altri esempi particolari: effettuare una moltiplicazione

Nel seguente esempio all'interno del codice Assembly verrà effettuata una semplice moltiplicazione. Il risultato verrà restituito al codice C, e da esso all'applicativo Gambas.

Private Extern C_Asm(bI As Integer, cI As Integer) As Integer In "/tmp/molti"


Public Sub Main()
 
 Dim i As Integer
 
  CreaSo()
  
  i = C_Asm(1000000000, 2)
  
  Print i

End


Private Procedure CreaSo()
  
 File.Save("/tmp/molti.c", "int C_Asm(int b, int c) {" &
           "\n\nint a, d;" &
           "\n\n__asm__(\"xor %%eax, %%eax;\"     /* Azzera il registro 'eax' */" &
           "\n\"mov %%ecx, %%eax;\"               /* Si sposta il valore del registro 'ecx' nel registro 'eax' */" &
           "\n\"mul %%ebx;\"                      /* Si moltiplica il valore del registro 'ebx' per il valore del registro 'eax' */" &
           "\n\"mov %%ebx, %%edx;\"               /* Si sposta il valore del registro 'ebx' nel registro 'edx', affinché venga restituito al codice C */" &
           "\n\n: \"=a\" (a)" &
           "\n: \"b\"  (b), \"c\" (c), \"d\" (d)" &
           "\n);\n\n}")
           
 Shell "gcc -o /tmp/molti.so /tmp/molti.c -shared" Wait
 
End


Altri esempi particolari: effettuare una divisione

Nel seguente esempio all'interno del codice Assembly verrà effettuata una semplice divisione. Il risultato verrà restituito al codice C, e da esso all'applicativo Gambas.

Private Extern C_Asm(bI As Integer, cI As Integer) As Integer In "/tmp/divis"


Public Sub Main()
 
 Dim i As Integer
 
  CreaSo()
  
  i = C_Asm(2, 500)
  
  Print i

End


Private Procedure CreaSo()
  
 File.Save("/tmp/divis.c", "int C_Asm(int b, int c) {" &
           "\n\nint a, d;" &
           "\n\n__asm__(\"xor %%ax, %%ax;\"     /* Azzera il registro 'ax' */" &
           "\n\"mov %%cx, %%ax;\"               /* Si sposta il valore del registro 'cx' nel registro 'ax' */" &
           "\n\"div %%bl;\"                     /* Si divide il valore del registro 'cx' per il valore del registro 'bl' */" &
           "\n\"mov %%bx, %%dx;\"               /* Si sposta il valore del registro 'bx' nel registro 'dx', affinché venga restituito al codice C */" &
           "\n\n: \"=a\" (a)" &
           "\n: \"b\"  (b), \"c\" (c), \"d\" (d)" &
           "\n);\n\n}")
           
 Shell "gcc -o /tmp/divis.so /tmp/divis.c -shared" Wait
 
End


Altri esempi particolari: passare una stringa di caratteri da far stampare in console/Terminale

Nel seguente esempio all'interno del codice Assembly verrà passata una stringa di caratteri da far stampare in console/Terminale.

Private Extern C_Asm(bI As Integer, cI As String) In "/tmp/stringa"


Public Sub Main()
 
 Dim s As String
 Dim i As Integer
 
  CreaSo()

  s = "testo qualsiasi\n"
  
  i = Len(s)

' E' necessario passare anche la lunghezza della stringa da far stampare in console/Terminale:
  C_Asm(i, s)
   
End


Private Procedure CreaSo()
  
 File.Save("/tmp/stringa.c", "void C_Asm(int d, char * s) {      // int d è la lunghezza della Stringa passata. E' imprescindibile !" &
           "\n  int a, b;" &
           "\n\n  __asm__(" &
           "mov $4, %%eax;\n" &
           "mov $1, %%ebx;\n" &
           "int $0x80;\n" &
           "mov $1, %%eax;\n" &
           "mov $0, %%ebx;\n" &
           "int $0x80;\n\n" &
           ": \"=a\" (a)\n" &
           ": \"b\" (b), \"c\" (s), \"d\" (d)\n" &
           ");\n\n}")
           
 Shell "gcc -o /tmp/stringa.so /tmp/stringa.c -shared" Wait
 
End