Bytecode Interpreter / VM terkecil

17

Papan Peringkat - JIT Dikompilasi (Lebih Rendah Lebih Baik)

  1. es1024 - 81.2 poin (termasuk kompiler yang berfungsi!)
  2. Kieth Randall - 116 poin
  3. Ell - 121 poin

Papan - Ditafsirkan (Lebih rendah lebih baik)

  1. Martin Büttner - 706654 poin (sekitar 2 jam).
  2. criptych - 30379 poin (97 detik)

Misi Anda, jika Anda memilih untuk menerimanya, adalah untuk menulis bytecode interpreter / VM terkecil. VM / interpreter menggunakan arsitektur CISC kecil (ukuran operasi dapat bervariasi), dengan bahasa yang ditentukan di bawah ini. Setelah selesai, Anda harus mencetak nilai 3 register CPU untuk membuktikan bahwa output yang benar dicetak (3.126.900.366).

Penyusun

Jika Anda ingin membuat tes sendiri, kompiler diposting di bawah ini. Jangan ragu untuk memposting tes Anda dengan jawaban Anda.

Spesifikasi "VM"

VM memiliki 3 register integral 32 32 bit yang tidak ditandai: R0, R1, R2. Mereka direpresentasikan dalam hex sebagai 0x00, 0x01, dan 0x02.

Operasi berikut harus didukung:

Formatnya adalah [nama] [... operan ...], [kode-heksadesimal] [... operan diulang ...]

  • LOAD [daftar] [nilai 4 byte], 0x00 [daftar] [nilai 4 byte]
  • PUSH [daftar], 0x02 [daftar]
  • POP [daftar], 0x03 [daftar]
  • TAMBAH [daftar, 1 byte] [daftar, 1 byte], 0x04 [daftar] [daftar]
  • SUB [daftar, 1 byte] [daftar, 1 byte], 0x05 [daftar] [daftar]
  • MULI [daftar, 1 byte] [daftar, 1 byte], 0x06 [daftar] [daftar]
  • DIV [daftar, 1 byte] [daftar, 1 byte], 0x07 [daftar] [daftar]
  • JMP [baris kode, 4 byte], 0x08 [nomor baris kode 4 byte]
  • CMP [daftar, 1 byte] [daftar, 1 byte], 0x09 [daftar] [daftar]
  • BRANCHLT [baris kode, 4 byte], 0x0a [nomor baris kode 4 byte]

Beberapa catatan:

  • Operasi matematika di atas menambahkan nilai 2 register bersama-sama, menempatkan output di register pertama.
  • CMP, operator pembanding, harus membandingkan nilai 2 register dan menyimpan output dalam beberapa flag internal (ini bisa khusus implementasi) untuk digunakan di masa depan pada instruksi cabang.
  • Jika BRANCH dipanggil sebelum CMP, kecuali BRANCHEQ dipanggil, "VM" seharusnya tidak bercabang.
  • PUSH / POP secara tak terduga mendorong atau mengeluarkan nomor dari tumpukan.
  • Lompat dan Cabang operator melompat ke operasi tertentu (baris kode), bukan alamat biner.
  • Operasi cabang tidak melakukan perbandingan. Sebaliknya, mereka mengambil output dari perbandingan terakhir untuk dieksekusi.
  • Operator Branch and Jump menggunakan sistem pengindeksan angka garis berbasis nol. (Misalnya JMP 0 melompat ke baris pertama)
  • Semua operasi harus dilakukan pada angka yang tidak ditandatangani yang melimpah ke nol dan tidak membuang pengecualian pada bilangan bulat bilangan bulat.
  • Pembagian dengan nol tidak diperbolehkan dan dengan demikian, perilaku program tidak didefinisikan. Anda dapat (misalnya) ...
    • Hancurkan program.
    • Akhiri pelaksanaan VM dan kembalikan ke kondisi saat ini.
    • Tampilkan pesan "ERR: Division by 0".
  • Pengakhiran program didefinisikan sebagai ketika penunjuk instruksi mencapai akhir program (program yang tidak kosong dapat diasumsikan).

Output Keluaran harus persis seperti ini (termasuk baris baru)

R0 3126900366
R1 0
R2 10000    

Poin Poin dihitung berdasarkan rumus berikut:Number Of Characters * (Seconds Needed To Run / 2)

