Trình thông dịch mã byte / VM nhỏ nhất


17

Bảng xếp hạng - Biên soạn JIT (Thấp hơn là tốt hơn)

  1. es1024 - 81,2 điểm (bao gồm trình biên dịch hoạt động!)
  2. Kieth Randall - 116 điểm
  3. Ell - 121 điểm

Bảng xếp hạng - Giải thích (Thấp hơn là tốt hơn)

  1. Martin Büttner - 706654 điểm (đâu đó khoảng 2 giờ).
  2. criptych - 30379 điểm (97 giây)

Nhiệm vụ của bạn, nếu bạn chọn chấp nhận nó, là viết trình thông dịch / VM mã hóa nhỏ nhất có thể. VM / trình thông dịch sử dụng kiến ​​trúc CISC nhỏ (các thao tác có thể thay đổi kích thước), với ngôn ngữ được chỉ định bên dưới. Sau khi hoàn thành, bạn phải in giá trị của 3 thanh ghi CPU để chứng minh rằng đầu ra chính xác được in (3.126.900.366).

Trình biên dịch

Nếu bạn muốn thực hiện các bài kiểm tra của riêng mình, một trình biên dịch được đăng dưới đây. Hãy gửi bài kiểm tra của bạn với câu trả lời của bạn.

Thông số kỹ thuật "VM"

VM có 3 thanh ghi tích phân không dấu 32 bit: R0, R1, R2. Chúng được biểu diễn dưới dạng hex là 0x00, 0x01 và 0x02.

Các hoạt động sau đây phải được hỗ trợ:

Định dạng là [name] [... toán hạng ...], [mã thập lục phân] [... toán hạng được lặp lại ...]

  • LOAD [thanh ghi] [giá trị 4 byte], 0x00 [thanh ghi] [giá trị 4 byte]
  • PUSH [đăng ký], 0x02 [đăng ký]
  • POP [đăng ký], 0x03 [đăng ký]
  • THÊM [đăng ký, 1 byte] [đăng ký, 1 byte], 0x04 [đăng ký] [đăng ký]
  • SUB [đăng ký, 1 byte] [đăng ký, 1 byte], 0x05 [đăng ký] [đăng ký]
  • MUL [đăng ký, 1 byte] [đăng ký, 1 byte], 0x06 [đăng ký] [đăng ký]
  • DIV [đăng ký, 1 byte] [đăng ký, 1 byte], 0x07 [đăng ký] [đăng ký]
  • JMP [dòng mã, 4 byte], 0x08 [Số dòng mã 4 byte]
  • CMP [đăng ký, 1 byte] [đăng ký, 1 byte], 0x09 [đăng ký] [đăng ký]
  • BRANCHLT [dòng mã, 4 byte], 0x0a [số dòng mã 4 byte]

Một số lưu ý:

  • Các hoạt động toán học ở trên cộng các giá trị của 2 thanh ghi lại với nhau, đặt đầu ra vào thanh ghi đầu tiên.
  • CMP, toán tử so sánh, nên so sánh các giá trị của 2 thanh ghi và lưu trữ đầu ra trong một số cờ nội bộ (điều này có thể được thực hiện cụ thể) để sử dụng trong các hướng dẫn chi nhánh trong tương lai.
  • Nếu BRUC được gọi trước CMP, trừ khi BRUCEQ được gọi, "VM" sẽ không phân nhánh.
  • PUSH / POP không đáng ngạc nhiên đẩy số hoặc pop từ ngăn xếp.
  • Các toán tử Jump và Branch nhảy đến một hoạt động cụ thể (dòng mã), không phải là một địa chỉ nhị phân.
  • Chi nhánh hoạt động không làm so sánh. Thay vào đó, họ lấy đầu ra từ so sánh cuối cùng để thực hiện.
  • Các toán tử Branch và Jump sử dụng hệ thống lập chỉ mục số dòng dựa trên số không. (Ví dụ: JMP 0 nhảy đến dòng đầu tiên)
  • Tất cả các hoạt động phải được thực hiện trên các số không dấu mà tràn về 0 và không ném ngoại lệ vào tràn số nguyên.
  • Chia cho số 0 là không được phép và như vậy, hành vi của chương trình không được xác định. Bạn có thể (ví dụ) ...
    • Tai nạn chương trình.
    • Kết thúc thực thi VM và trả về trạng thái hiện tại của nó.
    • Hiển thị thông báo "ERR: Chia cho 0".
  • Chấm dứt chương trình được định nghĩa là khi con trỏ lệnh đến cuối chương trình (một chương trình không trống có thể được giả định).

Đầu ra Đầu ra phải chính xác như vậy (bao gồm các dòng mới)

