Ho scritto una macchina virtuale in C che ha prestazioni decenti per una VM non JIT, ma voglio imparare qualcosa di nuovo e migliorare le prestazioni. La mia attuale implementazione utilizza semplicemente un interruttore per tradurre da bytecode VM alle istruzioni, che viene compilato su una tabella di jump. Come ho detto, prestazioni decenti per quello che è, ma ho colpito una barriera che può essere superata solo con un compilatore JIT.Scrivere un compilatore JIT nell'assemblaggio
Ho già fatto una domanda simile non molto tempo fa sul codice auto-modificante, ma mi sono reso conto che non stavo facendo la domanda giusta.
Quindi il mio obiettivo è scrivere un compilatore JIT per questa macchina virtuale C, e voglio farlo in assembly x86. (Sto usando la NASM come mio assemblatore) Non sono abbastanza sicuro di come fare per farlo. Sono a mio agio con il montaggio e ho esaminato alcuni esempi di codice auto-modificanti, ma non sono ancora arrivato a capire come fare la generazione di codice.
Il mio blocco principale finora è la copia delle istruzioni per un pezzo di memoria eseguibile, con i miei argomenti. Sono consapevole di poter etichettare una determinata linea in NASM e copiare l'intera riga da quell'indirizzo con gli argomenti statici, ma non è molto dinamico e non funziona per un compilatore JIT. Devo essere in grado di interpretare l'istruzione da bytecode, copiarla in memoria eseguibile, interpretare il primo argomento, copiarlo in memoria, quindi interpretare il secondo argomento e copiarlo in memoria.
Sono stato informato di diverse librerie che renderebbero questo compito più semplice, come il fulmine GNU e persino LLVM. Tuttavia, mi piacerebbe scriverlo a mano prima, per capire come funziona, prima di usare risorse esterne.
Esistono risorse o esempi che questa comunità potrebbe fornire per aiutarmi a iniziare questa attività? Un semplice esempio che mostra due o tre istruzioni come "aggiungi" e "mov" utilizzate per generare codice eseguibile, con argomenti, dinamicamente, in memoria, farebbe miracoli.
Proprio perché un jitter genera codice macchina * non * significa che esso stesso deve essere scritto in assembly. Non ha senso farlo. –
Un passo intermedio da provare è la distribuzione con thread utilizzando l'estensione goto calcolata di GCC (usando 'void * optable [] = {&& op_add, && op_subtract, ...}' e ciascun operando è 'op_add: ... goto * optable [* ip ++] ; '). Ho visto grandi guadagni in interpreti commutati come descrivi tu. –