Untuk menghindari perbedaan perangkat keras yang menyebabkan waktu yang berbeda, setiap tes akan dijalankan di komputer saya (i5-4210u, ram 8GB) di server ubuntu atau Windows 8, jadi cobalah untuk tidak menggunakan beberapa runtime gila-eksotik yang hanya dikompilasi pada Dual G5 Mac Pro dengan tepat 762,66 mb RAM gratis.

Jika Anda menggunakan runtime / bahasa khusus, silakan kirim tautannya.

Program Tes

Ide itu datang dari sini , jadi kami akan menggunakan versi program mereka yang agak dimodifikasi.

Output yang benar untuk program ini adalah: 3.126.900.366

Dalam C:

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

Dalam kode: [R0 mewakili s, R1 j, R2 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

Dalam biner / hex:

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

Poin Bonus (Efek diterapkan multiplikasi) Misalnya jika Anda memenuhi syarat untuk ketiganya, itu akan menjadi ((karakter * 0,50) * 0,75) * 0,90

  • 50% berkurang jika penerjemah sebenarnya adalah kompiler JIT
  • Penurunan 25% jika menerapkan segala bentuk pengulangan membuka gulungan / optimasi yang berarti.
  • Penurunan 10% jika Anda memperpanjang VM dengan
    • BRANCHEQ [baris kode, 4 byte] (Cabang jika sama - opcode 0x0b)
    • BRANCHGT [kode baris, 4 byte] (Cabang jika lebih besar dari - opcode 0x0c)
    • BRANCHNE [kode baris, 4 byte] (Cabang jika tidak sama - opcode 0x0d)
    • RLOAD [register 1] [register 2] (pindahkan nilai register 2 ke register 1 - opcode 0x01).

Tidak diizinkan

  • Mengompilasi kasus uji ke dalam program dilarang. Anda harus menerima bytecode dari STDIN atau dari file (Tidak masalah yang mana).
  • Mengembalikan output tanpa menjalankan program.
  • Cara lain yang dapat Anda pikirkan untuk menipu persyaratan VM.
Monokrom Penuh Warna
sumber
Mengapa tidak menyertakan beberapa program pengujian lagi untuk mencegah hal-hal yang Anda katakan dilarang? Jika itu adalah VM, itu harus dapat menjalankan apa pun yang ditulis dalam spesifikasi bytecode, kan?
Kasran
Saya akan mencoba melakukannya malam ini. Saya sedang menulis kompiler sekarang.
Penuh warna Monokrom
Kompilernya ada di sini jsfiddle.net/aL9y19bd/23
Colorfully Monochrome
1
Apakah CMPmemeriksa kurang dari atau setara? Dan apa yang terjadi pada hasilnya?
es1024
1
MULdan DIVjuga tidak ditentukan. Haruskah mereka ditandatangani atau tidak ditandatangani? Apa yang terjadi pada multiplication overflow?
feersum

Jawaban:

8

C, 752 (589 + 163 untuk flag yang ditentukan) * 0,5 (JIT) * 0,9 (ekstensi) * (optimasi 0,75) * (0,64 detik / 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);}

Mengambil kode ( LOAD R0, dll), tidak ada karakter trailing, spasi tunggal, tidak ada baris kosong di tengah, tidak ada komentar, dll. Trailing newline diperlukan.

Ini kemudian dikonversi ke bytecode 80386 dan dieksekusi.

Memuat 0ke register digantikan oleh xoring register dengan dirinya sendiri bukan moving0 ke dalam register, yang merupakan tiga byte pendek di bytecode yang dihasilkan, dan mungkin sangat sedikit lebih cepat.

Kompilasi dengan:

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

Diperlukan OS yang mendukung POSIX.

Input dibaca dari STDIN (gunakan ./bytecode < fileuntuk menyalurkan dari file).

Bytecode yang dihasilkan untuk program uji:

; 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