R0 3126900366
R1 0
R2 10000    

Điểm Điểm được tính dựa trên công thức sau:Number Of Characters * (Seconds Needed To Run / 2)

Để tránh sự khác biệt về phần cứng gây ra các thời điểm khác nhau, mỗi thử nghiệm sẽ được chạy trên máy tính của tôi (i5-4210u, ram 8GB) trong máy chủ ub Ubuntu hoặc Windows 8, vì vậy hãy cố gắng không sử dụng một số thời gian chạy điên rồ chỉ biên dịch trên Dual G5 Mac Pro với chính xác 762,66 mb RAM miễn phí.

Nếu bạn đang sử dụng thời gian chạy / ngôn ngữ chuyên biệt, vui lòng gửi một liên kết đến nó.

Chương trình kiểm tra

Ý tưởng xuất phát từ đây , vì vậy chúng tôi sẽ sử dụng một phiên bản sửa đổi phần nào của chương trình của họ.

Đầu ra chính xác cho chương trình là: 3.126.900.366

Trong C:

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

Trong mã: [R0 là đại diện của s, R1 của j, R2 của 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

Trong nhị phân / 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

Điểm thưởng (Hiệu ứng được áp dụng nhiều lần) Ví dụ: nếu bạn đủ điều kiện cho cả ba, nó sẽ là ((ký tự * 0,50) * 0,75) * 0,90

  • Giảm 50% nếu trình thông dịch thực sự là trình biên dịch JIT
  • Giảm 25% nếu áp dụng bất kỳ loại vòng lặp không kiểm soát / tối ưu hóa có ý nghĩa.
  • Giảm 10% nếu bạn mở rộng VM với
    • BRANCHEQ [dòng mã, 4 byte] (Chi nhánh nếu bằng - opcode 0x0b)
    • BRANCHGT [dòng mã, 4 byte] (Chi nhánh nếu lớn hơn - opcode 0x0c)
    • BRANCHNE [dòng mã, 4 byte] (Chi nhánh nếu không bằng - opcode 0x0d)
    • TẢI XUỐNG [đăng ký 1] [đăng ký 2] (di chuyển giá trị của đăng ký 2 sang đăng ký 1 - opcode 0x01).

Không được phép

  • Việc biên dịch lại trường hợp thử nghiệm vào chương trình đều bị cấm. Bạn phải chấp nhận mã byte từ STDIN hoặc từ tệp (Không quan trọng).
  • Trả lại đầu ra mà không chạy chương trình.
  • Bất kỳ cách nào khác bạn có thể nghĩ ra để gian lận yêu cầu VM.

Tại sao không bao gồm một vài chương trình kiểm tra nữa để ngăn cản những điều bạn nói không được phép? Nếu là VM, nó sẽ có thể chạy bất cứ thứ gì được viết trong đặc tả mã byte, phải không?
Kasran

Tôi sẽ cố gắng làm điều đó tối nay. Tôi đang viết trình biên dịch ngay bây giờ.
Đơn sắc đầy màu sắc

Trình biên dịch có ở đây jsfiddle.net/aL9y19bd/23
Màu đơn sắc

1
CMPkiểm tra ít hơn hoặc bình đẳng? Và điều gì xảy ra với kết quả của nó?
es1024

1
MULDIVcũng không được xác định rõ. Họ nên được ký hoặc không dấu? Điều gì xảy ra trên tràn nhân?
frageum

Câu trả lời:


8

C, 752 (589 + 163 cho cờ xác định) * 0,5 (JIT) * 0,9 (tiện ích mở rộng) * (tối ưu hóa 0,75) * (0,64 giây / 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);}

Lấy mã ( LOAD R0, v.v.), không có ký tự dấu, dấu cách đơn, không có dòng trống ở giữa, không có nhận xét, v.v.

Điều này sau đó được chuyển đổi thành mã byte 80386 và được thực thi.

Đang tải 0vào một thanh ghi được thay thế bởi xoring sổ đăng ký với chính nó thay vì moving 0vào sổ đăng ký, đó là ba byte ngắn hơn ở bytecode tạo ra, và có thể rất nhẹ nhanh hơn.

Biên dịch với:

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

Yêu cầu hệ điều hành POSIX.

Đầu vào được đọc từ STDIN (sử dụng ./bytecode < fileđể dẫn từ một tệp).

Kết quả mã byte cho chương trình thử nghiệm:

; 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

Ung dung:

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

Ồ Tôi ước tôi đã thêm một phần thưởng cho việc bao gồm trình biên dịch trong VM.
Đơn sắc đầy màu sắc

