Interprete / VM di bytecode più piccolo


17

Classifica - JIT compilato (inferiore è meglio)

  1. es1024 - 81.2 punti (incluso un compilatore funzionante!)
  2. Kieth Randall - 116 punti
  3. Ell - 121 punti

Classifica - Interpretato (Inferiore è meglio)

  1. Martin Büttner - 706654 punti (da qualche parte circa 2 ore).
  2. criptych - 30379 punti (97 secondi)

La tua missione, se scegli di accettarla, è quella di scrivere il più piccolo interprete / VM del codice byte possibile. Il VM / interprete utilizza una piccola architettura CISC (le operazioni possono variare di dimensioni), con la lingua specificata di seguito. Al termine, è necessario stampare il valore dei 3 registri della CPU per provare che viene stampato l'output corretto (3.126.900.366).

Compiler

Se desideri effettuare i tuoi test, di seguito viene pubblicato un compilatore. Sentiti libero di pubblicare i tuoi test con la tua risposta.

Specifiche "VM"

La macchina virtuale ha 3 registri integrali senza segno a 32 bit: R0, R1, R2. Sono rappresentati in esadecimale come 0x00, 0x01 e 0x02.

Devono essere supportate le seguenti operazioni:

Il formato è [nome] [... operandi ...], [codice op esadecimale] [... operandi ripetuti ...]

  • LOAD [registro] [valore 4 byte], 0x00 [registro] [valore 4 byte]
  • PUSH [registro], 0x02 [registro]
  • POP [registro], 0x03 [registro]
  • ADD [registro, 1 byte] [registro, 1 byte], 0x04 [registro] [registro]
  • SUB [registro, 1 byte] [registro, 1 byte], 0x05 [registro] [registro]
  • MUL [registro, 1 byte] [registro, 1 byte], 0x06 [registro] [registro]
  • DIV [registro, 1 byte] [registro, 1 byte], 0x07 [registro] [registro]
  • JMP [riga codice, 4 byte], 0x08 [numero riga codice 4 byte]
  • CMP [registro, 1 byte] [registro, 1 byte], 0x09 [registro] [registro]
  • BRANCHLT [riga codice, 4 byte], 0x0a [numero riga codice 4 byte]