Tidak Disatukan:

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);
}
es1024
sumber
Wow. Saya berharap saya akan menambahkan bonus untuk memasukkan kompiler di VM.
Monokrom Penuh Warna
Rata-rata 0,67 detik per run lebih dari 15 run
Monokrom Penuh Warna
Saya harus tidak setuju bahwa xoring adalah optimasi. Meskipun ukuran kode pintar, xoring tidak mengubah karakteristik kinerja VM (koreksi saya jika saya salah). Yang saya maksud dengan optimasi adalah mengubah atau menghapus instruksi dari kode input (mis. Menghapus POP yang berlebihan ... PUSH) atau mewujudkan 2 instruksi dalam satu baris memuat register, sehingga seseorang dapat dihapus, dll.
Colourful Monochrome
EDIT: Sebenarnya, itu adalah optimasi: turun menjadi 0,64 detik per run lebih dari 15 run. Saya kira itu mencegah meronta-ronta cache atau sesuatu dengan mempersingkat kode (atau menghapus akses memori yang berlebihan)?
Monokrom Penuh Warna
@ColorfullyMonochrome Beberapa arsitektur, ketika disajikan dengan xoring sebuah register untuk dirinya sendiri, tidak akan benar-benar menjalankan instruksi, tetapi hanya nol register itu sendiri.
es1024
7

C, Skor = 854 byte × (~ 0,8 detik / 2) × 0,5 [JIT] × 0,9 [Ekstensi] = ~ 154 byte detik

#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]);}

Kompilasi dengan gcc vm.c -ovm -m32 -wOS yang kompatibel dengan POSIX x86.
Jalankan dengan ./vm < program, di mana programfile program biner.


Pergi untuk kecepatan. Program ini melakukan terjemahan program input yang sangat mudah ke kode mesin x86 dan memungkinkan CPU melakukan sisanya.

Misalnya, inilah terjemahan dari program pengujian. ecx, esidan edisesuai dengan R0, R1dan R2, masing-masing; bhmemegang bendera status; eaxdan edxmerupakan register awal; tumpukan panggilan sesuai dengan tumpukan 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

Tidak disatukan

Elo
sumber
Wow ... JIT saya adalah ~ 900 baris kode (ditulis dalam c ++) ...
Colourful Monochrome
Rata-rata 0,63 detik per lari selama 15 berjalan.
Monokrom Penuh Warna
2

CJam, 222 187 185 byte * (terlalu lambat / 2)

Saya hanya ingin melihat seberapa pendek saya bisa mendapatkan bytecode VM dengan menulisnya di CJam. Kurang dari 200 byte tampaknya cukup baik. Ini sangat lambat, karena CJam sendiri ditafsirkan. Butuh waktu lama untuk menjalankan program pengujian.

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}/

Untuk menjalankannya, unduh Java interpreter di tautan sourceforge ini , simpan kodenya vm.cjamdan jalankan bersama

java -jar cjam-0.6.2.jar vm.cjam

Program ini mengharapkan bytecode pada STDIN. Saya belum menemukan cara untuk menyalurkan data biner ke dalam sebuah program, tanpa PowerShell menambahkan jeda baris tambahan dan mengubahnya 0x0amenjadi 0x0d 0x0a, yang benar-benar menjengkelkan. Kode ini mencakup 4 byte untuk memperbaikinya (D-); ), yang belum saya sertakan dalam jumlah total, karena itu bukan sesuatu yang harus dilakukan oleh program jika ia benar-benar menerima bytecode itu sendiri di STDIN, alih-alih beberapa versi yang dikodekan secara aneh dari kode itu. . Jika seseorang mengetahui perbaikan untuk itu, beri tahu saya.

Sedikit tidak berbulu:

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}/

Saya akan menambahkan penjelasan yang tepat besok.

Singkatnya, saya menyimpan semua register, pointer instruksi dan flag perbandingan dalam variabel, sehingga saya bisa menjaga stack CJam bebas untuk digunakan sebagai stack VM.

Martin Ender
sumber
Mari kita lanjutkan diskusi ini dalam obrolan .
Martin Ender
1
Rata-rata 15.279 detik untuk 20 iterasi. - 15 tes. Itu berarti 2,12208333 jam per tes.
Monokrom Penuh Warna
1

python / c ++, skor = 56,66

1435 karakter * .234 / 2 detik * .5 [JIT] * .75 [Optimasi] * .90 [Instruksi tambahan]

Mengkompilasi program input ke c ++, menjalankan gcc di atasnya, lalu menjalankan hasilnya. Sebagian besar waktu dihabiskan di dalam gcc.