Trung bình 0,67 giây mỗi lần chạy trên 15 lần chạy.
Đơn sắc đầy màu sắc

Tôi phải không đồng ý rằng xored là một tối ưu hóa. Mặc dù kích thước mã thông minh khôn ngoan, xoring không thay đổi các đặc tính hiệu suất của VM (sửa tôi nếu tôi sai). Điều tôi muốn nói là tối ưu hóa là thay đổi hoặc xóa hướng dẫn khỏi mã đầu vào (ví dụ: xóa POP dư thừa ... PUSH) hoặc thực hiện 2 hướng dẫn trong một hàng tải thanh ghi, do đó, có thể xóa một, v.v.
Đơn sắc màu

EDIT: Trên thực tế, đó là một tối ưu hóa: nó giảm xuống 0,64 giây mỗi lần chạy trong 15 lần chạy. Tôi đoán nó ngăn chặn việc đập bộ nhớ cache hoặc một cái gì đó bằng cách rút ngắn mã (hoặc loại bỏ truy cập bộ nhớ dự phòng)?
Đơn sắc đầy màu sắc

@ColorfullyMonochrom Một số kiến ​​trúc, khi được trình bày với việc xé một thanh ghi cho chính nó, sẽ không thực sự thực hiện lệnh, mà chỉ đơn giản là không đăng ký chính nó.
es1024

7

C, Điểm = 854 byte × (~ 0,8 giây / 2) × 0,5 [JIT] × 0,9 [Tiện ích mở rộng] = ~ 154 byte giây

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

Biên dịch với gcc vm.c -ovm -m32 -whệ điều hành tương thích POSIX x86.
Chạy với ./vm < program, đâu programlà một tệp chương trình nhị phân.


Đi cho tốc độ. Chương trình thực hiện một bản dịch khá đơn giản của chương trình đầu vào sang mã máy x86 và cho phép CPU làm phần còn lại.

Ví dụ, đây là bản dịch của chương trình thử nghiệm. ecx, esieditương ứng với R0, R1R2, tương ứng; bhgiữ các cờ trạng thái; eaxedxlà thanh ghi đầu; ngăn xếp cuộc gọi tương ứng với ngăn xếp của 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

Bị đánh cắp


Wow ... JIT của tôi là ~ 900 dòng mã (được viết bằng c ++) ...
Màu đơn sắc

Trung bình 0,63 giây mỗi lần chạy trong 15 lần chạy.
Đơn sắc đầy màu sắc

2

CJam, 222 187 185 byte * (quá chậm / 2)

Tôi chỉ muốn xem làm thế nào tôi có thể có được một VM mã byte bằng cách viết nó trong CJam. Ít hơn 200 byte có vẻ khá tốt. Mặc dù điều đó rất chậm, bởi vì bản thân CJam được giải thích. Phải mất nhiều năm để chạy chương trình thử nghiệm.

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

Để chạy nó, hãy tải xuống trình thông dịch Java tại liên kết sourceforge này , lưu mã vào vm.cjamvà chạy nó với

java -jar cjam-0.6.2.jar vm.cjam

Chương trình mong đợi mã byte trên STDIN. Tôi chưa tìm thấy cách chuyển dữ liệu nhị phân vào một chương trình, mà không cần PowerShell thêm ngắt dòng và chuyển đổi 0x0asang 0x0d 0x0a, điều này thực sự gây phiền nhiễu. Mã này bao gồm 4 byte để sửa lỗi ( D-);) mà tôi chưa bao gồm trong tổng số, bởi vì đó không phải là điều mà chương trình cần phải làm nếu nó thực sự nhận được mã byte trên STDIN, thay vì một phiên bản được mã hóa kỳ lạ của nó . Nếu ai đó biết một sửa chữa cho điều đó, xin vui lòng cho tôi biết.

Hơi vô dụng:

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

Tôi sẽ thêm một lời giải thích thích hợp vào ngày mai.

Nói tóm lại, tôi đang lưu trữ tất cả các thanh ghi, con trỏ lệnh và cờ so sánh trong các biến, để tôi có thể giữ ngăn xếp của CJam miễn phí sử dụng làm ngăn xếp của VM.



1
Trung bình 15.279 giây cho 20 lần lặp. - 15 bài kiểm tra. Điều đó có nghĩa là 2,12208333 giờ mỗi bài kiểm tra.
Đơn sắc đầy màu sắc

1

trăn / c ++, điểm = 56,66

1435 ký tự * .234 / 2 giây * .5 [JIT] * .75 [Tối ưu hóa] * .90 [Hướng dẫn thêm]

Biên dịch chương trình đầu vào thành c ++, chạy gcc trên đó, sau đó chạy kết quả. Hầu hết thời gian được dành cho gcc.

