Найменший перекладач байт-кодів / ВМ


17

Табло - Складено JIT (нижче краще)

  1. es1024 - 81,2 бала (включаючи робочий компілятор!)
  2. Кіт Рендалл - 116 балів
  3. Елл - 121 бал

Таблиця лідерів - Інтерпретована (нижче краще)

  1. Мартін Бюттнер - 706654 балів (десь близько 2 годин).
  2. криптих - 30379 балів (97 секунд)

Якщо ви вирішите прийняти це, ваша місія полягає в тому, щоб написати найменший можливий інтерпретатор байт-коду / VM. ВМ / інтерпретатор використовує невелику архітектуру CISC (операції можуть відрізнятися за розміром), мовою, зазначеною нижче. Після завершення потрібно надрукувати значення трьох регістрів процесора, щоб довести, що надруковано правильний вихід (3,126,900,366).

Укладач

Якщо ви хочете зробити власні тести, нижче розміщений компілятор. Сміливо розміщуйте свої тести зі своєю відповіддю.

Технічні характеристики "VM"

ВМ має 3 32-бітні непідписані інтегральні регістри: R0, R1, R2. Вони представлені в шістнадцятковій формі як 0x00, 0x01 та 0x02.

Необхідно підтримувати наступні операції:

Формат: [ім'я] [... операнди ...], [шістнадцятковий оп-код] [... операнди повторюються ...]

  • ЗАВАНТАЖЕННЯ [регістр] [4 байтне значення], 0х00 [регістр] [4 байтне значення]
  • PUSH [регістр], 0x02 [реєстр]
  • POP [реєстр], 0x03 [реєстр]
  • ADD [регістр, 1 байт] [регістр, 1 байт], 0x04 [регістр] [регістр]
  • SUB [регістр, 1 байт] [регістр, 1 байт], 0x05 [регістр] [регістр]
  • MUL [регістр, 1 байт] [регістр, 1 байт], 0x06 [регістр] [регістр]
  • DIV [регістр, 1 байт] [регістр, 1 байт], 0x07 [регістр] [регістр]
  • JMP [рядок коду, 4 байти], 0x08 [номер 4-байтового кодового рядка]
  • CMP [регістр, 1 байт] [регістр, 1 байт], 0x09 [регістр] [регістр]
  • BRANCHLT [рядок коду, 4 байти], 0x0a [номер 4-байтового кодового рядка]

Деякі примітки:

  • Вищеописані математичні операції додають значення двох регістрів разом, розміщуючи вихід у першому регістрі.
  • CMP, оператор порівняння, повинен порівнювати значення 2 регістрів і зберігати вихід у якомусь внутрішньому прапорі (це може бути специфічно для реалізації) для подальшого використання в інструкціях гілки.
  • Якщо BRANCH викликається перед CMP, якщо BRANCHEQ не викликається, "VM" не повинен розгалужуватися.
  • PUSH / POP несподівано натискає чи виводить номери зі стека.
  • Оператори Jump and Branch переходять до певної операції (рядка коду), а не бінарної адреси.
  • Операції з філіями не роблять порівняння. Швидше, вони беруть висновок з останнього порівняння для виконання.
  • Оператори відділення та переходу використовують систему індексації нумерації рядків на основі нуля. (Наприклад, JMP 0 переходить до першого рядка)
  • Усі операції слід виконувати на непідписаних числах, які переповнюються до нуля і не викидають виняток для цілого переповнення.
  • Ділення на нуль заборонено, і поведінка програми не визначається. Ви можете (наприклад) ...
    • Збій програму.
    • Закінчити виконання VM та повернути його поточний стан.
    • Показати повідомлення "ERR: Поділ на 0".
  • Припинення програми визначається як тоді, коли покажчик інструкції досягає кінця програми (не можна вважати порожню програму).

Вихід Вихід повинен бути саме таким (включені нові рядки)

R0 3126900366
R1 0
R2 10000    

Очки Бали обчислюються за такою формулою:Number Of Characters * (Seconds Needed To Run / 2)

Щоб уникнути різниць апаратних засобів, що спричиняють різний час, кожен тест буде запускатися на моєму комп’ютері (i5-4210u, 8 ГБ оперативної пам’яті) на сервері ubuntu або Windows 8, тому намагайтеся не використовувати деякий божевільно-екзотичний час виконання, який збирається лише на Dual G5 Mac Pro з рівно 762,66 мб вільної оперативної пам’яті.

Якщо ви використовуєте спеціалізовану програму виконання / мову, будь ласка, опублікуйте посилання на неї.

  • Для зацікавлених сторін я виклав код тестування (написаний на C #) тут: http://pastebin.com/WYCG5Uqu

Тестова програма

Ідея виникла звідси , тому ми будемо використовувати дещо модифіковану версію їх програми.

Правильний вихід для програми: 3,126,900,366

В:

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

У коді: [R0 є представником 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

У двійковій / шестигранній формі:

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

Бонусні бали (ефекти застосовуються мультипликативно) Наприклад, якщо ви маєте право на всі три, це було б ((символи * 0,50) * 0,75) * 0,90

  • На 50% зменшується, якщо перекладач насправді є компілятором JIT
  • На 25% зменшується, якщо застосовується якийсь цикл розкручування / змістовної оптимізації.
  • На 10% зменшується, якщо ви продовжите VM за допомогою
    • BRANCHEQ [рядок коду, 4 байти] (Відгалуження, якщо рівне - опкод 0x0b)
    • BRANCHGT [рядок коду, 4 байти] (Відділення, якщо більше - opcode 0x0c)
    • BRANCHNE [рядок коду, 4 байти] (Відгалуження, якщо не рівне - opcode 0x0d)
    • RLOAD [регістр 1] [регістр 2] (перемістіть значення регістру 2 на реєстр 1 - опкод 0x01).

Заборонено

  • Попереднє компілювання тестового випадку в програму заборонено. Ви повинні або прийняти байт-код із STDIN або з файлу (не важливо, який).
  • Повернення виводу без запуску програми.
  • Будь-який інший спосіб ви можете придумати, щоб обдурити вимогу VM.

Чому б не включити ще кілька тестових програм, щоб відмовитись від того, що ви сказали, заборонено? Якщо це VM, він повинен мати можливість запускати все, що написано в специфікації байт-коду, правда?
Касран

Я спробую це зробити сьогодні ввечері. Я зараз пишу компілятор.
Барвисто монохромний


1
Чи CMPперевіряється на менше чи рівність? І що відбувається з її результатом?
es1024

1
MULі DIVтакож не визначено. Чи повинні вони бути підписані або без підпису? Що відбувається при переповненні множення?
feersum

Відповіді:


8

C, 752 (589 + 163 для визначення прапорців) * 0,5 (JIT) * 0,9 (розширення) * (оптимізація 0,75) * (0,64 секунди / 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);}

Приймає код ( LOAD R0тощо), відсутні символи, пробіли, пробіли в середині, відсутні коментарі тощо. Не потрібно вводити новий рядок.

Потім це перетворюється в 80386 байт-код і виконується.

Завантаження 0в регістр замінюється xoring-регістром із собою, а не moving0 в регістр, який на три байти коротший у створеному байтовому коді, і може бути дуже незначно швидшим.

Зібрати:

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

Потрібна ОС-сумісна ОС.

Введення зчитується з STDIN (використання ./bytecode < fileдля передачі з файлу).

Отриманий байт-код для тестової програми:

; 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

Безголовки:

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

Ого. Я хотів би, щоб я додав бонус за включення компілятора до VM.
Барвисто монохромний

В середньому .67 секунд за пробіг протягом 15 пробіжок.
Барвисто монохромний

Я маю згоду з тим, що xoring - це оптимізація. Хоча розумний розмір коду мудрий, xoring не змінює експлуатаційні характеристики VM (виправте мене, якщо я помиляюся). Що я мав на увазі під оптимізацією - це зміна або видалення інструкцій з вхідного коду (наприклад, видалення зайвого POP ... PUSH) або реалізація 2 інструкцій підряд завантажують регістр, так що одну можна видалити і т. Д.
Барвисто монохромний

EDIT: Власне, це оптимізація: вона знизилася до 0,64 секунди за цикл протягом 15 циклів. Я думаю, що це запобігає обробці кешу чи щось, скорочуючи код (або видаляючи зайві доступ до пам'яті)?
Барвисто монохромний

@ColorfullyMonochrome Деякі архітектури, коли представлені з допомогою xoring реєстру до себе, фактично не виконують інструкцію, а просто нулюють сам регістр.
es1024

7

C, Оцінка = 854 байт × (~ 0,8 сек / 2) × 0,5 [JIT] × 0,9 [Розширення] = ~ 154 байт сек

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

Компілюйте з gcc vm.c -ovm -m32 -wОС, сумісною з x86 POSIX.
Запустіть ./vm < program, де programє файл бінарної програми.


Йдеш за швидкістю. Програма виконує досить прямий переклад програми введення в машинний код x86 і дозволяє процесору робити все інше.

Наприклад, ось переклад програми тестування. ecx, esiі ediвідповідають R0, R1і R2відповідно; bhтримає прапори статусу; eaxі edxє регістрами нуля; стек викликів відповідає стеку 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

Безумовно


Нічого ... мій JIT склав ~ 900 рядків коду (написано c ++) ...
Барвисто монохромний

В середньому 0,63 секунди на пробіг за 15 пробіжок.
Барвисто монохромний

2

CJam, 222 187 185 байт * (занадто повільно / 2)

Я просто хотів побачити, як коротко я можу отримати байт-код VM, записавши його в CJam. Менше 200 байтів здається досить пристойним. Це чорт повільно, тому що сам CJam інтерпретується. Для запуску програми тестування потрібні віки.

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

Щоб запустити його, завантажте інтерпретатор Java за цим посиланням sourceforge , збережіть код vm.cjamі запустіть його

java -jar cjam-0.6.2.jar vm.cjam

Програма очікує байт-код на STDIN. Я ще не знайшов способу передавати бінарні дані в програму, не додавши PowerShell розриву кінцевої лінії та переходу 0x0aна це 0x0d 0x0a, що насправді дратує. Код включає 4 байти, щоб виправити це (D-); ), який я не включив до загального підрахунку, тому що програма не повинна робити, якщо вона насправді отримала сам байт-код на STDIN, а не якусь дивно закодовану його версію. . Якщо хтось знає виправлення для цього, будь ласка, дайте мені знати.

Трохи незворушений:

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

Я завтра додам належне пояснення.

Коротше кажучи, я зберігаю всі регістри, покажчик інструкцій та прапор порівняння у змінних, щоб я міг стек CJam вільним використовувати як стек VM.



1
В середньому 15.279 секунд за 20 ітерацій. - 15 тестів. Це означає 2.12208333 години за тест.
Барвисто монохромний

1

python / c ++, оцінка = 56,66

1435 символів * .234 / 2 секунди * .5 [JIT] * .75 [Оптимізація] * .90 [Додаткові інструкції]

Складає програму введення в c ++, запускає на ній gcc, після чого запускає результат. Більша частина часу проводиться всередині gcc.

Оптимізація, яку я роблю, - це зменшити операції стека до явних змінних, якщо це дозволено семантично. Це дуже допомагає, приблизно в 10 разів кращий час компільованого коду (приблизно 0,056 сек, щоб реально запустити отриманий двійковий код). Я не впевнений, що робить GCC, що дає вам таке покращення, але це добре.

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

Можна, звичайно, пограти ще трохи.


В середньому .477 секунд за пробіг протягом 15 пробіжок.
Барвисто монохромний

1

Lua 5.2 (або LuaJIT), 740 байт

Спочатку спробуйте, лише мінімально гольфуйте. Ця версія працює (принаймні, на тестовій програмі) та реалізує додаткові опкоди, але не підтримує неподписану математичну вимогу і не особливо швидка. Як бонус, однак, це VM, що працює у VM, і записаний таким чином, що він може бути інтерпретований (запуск з PUC-Lua) або свого роду JIT (запуск з LuaJIT; все ще інтерпретується, але інтерпретатор зараз JITted).

EDIT: Гольф краще, все ще великий.

EDIT: виправлена ​​основна помилка, а тепер обмежує арифметику unsigned longдіапазоном. Хоч якось вдалося утримати розмір, щоб не вийти з рук, але це все одно дає неправильну відповідь.

Редагувати: Виявляється, результат був правильним, але результат не був. Перехід до друку %uзамість, %dа все добре. Також вимкнено регістри на основі таблиць для змінних, щоб дещо поліпшити розмір та швидкість.

EDIT: Використовуючи gotoвислів Lua 5.2 (також доступний у LuaJIT), я замінив інтерпретатора на "JIT-to-Lua", генеруючи код, який безпосередньо управляє самим Lua VM. Не впевнений, чи справді це вважається JIT, але це покращує швидкість.

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

Ось оригінальна, читаемая версія.

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

Коли я запустив вашу програму, я отримав таку помилку: pastebin.com/qQBD7Rs8 . Чи очікуєте ви байт-код над stdin або як файл?
Барвисто монохромний

Вибачте. Мій двійковий файл для Windows був зіпсований. Таким чином, працювали всі версії gcc / linux, але всі тести Windows вийшли з ладу. Однак, як і раніше, повідомляється, що R0 і R1 дорівнюють 0, тоді як R2 - 1.
Барвисто монохромний

Я підозрюю, що це насправді не виконується: в середньому на роботу знадобилося 33,8 мс (GCC займає ~ .25 секунди).
Барвисто монохромний

Сценарій очікує байт-код у вигляді файлу, при цьому ім'я файлу передається в командному рядку. Ти маєш рацію, але я простежив це, і схоже, що це лише перша зовнішня петля. Назад до дошки для малювання ...
криптич стоїть з Монікою

Ось що я отримую для роздумів в С і написання в Луа: Я використовував <замість цього цикл<= , тому остаточну інструкцію з гілки залишили. Він як і раніше отримує неправильну відповідь, але зараз на це потрібно кілька хвилин. :)
Кріптих стоїть з Монікою

1

C #

1505 рік 1475 байт

Це моя версія перекладача, написана на C #, могла б бути оптимізована / гольф більше, я думаю, але я не знаю де;)

версія для гольфу:

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

редагувати

видалено зайві publicта privateмодифікатори:

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

зателефонуйте йому, executable.exe filenameде filenameзнаходиться файл, що містить код для інтерпретації

Моя "програма тестування":

# 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 

Інтерпретатор не має переваги з довшими змінними імен, класів, ...

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]);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.