Salah satu optimasi yang saya lakukan adalah mengurangi operasi stack menjadi variabel eksplisit jika diizinkan secara semantik. Ini sangat membantu, sekitar 10x lebih baik runtime dari kode yang dikompilasi (sekitar 0,056 detik untuk benar-benar menjalankan biner yang dihasilkan). Saya tidak yakin apa yang dilakukan gcc yang membuat Anda mendapatkan peningkatan itu, tapi itu bagus.

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")

Bisa dipastikan golf lagi.

Keith Randall
sumber
Rata-rata 0,477 detik per run lebih dari 15 run.
Monokrom Penuh Warna
1

Lua 5.2 (atau LuaJIT), 740 byte

Pertama coba, hanya golf minimal. Versi ini berfungsi (setidaknya pada program pengujian), dan mengimplementasikan opcode tambahan, tetapi tidak menjunjung tinggi persyaratan matematika yang tidak ditandatangani dan tidak terlalu cepat. Sebagai bonus, ini adalah VM yang berjalan dalam VM, dan ditulis sedemikian rupa sehingga dapat diinterpretasikan (dijalankan dengan PUC-Lua) atau semacam-JIT (dijalankan dengan LuaJIT; masih ditafsirkan, tetapi interpreternya sekarang JITted).

EDIT: Golf lebih baik, masih besar.

EDIT: Memperbaiki kesalahan utama, dan sekarang membatasi aritmatika ke unsigned longrentang. Namun, entah bagaimana berhasil menjaga ukuran agar tidak lepas kendali, tetapi tetap saja memberikan jawaban yang salah.

EDIT: Ternyata, hasilnya benar tetapi hasilnya tidak. Beralih ke pencetakan dengan %ualih - alih %ddan semuanya baik-baik saja. Juga beralih register berbasis tabel untuk variabel untuk meningkatkan ukuran dan kecepatan agak.

EDIT: Menggunakan gotopernyataan Lua 5.2 (juga tersedia di LuaJIT) Saya telah mengganti penerjemah dengan "JIT-to-Lua," menghasilkan kode yang dijalankan langsung oleh Lua VM itu sendiri. Tidak yakin apakah ini benar-benar dianggap sebagai JIT, tetapi itu meningkatkan kecepatan.

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))

Ini versi aslinya yang dapat dibaca.

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))
criptych berdiri bersama Monica
sumber
Ketika saya menjalankan program Anda, saya mendapatkan kesalahan berikut: pastebin.com/qQBD7Rs8 . Apakah Anda mengharapkan bytecode melalui stdin atau sebagai file?
Penuh warna Monokrom
Maaf. Biner saya untuk windows rusak. Dengan demikian, semua versi gcc / linux bekerja tetapi tes windows semua macet. Namun, masih melaporkan bahwa R0 dan R1 adalah 0, sedangkan R2 adalah 1.
Colourful Monochrome
Saya menduga itu tidak benar-benar mengeksekusi: butuh rata-rata 33,8 ms untuk berjalan (GCC membutuhkan ~ .25 detik).
Monokrom Penuh Warna
Script mengharapkan bytecode sebagai file, dengan nama file yang diteruskan pada baris perintah. Anda benar, saya melacaknya dan sepertinya itu hanya melakukan loop luar pertama. Kembali ke papan gambar ...
criptych berdiri bersama Monica
Itulah yang saya dapatkan untuk berpikir dalam bahasa C dan menulis dalam bahasa Lua: Saya menggunakan <di loop saya alih-alih <=, sehingga instruksi cabang terakhir ditinggalkan. Masih mendapat jawaban yang salah, tetapi sekarang perlu beberapa menit untuk melakukannya. :)
criptych berdiri bersama Monica
1

C #

1505 1475 byte

Ini adalah versi penerjemah saya, ditulis dalam bahasa C # dapat dioptimalkan / golf lebih saya pikir, tapi saya tidak benar-benar tahu di mana;)

versi golf:

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]);}}

sunting

menghapus beberapa yang tidak perlu publicdan privatepengubah:

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]);}}

panggil dengan executable.exe filenamemana filenamefile yang berisi kode yang akan ditafsirkan

"Program pengujian" saya:

# 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 

Penerjemah tidak tahu nama variabel, kelas, ...

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]);
    }
}
Stefan
sumber