Tối ưu hóa tôi làm là giảm các hoạt động ngăn xếp thành các biến rõ ràng nếu nó được cho phép về mặt ngữ nghĩa. Nó giúp rất nhiều, khoảng 10 lần thời gian chạy tốt hơn của mã được biên dịch (khoảng 0,56 giây để thực sự chạy nhị phân kết quả). Tôi không chắc gcc đang làm gì giúp bạn cải thiện điều đó, nhưng nó tốt.

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

Chắc chắn có thể được chơi golf nhiều hơn nữa.


Trung bình 0,477 giây mỗi lần chạy trên 15 lần chạy.
Đơn sắc đầy màu sắc

1

Lua 5.2 (hoặc LuaJIT), 740 byte

Lần thử đầu tiên, chỉ chơi golf tối thiểu. Phiên bản này hoạt động (ít nhất là trên chương trình thử nghiệm) và thực hiện các mã bổ sung, nhưng không duy trì yêu cầu toán học không dấu và không đặc biệt nhanh. Tuy nhiên, như một phần thưởng, đó là một VM chạy trong VM và được viết sao cho nó có thể được hiểu (chạy bằng PUC-Lua) hoặc sắp xếp JIT (chạy với LuaJIT; vẫn được thông dịch, nhưng hiện tại trình thông dịch BẮT ĐẦU).

EDIT: Chơi golf tốt hơn, vẫn lớn.

EDIT: Đã sửa một lỗi lớn và hiện hạn chế số học trong unsigned longphạm vi. Tuy nhiên, bằng cách nào đó quản lý để giữ cho kích thước ra khỏi tầm tay, nhưng nó vẫn đưa ra câu trả lời sai.

EDIT: Hóa ra, kết quả đã đúng nhưng đầu ra thì không. Chuyển sang in với %uthay vì %dvà tất cả là tốt. Cũng chuyển ra các thanh ghi dựa trên bảng cho các biến để cải thiện kích thước tốc độ phần nào.

EDIT: Sử dụng gotocâu lệnh của Lua 5.2 (cũng có sẵn trong LuaJIT) Tôi đã thay thế trình thông dịch bằng "JIT-to-Lua", tạo mã được chạy trực tiếp bởi chính Lua VM. Không chắc chắn nếu điều này thực sự được tính là JIT, nhưng nó sẽ cải thiện tốc độ.

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

Đây là phiên bản gốc, có thể đọc được.

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

Khi tôi chạy chương trình của bạn, tôi đã gặp lỗi sau: pastebin.com/qQBD7Rs8 . Bạn có mong đợi mã byte trên stdin hoặc dưới dạng tệp không?
Đơn sắc đầy màu sắc

Lấy làm tiếc. Nhị phân của tôi cho các cửa sổ đã bị hỏng. Do đó, tất cả các phiên bản gcc / linux đều hoạt động nhưng các cửa sổ kiểm tra đều bị lỗi. Tuy nhiên, vẫn báo cáo rằng R0 và R1 là 0, trong khi R2 là 1.
Đơn sắc nhiều màu vào

Tôi nghi ngờ nó không thực sự thực thi: trung bình mất 33,8 ms để chạy (GCC mất ~ 0,25 giây).
Màu đơn sắc

Kịch bản lệnh hy vọng mã byte là một tệp, với tên tệp được truyền trên dòng lệnh. Mặc dù bạn đúng, tôi đã theo dõi nó và có vẻ như nó chỉ thực hiện vòng lặp bên ngoài đầu tiên. Quay lại bảng vẽ ...
criptych đứng cùng với Monica

Đó là những gì tôi nhận được khi nghĩ về C và viết bằng Lua: Tôi đã sử dụng <các vòng lặp của mình thay vì <=, vì vậy hướng dẫn chi nhánh cuối cùng đã bị bỏ đi. Nó vẫn nhận được câu trả lời sai, nhưng bây giờ mất vài phút để làm điều đó. :)
criptych đứng cùng với Monica

1

C #

1505 1475 byte

Đây là phiên bản trình thông dịch của tôi, được viết bằng C # có thể được tối ưu hóa / đánh gôn nhiều hơn tôi nghĩ, nhưng tôi thực sự không biết ở đâu;)

phiên bản đánh gôn:

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

biên tập

loại bỏ một số không cần thiết publicprivatesửa đổi:

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

gọi nó với executable.exe filenamenơi filenametập tin chứa mã sẽ được giải thích

"Chương trình thử nghiệm" của tôi:

# 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 

Thông dịch viên không hiểu biết với các biến, lớp, ...

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]);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.