Alcune note:

  • Le suddette operazioni matematiche sommano i valori di 2 registri, posizionando l'output nel primo registro.
  • CMP, l'operatore di confronto, dovrebbe confrontare i valori di 2 registri e memorizzare l'output in alcuni flag interni (questo può essere specifico dell'implementazione) per un uso futuro nelle istruzioni di diramazione.
  • Se BRANCH viene chiamato prima di CMP, a meno che non venga chiamato BRANCHEQ, la "VM" non dovrebbe diramarsi.
  • PUSH / POP non sorprende che spinga o schiocchi numeri dalla pila.
  • Gli operatori Jump e Branch passano a un'operazione specifica (riga di codice), non a un indirizzo binario.
  • Le operazioni di filiale non fanno il confronto. Piuttosto, prendono l'esecuzione dell'ultimo confronto.
  • Gli operatori Branch e Jump utilizzano un sistema di indicizzazione del numero di riga in base zero. (Ad esempio, JMP 0 passa alla prima riga)
  • Tutte le operazioni devono essere eseguite su numeri senza segno che traboccano a zero e non generano un'eccezione su un overflow intero.
  • La divisione per zero non è consentita e, in quanto tale, il comportamento del programma non è definito. Puoi (per esempio) ...
    • Arresto anomalo del programma.
    • Termina l'esecuzione della VM e restituisce lo stato corrente.
    • Mostra un messaggio "ERR: divisione per 0".
  • La conclusione del programma è definita come quando il puntatore dell'istruzione raggiunge la fine del programma (si può presumere un programma non vuoto).

Output L'output deve essere esattamente questo (nuove righe incluse)

R0 3126900366
R1 0
R2 10000    

Punti I punti vengono calcolati in base alla seguente formula:Number Of Characters * (Seconds Needed To Run / 2)

Per evitare differenze hardware che causano tempi diversi, ogni test verrà eseguito sul mio computer (i5-4210u, 8 GB di RAM) su server Ubuntu o Windows 8, quindi cerca di non usare un runtime insano-esotico che si compila solo su un Dual G5 Mac Pro con esattamente 762,66 MB di RAM libera.

Se stai utilizzando un runtime / una lingua specializzati, pubblica un link ad esso.

Programma di test

L'idea è nata da qui , quindi useremo una versione leggermente modificata del loro programma.

L'output corretto per il programma è: 3.126.900.366

In C:

int s, i, j;
for (s = 0, i = 0; i < 10000; i++) {
    for (j = 0; j < 10000; j++)
        s += (i * j) / 3;
}

Nel codice: [R0 è rappresentativo di s, R1 di j, R2 di i]

LOAD R0 0
LOAD R2 0 <--outer loop value
LOAD R1 0 <--inner loop value
     --Begin inner loop--
PUSH R1 <--push inner loop value to the stack
MUL R1 R2 <--(i*j)
PUSH R2
LOAD R2 3
DIV R1 R2 <-- / 3
POP R2
ADD R0 R1 <-- s+=
POP R1
PUSH R2 
LOAD R2 1
ADD R1 R2 <--j++
POP R2
PUSH R2
LOAD R2 10000
CMP R1 R2 <-- j < 10000
POP R2
BRANCHLT 3 <--Go back to beginning inner loop
--Drop To outer loop--
LOAD R1 1
ADD R2 R1 <--i++
LOAD R1 10000
CMP R2 R1 <-- i < 10000
LOAD R1 0 <--Reset inner loop
BRANCHLT 2

In binario / esadecimale:

0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x02 0x00 0x00 0x00 0x00
0x00 0x01 0x00 0x00 0x00 0x00
0x02 0x01
0x06 0x01 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x03
0x07 0x01 0x02
0x03 0x02
0x04 0x00 0x01
0x03 0x01
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x01
0x04 0x01 0x02
0x03 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x27 0x10
0x09 0x01 0x02
0x03 0x02
0x0a 0x00 0x00 0x00 0x03
0x00 0x01 0x00 0x00 0x00 0x01
0x04 0x02 0x01
0x00 0x01 0x00 0x00 0x27 0x10
0x09 0x02 0x01
0x00 0x01 0x00 0x00 0x00 0x00
0x0a 0x00 0x00 0x00 0x02

Punti bonus (gli effetti vengono applicati in modo moltiplicativo) Ad esempio se ti qualifichi per tutti e tre, sarebbe ((caratteri * 0,50) * 0,75) * 0,90

  • Riduzione del 50% se l'interprete è in realtà un compilatore JIT
  • Riduzione del 25% se applica qualsiasi tipo di srotolamento / ottimizzazione significativa del loop.
  • Riduzione del 10% se si estende la VM con
    • BRANCHEQ [riga di codice, 4 byte] (Branch se uguale - opcode 0x0b)
    • BRANCHGT [riga di codice, 4 byte] (Branch se maggiore di - opcode 0x0c)
    • BRANCHNE [riga di codice, 4 byte] (Branch se non uguale - opcode 0x0d)
    • RLOAD [register 1] [register 2] (sposta il valore del registro 2 sul registro 1 - opcode 0x01).

Non consentito

  • È vietato precompilare il caso di test nel programma. Devi accettare il bytecode da STDIN o da un file (non importa quale).
  • Restituisce l'output senza eseguire il programma.
  • In qualsiasi altro modo ti venga in mente di imbrogliare il requisito della VM.

Perché non includere qualche altro programma di test per scoraggiare le cose che hai detto non sono consentite? Se si tratta di una macchina virtuale, dovrebbe essere in grado di eseguire qualsiasi cosa scritta nelle specifiche del bytecode, giusto?
Kasran,

Proverò a farlo stasera. Sto scrivendo il compilatore in questo momento.
Colorfully Monochrome,


1
Fa CMPassegno di minore o l'uguaglianza? E cosa succede al suo risultato?
es1024,

1
MULe DIVsono anche sottostimati. Dovrebbero essere firmati o non firmati? Cosa succede all'overflow di moltiplicazione?
febbraio

Risposte:


8

C, 752 (589 + 163 per i flag di definizione) * 0,5 (JIT) * 0,9 (estensioni) * (ottimizzazione 0,75) * (0,64 secondi / 2) = 81,216

C[S],J[S],i,j,k,y,c,X,Y,Z;char*R,a,b[9];main(x){R=mmap(0,S,6,34,-1,0);N=85;while(scanf("%c%s%*[ R]%d%*[ R]%d",&a,b,&x,&y)&&~getchar())a-=65,a-1?a-15?a-9?a?a-2?a-3?a-11?a-12?a-17?(N=41,v):(N=137,v):(N=137,u,N=247,g(H,4),N=139,u):(y?N=189+x,s(y):(N=51,g(G,G))):(N=137,u,N=247,g(H,6),N=139,u):(N=57,v,s(0xFC8A9F),--j):(N=1,v):(N=233,J[k++]=i,s(x)):b[1]-80?N=85+x:(N=93+x):(c=b[5],s(0x0F9EE78A),N=(c-69?c-71?c-76?1:8:11:0)+132,J[k++]=i,s(x)),C[++i]=j;U(E8,X)U(F0,Y)U(F8,Z)s(50013);i=j;while(k--)j=C[J[k]]+1,R[j-1]-233&&(j+=4),s(C[*(int*)(R+j)]-j-4);((int(*)())R)();printf("%u %u %u\n",X,Y,Z);}

Prende il codice (LOAD R0 , ecc.), Nessun carattere finale, spazio singolo, nessuna riga vuota nel mezzo, nessun commento, ecc. Nuova riga finale richiesta.

Questo viene quindi convertito in bytecode 80386 ed eseguito.

Il caricamento 0in un registro viene sostituito xoringing il registro con se stesso anziché moving 0nel registro, che è più corto di tre byte nel codice byte generato e può essere molto più veloce.

Compilare con:

gcc -m32 -D"g(a,b)=(N=192|b<<3|a)"-D"s(b)=(*(int*)(R+j)=b,j+=4)"-DN=R[j++]-D"G=((x+1)|4)"
-D"H=((y+1)|4)"-DS=9999-D"u=g(0,G)"-D"v=g(G,H)"-D"U(b,c)=s(0xA3##b##89),--j,s(&c);"
bytecode.c -o bytecode

Sistema operativo compatibile POSIX richiesto.

L'input viene letto da STDIN (utilizzare ./bytecode < fileper reindirizzare da un file).

Bytecode risultante per il programma di test:

; start
 0:   55                      push   %ebp
; LOAD R0 0
 1:   33 ed                   xor    %ebp,%ebp
; LOAD R2 0
 3:   33 ff                   xor    %edi,%edi
; LOAD R1 0
 5:   33 f6                   xor    %esi,%esi
; PUSH $1
 7:   56                      push   %esi
; MUL R1 R2
 8:   89 f0                   mov    %esi,%eax
 a:   f7 e7                   mul    %edi
 c:   8b f0                   mov    %eax,%esi
; PUSH R2
 e:   57                      push   %edi
; LOAD R2 3
 f:   bf 03 00 00 00          mov    $0x3,%edi
; DIV R1 R2
14:   89 f0                   mov    %esi,%eax
16:   f7 f7                   div    %edi
18:   8b f0                   mov    %eax,%esi
; POP R2
1a:   5f                      pop    %edi
; ADD R0 R1
1b:   01 f5                   add    %esi,%ebp
; POP R1
1d:   5e                      pop    %esi
; PUSH R2
1e:   57                      push   %edi
; LOAD R2 1
1f:   bf 01 00 00 00          mov    $0x1,%edi
; ADD R1 R2
24:   01 fe                   add    %edi,%esi
; POP R2
26:   5f                      pop    %edi
; PUSH R2
27:   57                      push   %edi
; LOAD R2 10000
28:   bf 10 27 00 00          mov    $0x2710,%ed
; CMP R1 R2
2d:   39 fe                   cmp    %edi,%esi
2f:   9f                      lahf
30:   8a fc                   mov    %ah,%bh
; POP R2
32:   5f                      pop    %edi
; BRANCHLT 3
33:   8a e7                   mov    %bh,%ah
35:   9e                      sahf
36:   0f 8c cb ff ff ff       jl     0x7
; LOAD R1 1
3c:   be 01 00 00 00          mov    $0x1,%esi
; ADD R2 R1
41:   01 f7                   add    %esi,%edi
; LOAD R1 10000
43:   be 10 27 00 00          mov    $0x2710,%es
; CMP R2 R1
48:   39 f7                   cmp    %esi,%edi
4a:   9f                      lahf
4b:   8a fc                   mov    %ah,%bh
; LOAD R1 0
4d:   33 f6                   xor    %esi,%esi
; BRANCHLT 2
4f:   8a e7                   mov    %bh,%ah
51:   9e                      sahf
52:   0f 8c ad ff ff ff       jl     0x5
; copy R0 to X
58:   89 e8                   mov    %ebp,%eax
5a:   a3 28 5b 42 00          mov    %eax,0x425b
; copy R1 to Y
5f:   89 f0                   mov    %esi,%eax
61:   a3 38 55 44 00          mov    %eax,0x4455
; copy R2 to Z
66:   89 f8                   mov    %edi,%eax
68:   a3 40 55 44 00          mov    %eax,0x4455
; exit
6d:   5d                      pop    %ebp
6e:   c3                      ret

Ungolfed:

C[9999],J[9999],i,j,k,y,c,X,Y,Z;
char *R,a,b[9];
main(x){
    // 6 is PROC_WRITE|PROC_EXEC
    // 34 is MAP_ANON|MAP_PRIVATE
    R=mmap(0,'~~',6,34,-1,0);

    N=0x55;
    while(scanf("%c%s%*[ R]%d%*[ R]%d",&a,b,&x,&y)&&~getchar())
        a-=65,
        a-1? // B[RANCH**]
            a-15? // P[USH/OP]
                a-9? // J[MP]
                    a? // A[DD]
                        a-2? // C[MP]
                            a-3? // D[IV]
                                a-11? // L[OAD]
                                    a-12? // M[UL]
                                        a-17? // R[LOAD]
                                            // SUB
                                            (N=0x29,g(G,H))
                                        :(N=0x89,g(G,H))
                                    :(N=0x89,g(0,G),N=0xF7,g(H,4),N=0x8B,g(0,G))
                                :(y?N=0xBD+x,s(y):(N=0x33,g(G,G)))
                            :(N=0x89,g(0,G),N=0xF7,g(H,6),N=0x8B,g(0,G))
                        :(N=0x39,g(G,H),s(0xfc8a9f),--j)
                    :(N=0x1,g(G,H))
                :(N=0xE9,J[k++]=i,s(x))
            :b[1]-80? 
                N=0x55+x // PUSH
            :(N=0x5D+x) // POP
        :(c=b[5],s(0x0f9ee78a),N=(
        c-69? // EQ
            c-71? // GT
                c-76? // LT
                    1 // NE
                :8
            :11
        :0
        )+0x84,J[k++]=i,s(x)),
        C[++i]=j
        ;
    // transfer registers to X,Y,Z
    s(0xA3E889),--j,s(&X);
    s(0xA3F089),--j,s(&Y);
    s(0xA3F889),--j,s(&Z);

    // pop and ret
    s(0xC35D);

    i=j;
    // fix distances for jmp/branch**
    while(k--)
        j=C[J[k]]+1,R[j-1]-0xE9&&(j+=4),
        s(C[*(int*)(R+j)]-j-4);

    // call
    ((int(*)())R)();

    // output
    printf("%u %u %u\n",X,Y,Z);
}

Wow. Vorrei aggiungere un bonus per l'inclusione del compilatore nella VM.
Colorfully monocromatico,

Media di 0,57 secondi per corsa su 15 corse.
Colorfully monocromatico,

Non sono d'accordo sul fatto che il xoring sia un'ottimizzazione. Sebbene la dimensione del codice sia intelligente, il xoring non modifica le caratteristiche di prestazione della VM (correggimi se sbaglio). Quello che intendevo per ottimizzazione era cambiare o rimuovere le istruzioni dal codice di input (ad es. Rimuovere POP ... PUSH ridondante) o realizzare 2 istruzioni di fila per caricare il registro, in modo da poter essere rimosso, ecc.
Colorfully Monochrome

EDIT: In realtà, si tratta di un'ottimizzazione: è sceso a 0,64 secondi per corsa su 15 corse. Immagino che impedisca il crash della cache o qualcosa accorciando il codice (o rimuovendo gli accessi ridondanti alla memoria)?
Colorfully monocromatico,

@ColorfullyMonochrome Alcune architetture, quando presentate con il xoring di un registro su se stesso, non eseguiranno effettivamente l'istruzione, ma semplicemente azzereranno il registro stesso.
es1024,

7

C, Punteggio = 854 byte × (~ 0,8 sec / 2) × 0,5 [JIT] × 0,9 [Estensioni] = ~ 154 byte sec

#define G getchar()
#define L for(i=0;i<3;++i)
#define N*(int*)
#define M(x)"P\x8a\xe7\x9e\xf"#x"    KL"
*T[1<<20],**t=T,*F[1<<20],**f=F,R[3],r[]={1,6,7};char*I[]={"L\xb8    GGJH","I\x8b\xc0HHGH","H\x50GG","H\x58GG","I\3\xc0HHGH","I\53\xc0HHGH","M\x8b\xc0\xf7\xe0\x8b\xc0IHLGJ","O\63\xd2\x8b\xc0\xf7\xf0\x8b\xc0IJNGL","L\xe9    KH","L\73\xc0\x9f\x8a\xfcHHGH",M(\x82),M(\x84),M(\x87),M(\x85)},C[1<<24],*c=C;main(i,o,l,g){N c=0xb7ec8b60;c[4]=70;c+=5;while((o=G)>=0){char*s=I[o];l=*s-'G';memcpy(c,s+1,l);for(s+=l+1;o=*s++;){o-='G';if(o<3){g=r[G];c[*s++-'G']|=g<<3*(o&1);if(o>1)c[*s++-'G']|=g<<3;}else{if(o>3)*f++=c+*s-'G';for(i=4;i;--i)c[*s-'G'+i-1]=G;++s;}}*t++=c;c+=l;}*t=c;while(f>F)--f,**f=(int)T[**f]-(int)*f-4;L N&c[7*i]=0x5893e|r[i]<<19,N&c[3+7*i]=R+i;N&c[21]=0xc361e58b;mprotect((int)C>>12<<12,1<<24,7);((void(*)())C)();L printf("R%d %u\n",i,R[i]);}

Compilare con gcc vm.c -ovm -m32 -wun sistema operativo compatibile POSIX x86.
Esegui con ./vm < program, dove si programtrova un file di programma binario.


Andare per la velocità. Il programma esegue una traduzione piuttosto semplice del programma di input in codice macchina x86 e lascia che la CPU faccia il resto.

Ad esempio, ecco la traduzione del programma di test. ecx, esie edicorrispondono a R0, R1e R2, rispettivamente; bhcontiene i flag di stato; eaxe edxsono registri scratch; lo stack di chiamate corrisponde allo stack della VM:

# Prologue
     0:   60                      pusha
     1:   8b ec                   mov    ebp,esp
     3:   b7 46                   mov    bh,0x46
# LOAD R0 0
     5:   b9 00 00 00 00          mov    ecx,0x0
# LOAD R2 0 <--outer loop value
     a:   bf 00 00 00 00          mov    edi,0x0
# LOAD R1 0 <--inner loop value
     f:   be 00 00 00 00          mov    esi,0x0
#      --Begin inner loop--
# PUSH R1 <--push inner loop value to the stack
    14:   56                      push   esi
# MUL R1 R2 <--(i*j)
    15:   8b c6                   mov    eax,esi
    15:   f7 e7                   mul    edi
    19:   8b f0                   mov    esi,eax
# PUSH R2
    1b:   57                      push   edi
# LOAD R2 3
    1c:   bf 03 00 00 00          mov    edi,0x3
# DIV R1 R2 <-- / 3
    21:   33 d2                   xor    edx,edx
    23:   8b c6                   mov    eax,esi
    25:   f7 f7                   div    edi
    27:   8b f0                   mov    esi,eax
# POP R2
    29:   5f                      pop    edi
# ADD R0 R1 <-- s+=
    2a:   03 ce                   add    ecx,esi
# POP R1
    2c:   5e                      pop    esi
# PUSH R2
    2d:   57                      push   edi
# LOAD R2 1
    2e:   bf 01 00 00 00          mov    edi,0x1
# ADD R1 R2 <--j++
    33:   03 f7                   add    esi,edi
# POP R2
    35:   5f                      pop    edi
# PUSH R2
    36:   57                      push   edi
# LOAD R2 10000
    37:   bf 10 27 00 00          mov    edi,0x2710
# CMP R1 R2 <-- j < 10000
    3c:   3b f7                   cmp    esi,edi
    3e:   9f                      lahf
    3f:   8a fc                   mov    bh,ah
# POP R2
    41:   5f                      pop    edi
# BRANCHLT 4 <--Go back to beginning inner loop
    42:   8a e7                   mov    ah,bh
    44:   9e                      sahf
    45:   0f 82 c9 ff ff ff       jb     0x14
# --Drop To outer loop--
# LOAD R1 1
    4b:   be 01 00 00 00          mov    esi,0x1
# ADD R2 R1 <--i++
    50:   03 fe                   add    edi,esi
# LOAD R1 10000
    52:   be 10 27 00 00          mov    esi,0x2710
# CMP R2 R1 <-- i < 10000
    57:   3b fe                   cmp    edi,esi
    59:   9f                      lahf
    5a:   8a fc                   mov    bh,ah
# LOAD R1 0 <--Reset inner loop
    5c:   be 00 00 00 00          mov    esi,0x0
# BRANCHLT 3
    61:   8a e7                   mov    ah,bh
    63:   9e                      sahf
    64:   0f 82 a5 ff ff ff       jb     0xf
# Epilogue
    6a:   3e 89 0d 60 ac 04 09    mov    DWORD PTR ds:0x904ac60,ecx
    71:   3e 89 35 64 ac 04 09    mov    DWORD PTR ds:0x904ac64,esi
    78:   3e 89 3d 68 ac 04 09    mov    DWORD PTR ds:0x904ac68,edi
    7f:   8b e5                   mov    esp,ebp
    81:   61                      popa
    82:   c3                      ret

Ungolfed


Wow ... il mio JIT era ~ 900 righe di codice (scritto in c ++) ...
Colorfully Monochrome

Media di 0,63 secondi per corsa per 15 corse.
Colorfully monocromatico,

2

CJam, 222 187 185 byte * (troppo lento / 2)

Volevo solo vedere quanto posso ottenere una macchina virtuale bytecode scrivendola in CJam. Meno di 200 byte sembra abbastanza decente. È dannatamente lento, perché CJam stesso è interpretato. Ci vogliono anni per eseguire il programma di test.

304402480 6b:P;q:iD-);{(_P=@/(\L*@@+\}h;]:P;TTT]:R;{_Rf=~}:Q;{4G#%R@0=@t:R;}:O;{TP=("R\(\GG*bt:R;  ~R= R\~@t:R; Q+O Q4G#+-O Q*O Q/O ~(:T; Rf=~-:U; GG*bU0<{(:T}*;"S/=~T):TP,<}g3,{'R\_S\R=N}/

Per eseguirlo, scarica l'interprete Java su questo link sourceforge , salva il codice vm.cjamed eseguilo con

java -jar cjam-0.6.2.jar vm.cjam

Il programma prevede il bytecode su STDIN. Non ho ancora trovato un modo per convogliare i dati binari in un programma, senza che PowerShell aggiunga un'interruzione di riga finale e la conversione 0x0ain 0x0d 0x0a, il che è davvero fastidioso. Il codice include 4 byte per correggere that ( D-);), che non ho incluso nel conteggio totale, perché non è qualcosa che il programma dovrebbe fare se in realtà ha ricevuto il bytecode stesso su STDIN, invece di una versione stranamente codificata di esso . Se qualcuno conosce una soluzione per questo, per favore fatemelo sapere.

Leggermente non golfato:

304402480 6b:P; "Create lookup table for instruction sizes. Store in P.";
q:i             "Read program and convert bytes to integers.";
D-);            "Remove spurious carriage returns. This shouldn't be necessary.";
{(_P=@/(\L*@@+\}h;]:P; "Split into instructions. Store in P.";
"We'll use T for the instruction pointer as it's initialised to 0.";
"Likewise, we'll use U for the CMP flag.";
TTT]:R; "Store [0 0 0] in R for the registers.";
{_Rf=~}:Q; "Register lookup block.";
{4G#%R@0=@t:R;}:O; "Save in register block.";
{TP=("R\(\GG*bt:R;

~R=
R\~@t:R;
Q+O
Q4G#+-O
Q*O
Q/O
~(:T;
Rf=~-:U;
GG*bU0<{(:T}*;"N/=~T):TP,<}g "Run program.";
3,{'R\_S\R=N}/

Aggiungerò una spiegazione adeguata domani.

In breve, sto memorizzando tutti i registri, il puntatore alle istruzioni e il flag di confronto in variabili, in modo da poter mantenere lo stack di CJam libero da usare come stack della VM.



1
15,279 secondi in media per 20 iterazioni. - 15 test. Ciò significa 2,12208333 ore per test.
Colorfully Monochrome,

1

python / c ++, punteggio = 56.66

1435 caratteri * .234 / 2 secondi * .5 [JIT] * .75 [Ottimizzazione] * .90 [Istruzioni extra]

Compila il programma di input in c ++, esegue gcc su di esso, quindi esegue il risultato. La maggior parte del tempo è trascorso in gcc.

L'unica ottimizzazione che faccio è ridurre le operazioni dello stack a variabili esplicite se è consentito semanticamente. Aiuta molto, circa 10 volte meglio il tempo di esecuzione del codice compilato (circa 0,056 secondi per eseguire effettivamente il binario risultante). Non sono sicuro di cosa stia facendo gcc che ti dia quel miglioramento, ma va bene.

import sys,os
x=map(ord,sys.stdin.read())
w=lambda x:(x[0]<<24)+(x[1]<<16)+(x[2]<<8)+x[3]
I=[]
while x:
 if x[0]==0:f='r%d=%d'%(x[1],w(x[2:]));n=6
 if x[0]==1:f='r%d=r%d'%(x[1],x[2]);n=3
 if x[0]==2:f='P%d'%x[1];n=2
 if x[0]==3:f='O%d'%x[1];n=2
 if x[0]==4:f='r%d=r%d+r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==5:f='r%d=r%d-r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==6:f='r%d=r%d*r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==7:f='r%d=r%d/r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==8:f='goto L%d'%w(x[1:]);n=5
 if x[0]==9:f='a=r%d;b=r%d'%(x[1],x[2]);n=3
 if x[0]==10:f='if(a<b)goto L%d'%w(x[1:]);n=5
 if x[0]==11:f='if(a==b)goto L%d'%w(x[1:]);n=5
 if x[0]==12:f='if(a>b)goto L%d'%w(x[1:]);n=5
 if x[0]==13:f='if(a!=b)goto L%d'%w(x[1:]);n=5
 I+=[f];x=x[n:]
D=[]
d=0
for f in I:D+=[d];d+='P'==f[0];d-='O'==f[0]
J=[]
if all(d==D[int(f[f.find('L')+1:])]for f,d in zip(I,D)if f[0]in'gi'):
 H='uint32_t '+','.join('s%d'%i for i in range(max(D)))+';'
 for f,d in zip(I,D):
  if f[0]=='P':f='s%d=r'%d+f[1:]
  if f[0]=='O':f='r'+f[1:]+'=s%d'%(d-1)
  J+=[f]
else:
 H='std::vector<uint32_t>s;'
 for f,d in zip(I,D):
  if f[0]=='P':f='s.push_back(r'+f[1:]+')'
  if f[0]=='O':f='r'+f[1:]+'=s.back();s.pop_back()'
  J+=[f]
P='#include<vector>\n#include<cstdint>\nuint32_t r0,r1,r2,a,b;'+H+'int main(){'
for i,f in enumerate(J):P+='L%d:'%i+f+';'
P+=r'printf("R0 %u\nR1 %u\nR2 %u\n",r0,r1,r2);}'
c=open("t.cc", "w")
c.write(P)
c.close()
os.system("g++ -O1 t.cc")
os.system("./a.out")

Potrebbe sicuramente essere golfizzato ancora un po '.


Media di .477 secondi per corsa su 15 corse.
Colorfully monocromatico,

1

Lua 5.2 (o LuaJIT), 740 byte

Primo tentativo, solo minimamente giocato a golf. Questa versione funziona (almeno sul programma di test) e implementa i codici operativi aggiuntivi, ma non soddisfa i requisiti matematici non firmati e non è particolarmente veloce. Come bonus, tuttavia, è una VM in esecuzione in una VM, ed è scritta in modo tale che possa essere interpretata (eseguita con PUC-Lua) o sorta di JIT (eseguita con LuaJIT; ancora interpretata, ma l'interprete è ora JITted).

EDIT: Golfed meglio, ancora grande.

EDIT: risolto un errore grave e ora limita l'aritmetica unsigned longall'intervallo. In qualche modo è riuscito a evitare che le dimensioni sfuggissero di mano, ma continua a dare la risposta sbagliata.

EDIT: risulta, il risultato è stato corretto ma l'output no. Passato alla stampa con %uinvece di %de tutto va bene. Sono stati inoltre modificati registri basati su tabella per le variabili al fine di migliorare un po ' le dimensioni e la velocità.

EDIT: Usando la gotodichiarazione di Lua 5.2 (disponibile anche in LuaJIT) ho sostituito l'interprete con "JIT-to-Lua", generando il codice che viene eseguito direttamente dalla stessa Lua VM. Non sono sicuro se questo conta davvero come JIT, ma migliora la velocità.

U,S,P,F=table.unpack,table.insert,table.remove,math.floor X,r0,r1,r2,p,m,s=2^32,0,0,0,1,0,{}C={{'r%u=%u',1,4},{'r%u=r%u',1,1},{'S(s,r%u)',1},{'r%u=P(s)',1},{'r%u=(r%u+r%u)%%X',1,0,1},{'r%u=(r%u-r%u)%%X',1,0,1},{'r%u=(r%u*r%u)%%X',1,0,1},{'r%u=F(r%u/r%u)%%X',1,0,1},{'goto L%u',4},{'m=r%u-r%u',1,1},{'if m<0 then goto L%u end',4},{'if m==0 then goto L%u end',4},{'if m>0 then goto L%u end',4},{'if m~=0 then goto L%u end',4}}t={io.open(arg[1],'rb'):read('*a'):byte(1,-1)}i,n,r=1,0,{}while i<=#t do c,i,x,a=C[t[i]+1],i+1,0,{}for j=2,#c do y=c[j]if y>0 then x=0 for k=1,y do i,x=i+1,x*256+t[i]end end S(a,x)end S(r,('::L%d::'):format(n))n=n+1 S(r,c[1]:format(U(a)))end load(table.concat(r,' '))()print(('R0 %u\nR1 %u\nR2 %u'):format(r0,r1,r2))

Ecco la versione originale e leggibile.

U,S,P,F=table.unpack,table.insert,table.remove,math.floor

X,r0,r1,r2,p,m,s=2^32,0,0,0,1,0,{}

C={
    {'r%u=%u',1,4},
    {'r%u=r%u',1,1},
    {'S(s,r%u)',1},
    {'r%u=P(s)',1},
    {'r%u=(r%u+r%u)%%X',1,0,1},
    {'r%u=(r%u-r%u)%%X',1,0,1},
    {'r%u=(r%u*r%u)%%X',1,0,1},
    {'r%u=F(r%u/r%u)%%X',1,0,1},
    {'goto L%u',4},
    {'m=r%u-r%u',1,1},
    {'if m<0 then goto L%u end',4},
    {'if m==0 then goto L%u end',4},
    {'if m>0 then goto L%u end',4},
    {'if m~=0 then goto L%u end',4},
}

t={io.open(arg[1],'rb'):read('*a'):byte(1,-1)}
i,n,r=1,0,{}
while i<=#t do
    c,i,x,a=C[t[i]+1],i+1,0,{}
    for j=2,#c do
        y=c[j]
        if y>0 then
            x=0 
            for k=1,y do 
                i,x=i+1,x*256+t[i]
            end 
        end
        S(a,x)
    end
    S(r,('::L%d::'):format(n)) 
    n=n+1
    S(r,c[1]:format(U(a)))
end
load(table.concat(r,' '))()
print(('R0 %u\nR1 %u\nR2 %u'):format(r0,r1,r2))

Quando ho eseguito il programma, ho riscontrato il seguente errore: pastebin.com/qQBD7Rs8 . Ti aspetti il ​​bytecode su stdin o come file?
Colorfully Monochrome,

Scusa. Il mio binario per Windows era corrotto. Pertanto, tutte le versioni di gcc / linux hanno funzionato, ma i test di Windows si sono tutti arrestati in modo anomalo. Tuttavia, sta ancora segnalando che R0 e R1 sono 0, mentre R2 è 1.
Colorfully Monochrome

Sospetto che in realtà non si stia eseguendo: ci sono voluti 33,8 ms per funzionare in media (GCC impiega ~ .25 secondi).
Colorfully monocromatico,

Lo script prevede il bytecode come file, con il nome file passato sulla riga di comando. Hai ragione però, l'ho rintracciato e sembra che stia solo facendo il primo ciclo esterno. Di nuovo al tavolo da disegno ...
criptych sta con Monica il

Questo è quello che ottengo pensando in C e scrivendo in Lua: ho usato <nei miei loop anziché <=, quindi l'istruzione di ramo finale è stata lasciata. Riceve ancora la risposta sbagliata, ma ora ci vogliono alcuni minuti per farlo. :)
criptych sta con Monica il

1

C #

1505 1475 byte

Questa è la mia versione dell'interprete, scritta in C # potrebbe essere ottimizzata / giocata a golf più credo, ma non so davvero dove;)

versione golfizzata:

using System;using System.Collections.Generic;using System.IO;using System.Linq;class M{static void Main(string[]a){if(a.Length==1&&File.Exists(a[0])){B.E(B.P(File.ReadAllLines(a[0])));Console.WriteLine(B.O);}}}class B{public enum I{L=0x00,P=0x02,Q=0x03,A=0x04,S=0x05,M=0x06,D=0x07,J=0x08,C=0x09,BL=0x0a,BE=0x0b,BG=0x0c,BN=0x0d}public enum R{A,B,C}enum C{N,L,E,G}public static Dictionary<R,uint>r=new Dictionary<R,uint>{{R.A,0},{R.B,0},{R.C,0}};public static Stack<uint>s=new Stack<uint>();static C c=C.N;public static string O{get{return string.Format("R0 {0}\nR1 {1}\nR2 {2}",r[R.A],r[R.B],r[R.C]);}}public static void E(byte[][]l){for(uint i=0;i<l.Length;i++){var q=l[i];switch((I)q[0]){case I.L:r[(R)q[1]]=U(q,2);break;case I.P:r[(R)q[1]]=s.Pop();break;case I.Q:s.Push(r[(R)q[1]]);r[(R)q[1]]=0;break;case I.A:s.Push(r[(R)q[1]]+r[(R)q[2]]);break;case I.S:s.Push(r[(R)q[1]]-r[(R)q[2]]);break;case I.M:s.Push(r[(R)q[1]]*r[(R)q[2]]);break;case I.D:s.Push(r[(R)q[1]]/r[(R)q[2]]);break;case I.J:i=U(q,1)-1;break;case I.C:{uint x=r[(R)q[1]],y=r[(R)q[2]];c=x<y?C.L:x>y?C.G:C.E;}break;case I.BL:if(c==C.L)i=U(q,1)-1;break;case I.BG:if(c==C.G)i=U(q,1)-1;break;case I.BE:if(c==C.E)i=U(q,1)-1;break;case I.BN:if(c!=C.E)i=U(q,1)-1;break;}}}public static byte[][]P(string[]c){return c.Where(l=>!l.StartsWith("#")).Select(r=>r.Split(' ').Where(b=>b.Length>0).Select(b=>Convert.ToByte(b,16)).ToArray()).Where(l=>l.Length>0).ToArray();}static uint U(byte[]b,int i){return(uint)(b[i]<<24|b[i+1]<<16|b[i+2]<<8|b[i+3]);}}

modificare

rimosso alcuni inutili publice privatemodificatori:

using System;using System.Collections.Generic;using System.IO;using System.Linq;class M{static void Main(string[]a){if(a.Length==1&&File.Exists(a[0])){B.E(B.P(File.ReadAllLines(a[0])));Console.Write(B.O);}}}class B{enum I{L=0x00,P=0x02,Q=0x03,A=0x04,S=0x05,M=0x06,D=0x07,J=0x08,C=0x09,BL=0x0a,BE=0x0b,BG=0x0c,BN=0x0d}enum R{A,B,C}enum C{N,L,E,G}static Dictionary<R,uint>r=new Dictionary<R,uint>{{R.A,0},{R.B,0},{R.C,0}};static Stack<uint>s=new Stack<uint>();static C c=C.N;public static string O{get{return string.Format("R0 {0}\nR1 {1}\nR2 {2}\n",r[R.A],r[R.B],r[R.C]);}}public static void E(byte[][]l){for(uint i=0;i<l.Length;i++){var q=l[i];switch((I)q[0]){case I.L:r[(R)q[1]]=U(q,2);break;case I.P:r[(R)q[1]]=s.Pop();break;case I.Q:s.Push(r[(R)q[1]]);r[(R)q[1]]=0;break;case I.A:s.Push(r[(R)q[1]]+r[(R)q[2]]);break;case I.S:s.Push(r[(R)q[1]]-r[(R)q[2]]);break;case I.M:s.Push(r[(R)q[1]]*r[(R)q[2]]);break;case I.D:s.Push(r[(R)q[1]]/r[(R)q[2]]);break;case I.J:i=U(q,1)-1;break;case I.C:{uint x=r[(R)q[1]],y=r[(R)q[2]];c=x<y?C.L:x>y?C.G:C.E;}break;case I.BL:if(c==C.L)i=U(q,1)-1;break;case I.BG:if(c==C.G)i=U(q,1)-1;break;case I.BE:if(c==C.E)i=U(q,1)-1;break;case I.BN:if(c!=C.E)i=U(q,1)-1;break;}}}public static byte[][]P(string[]c){return c.Where(l=>!l.StartsWith("#")).Select(r=>r.Split(' ').Where(b=>b.Length>0).Select(b=>Convert.ToByte(b,16)).ToArray()).Where(l=>l.Length>0).ToArray();}static uint U(byte[]b,int i){return(uint)(b[i]<<24|b[i+1]<<16|b[i+2]<<8|b[i+3]);}}

chiamalo con executable.exe filenamedov'è filenameil file contenente il codice da interpretare

Il mio "programma di test":

# LOAD R0 5
# CMP R0 R1
# BRANCHEQ 13
# LOAD R1 1
# LOAD R2 1
# CMP R0 R2
# MUL R1 R2
# LOAD R1 1
# ADD R2 R1
# PUSH R2
# PUSH R1 
# BRANCHEQ 13
# JMP 5
# POP R2
# POP R0
# POP R1
# PUSH R0

0x0 0x0 0x0 0x0 0x0 0x5
0x9 0x0 0x1 
0xb 0x0 0x0 0x0 0xd 
0x0 0x1 0x0 0x0 0x0 0x1 
0x0 0x2 0x0 0x0 0x0 0x1 
0x9 0x0 0x2 
0x6 0x1 0x2 
0x0 0x1 0x0 0x0 0x0 0x1 
0x4 0x2 0x1 
0x2 0x2 
0x2 0x1 
0xb 0x0 0x0 0x0 0xd 
0x8 0x0 0x0 0x0 0x5 
0x3 0x2 
0x3 0x0 
0x3 0x1 
0x2 0x0 

Interprete non golfato con variabili di nomi più lunghi, classi, ...

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length == 1 && File.Exists(args[0]))
        {
            var code = ByteCodeInterpreter.ParseCode(File.ReadAllLines(args[0]));
            ByteCodeInterpreter.Execute(code);
            Console.WriteLine(ByteCodeInterpreter.Output);
        }
    }
}

public static class ByteCodeInterpreter
{
    public enum Instruction : byte
    {
        LOAD = 0x00,
        PUSH = 0x02,
        POP = 0x03,
        ADD = 0x04,
        SUB = 0x05,
        MUL = 0x06,
        DIV = 0x07,
        JMP = 0x08,
        CMP = 0x09,
        BRANCHLT = 0x0a,
        BRANCHEQ = 0x0b,
        BRANCHGT = 0x0c,
        BRANCHNE = 0x0d
    }

    public enum Register : byte
    {
        R0 = 0x00,
        R1 = 0x01,
        R2 = 0x02
    }

    private enum CompareFlag : byte
    {
        NONE = 0x00,
        LT = 0x01,
        EQ = 0x02,
        GT = 0x03,
    }

    public static readonly Dictionary<Register, uint> register = new Dictionary<Register, uint>
    {
        {Register.R0, 0},
        {Register.R1, 0},
        {Register.R2, 0}
    };

    public static readonly Stack<uint> stack = new Stack<uint>();
    private static CompareFlag compareFlag = CompareFlag.NONE;

    public static string Output
    {
        get
        {
            return string.Format("R0 {0}\nR1 {1}\nR2 {2}", register[Register.R0], register[Register.R1],
                register[Register.R2]);
        }
    }

    public static void Execute(byte[][] lines)
    {
        for (uint i = 0; i < lines.Length; i++)
        {
            var line = lines[i];
            switch ((Instruction)line[0])
            {
                case Instruction.LOAD:
                    register[(Register)line[1]] = GetUint(line, 2);
                    break;
                case Instruction.PUSH:
                    register[(Register)line[1]] = stack.Pop();
                    break;
                case Instruction.POP:
                    stack.Push(register[(Register)line[1]]);
                    register[(Register)line[1]] = 0;
                    break;
                case Instruction.ADD:
                    stack.Push(register[(Register)line[1]] + register[(Register)line[2]]);
                    break;
                case Instruction.SUB:
                    stack.Push(register[(Register)line[1]] - register[(Register)line[2]]);
                    break;
                case Instruction.MUL:
                    stack.Push(register[(Register)line[1]] * register[(Register)line[2]]);
                    break;
                case Instruction.DIV:
                    stack.Push(register[(Register)line[1]] / register[(Register)line[2]]);
                    break;
                case Instruction.JMP:
                    i = GetUint(line, 1) - 1;
                    break;
                case Instruction.CMP:
                    {
                        uint v0 = register[(Register)line[1]], v1 = register[(Register)line[2]];
                        if (v0 < v1)
                            compareFlag = CompareFlag.LT;
                        else if (v0 > v1)
                            compareFlag = CompareFlag.GT;
                        else
                            compareFlag = CompareFlag.EQ;
                    }
                    break;
                case Instruction.BRANCHLT:
                    if (compareFlag == CompareFlag.LT)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHGT:
                    if (compareFlag == CompareFlag.GT)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHEQ:
                    if (compareFlag == CompareFlag.EQ)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHNE:
                    if (compareFlag != CompareFlag.EQ)
                        i = GetUint(line, 1) - 1;
                    break;
            }
        }
    }

    public static byte[][] ParseCode(string[] code)
    {
        return
            code.Where(line => !line.StartsWith("#"))
                .Select(line => line.Split(' ').Where(b => b.Length > 0).Select(b => Convert.ToByte(b, 16)).ToArray())
                .Where(line => line.Length > 0)
                .ToArray();
    }

    private static uint GetUint(byte[] bytes, int index)
    {
        return (uint)(bytes[index] << 24 | bytes[index + 1] << 16 | bytes[index + 2] << 8 | bytes[index + 3]);
    }
}
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.