最小バイトコードインタープリター/ VM


17

リーダーボード-JITコンパイル済み(低いほど良い)

  1. es1024-81.2ポイント(動作中のコンパイラを含む!)
  2. キース・ランドール-116ポイント
  3. Ell-121ポイント

リーダーボード-解釈済み(低いほど良い)

  1. マーティン・ブットナー-706654ポイント(約2時間)。
  2. クリプティック-30379ポイント(97秒)

あなたがそれを受け入れることを選択した場合、あなたの使命は、可能な限り最小のバイトコードインタープリター/ VMを書くことです。VM /インタープリターは、以下で指定されている言語で、小さなCISCアーキテクチャを使用します(操作のサイズは異なる場合があります)。完了したら、3つのCPUレジスタの値を印刷して、正しい出力が印刷されたことを証明する必要があります(3,126,900,366)。

コンパイラ

独自のテストを作成する場合は、コンパイラを以下に掲載します。回答とともにテストを投稿してください。

「VM」の仕様

VMには、3つの32ビット符号なし整数レジスターR0、R1、R2があります。これらは、0x00、0x01、および0x02として16進数で表されます。

次の操作をサポートする必要があります。

形式は[name] [... operands ...]、[hexadecimal op-code] [... operands repeat ...]です。

  • LOAD [レジスタ] [4バイト値]、0x00 [レジスタ] [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バイトのコード行番号]

いくつかのメモ:

  • 上記の数学演算は、2つのレジスタの値を加算し、出力を最初のレジスタに配置します。
  • 比較演算子であるCMPは、2つのレジスタの値を比較し、ブランチ命令での将来の使用に備えて、出力を内部フラグ(これは実装固有の場合があります)に格納する必要があります。
  • BRANCHがCMPの前に呼び出される場合、BRANCHEQが呼び出されない限り、「VM」は分岐しません。
  • PUSH / POPは、当然、スタックから数字をプッシュまたはポップします。
  • ジャンプ演算子と分岐演算子は、バイナリアドレスではなく、特定の操作(コード行)にジャンプします。
  • 分岐操作は比較を行いません。むしろ、最後の比較の出力を使用して実行します。
  • 分岐演算子とジャンプ演算子は、ゼロベースの行番号インデックスシステムを使用します。(たとえば、JMP 0は最初の行にジャンプします)
  • すべての操作は、ゼロにオーバーフローし、整数オーバーフローで例外をスローしない符号なしの数値で実行されます。
  • ゼロによる除算は許可されていないため、プログラムの動作は定義されていません。あなたは(たとえば)...
    • プログラムをクラッシュさせます。
    • VMの実行を終了し、現在の状態を返します。
    • 「ERR:0による除算」メッセージを表示します。
  • プログラムの終了は、命令ポインタがプログラムの最後に到達したときとして定義されます(空でないプログラムを想定できます)。

出力 出力は正確にこれである必要があります(改行を含む)

R0 3126900366
R1 0
R2 10000    

ポイント ポイントは、次の式に基づいて計算されます。Number Of Characters * (Seconds Needed To Run / 2)

異なる時間を引き起こすハードウェアの違いを回避するために、各テストは、ubuntuサーバーまたはWindows 8のいずれかのコンピューター(i5-4210u、8GB ram)で実行されます。 762.66 MBのRAMを搭載したMac Pro。

専用のランタイム/言語を使用している場合は、リンクを投稿してください。

テストプログラム

ここからアイデアが生まれたので、プログラムを多少変更したバージョンを使用します。

プログラムの正しい出力は、3,126,900,366です。

Cで:

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

バイナリ/ 16進数:

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

ボーナスポイント (効果は乗法的に適用されます)たとえば、3つすべての資格がある場合、((文字* 0.50)* 0.75)* 0.90

  • インタープリターが実際にJITコンパイラーである場合、50%減少
  • 何らかの種類のループ展開/意味のある最適化を適用すると、25%減少します。
  • VMを拡張すると10%減少
    • BRANCHEQ [コード行、4バイト](等しい場合は分岐-オペコード0x0b)
    • BRANCHGT [コード行、4バイト](より大きい場合は分岐-opcode 0x0c)
    • BRANCHNE [コード行、4バイト](等しくない場合は分岐-オペコード0x0d)
    • RLOAD [レジスタ1] [レジスタ2](レジスタ2の値をレジスタ1に移動-オペコード0x01)。

不許可

  • テストケースをプログラムにプリコンパイルすることは禁止されています。STDINまたはファイルからバイトコードを受け入れる必要があります(どちらでもかまいません)。
  • プログラムを実行せずに出力を返します。
  • VM要件をごまかすために考えられる他の方法。

禁止されているとあなたが言ったことを思いとどまらせるために、さらにいくつかのテストプログラムを含めてはどうですか?VMの場合、バイトコード仕様で書かれたものを実行できるはずですよね?
カズラン14年

今夜それをやろうとします。私は今、コンパイラを書いています。
カラフルなモノクロ14年

コンパイラはこちらjsfiddle.net/aL9y19bd/23
カラフルモノクロ

1
CMP以下かどうかをチェックしますか?そして、その結果はどうなりますか?
es1024 14年

1
MULそして、DIVもunderspecifiedされています。署名する必要がありますか?乗算オーバーフローはどうなりますか?
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レジスタには置き換えられますxor自体をレジスタにINGの代わりにmovINGの0は、生成されたバイトコードで3バイト短いレジスターへ非常にわずかに高速になる場合があります。

コンパイル:

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

POSIX準拠のOSが必要です。

入力は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にコンパイラーを含めることでボーナスを追加したいと思います。
カラフルなモノクロ14年

15回の実行で1回の実行あたり平均.67秒。
カラフルなモノクロ14年

Xoringが最適化であることには同意しません。コードサイズは賢明ですが、xoringはVMのパフォーマンス特性を変更しません(間違っている場合は修正してください)。私は最適化が意味することは、1つ等、除去することができるように、(例えば、冗長POP ... PUSHを除去する)または行負荷の2つの命令レジスタを実現する入力コードから命令を変更または削除された
カラフルなモノクロ

編集:実際には、それは最適化です:15回の実行で実行ごとに0.64秒に低下しました。コードを短くすることでキャッシュのスラッシングなどを防ぎます(または冗長なメモリアクセスを削除します)。
カラフルなモノクロ14年

@ColorfullyMonochrome一部のアーキテクチャーでは、レジスター自体へのxoringが提示されると、実際に命令を実行せず、単にレジスター自体をゼロにします。
es1024 14年

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 -wx86 POSIX準拠のOSでコンパイルします。
で実行します。./vm < programここprogramで、はバイナリプログラムファイルです。


スピードを求めて。このプログラムは、入力プログラムをx86マシンコードに非常に簡単に変換し、CPUに残りの処理をさせます。

たとえば、テストプログラムの翻訳は次のとおりです。 ecxesiおよびedi対応しR0R1そして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 ++で書かれて)...だった
カラフルなモノクロ

15回の実行で1回の実行あたり平均0.63秒。
カラフルなモノクロ14年

2

CJam、222 187(2 /遅すぎる)185バイト*

バイトコード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}/

実行するには、このsourceforgeリンクでJavaインタープリターをダウンロードし、コードを保存しvm.cjamて実行します

java -jar cjam-0.6.2.jar vm.cjam

プログラムは、STDINのバイトコードを予期します。PowerShellが末尾の改行を追加してに変換せずにバイナリデータをプログラムにパイプする方法をまだ見つけていません0x0a0x0d 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
20回の繰り返しの平均15.279秒。-15回のテスト。つまり、テストあたり2.12208333時間です。
カラフルなモノクロ14年

1

python / c ++、スコア= 56.66

1435文字* .234 / 2秒* .5 [JIT] * .75 [最適化] * .90 [追加の指示]

入力プログラムをc ++にコンパイルし、gccを実行して、結果を実行します。ほとんどの時間はgcc内で費やされます。

私が行う最適化の1つは、意味的に許可されている場合、スタック操作を明示的な変数に減らすことです。コンパイルされたコードの実行時間が約10倍向上します(結果のバイナリを実際に実行するのに約.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")

確かにもう少しゴルフすることができます。


15回の実行での実行ごとの平均.477秒。
カラフルなモノクロ14年

1

Lua 5.2(またはLuaJIT)、740バイト

最初に試すのは、最小限のゴルフのみです。このバージョンは(少なくともテストプログラムで)動作し、追加のオペコードを実装しますが、符号なしの数学要件を維持せず、特に高速ではありません。ただし、ボーナスとして、VMで実行されているVMであり、解釈(PUC-Luaで実行)またはsort-of-JIT(LuaJITで実行。まだ解釈されますが、インタープリターはJITted)。

編集:ゴルフは良く、まだ大きい。

編集:重大なエラーを修正し、算術をunsigned long範囲に制限するようになりました。どういうわけか、サイズが手に負えなくなるのを防ぐことができましたが、それでも間違った答えを与えています。

編集:結果は正しいが、出力は正しくなかった。の%u代わりに印刷に切り替え%d、すべてが順調です。また、変数のテーブルベースのレジスタを切り替えて、サイズ速度を多少改善しました。

編集: Lua 5.2のgotoステートメント(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を。バイトコードは標準入力よりもファイルとして期待していますか?
カラフルなモノクロ14年

ごめんなさい。Windows用のバイナリが破損していました。したがって、すべてのgcc / linuxバージョンは機能しましたが、Windowsテストはすべてクラッシュしました。しかし、まだR2が1の間、R0とR1は、0であることを報告している
カラフルなモノクロ

実際には実行されていないと思われます。平均して実行するのに33.8ミリ秒かかりました(GCCは〜.25秒かかります)。
カラフルなモノクロ14年

スクリプトは、ファイル名としてコマンドラインで渡されるバイトコードをファイルとして期待します。あなたは正しいですが、私はそれをトレースしましたが、最初の外側のループだけを実行しているようです。製図板に戻る...
criptychはモニカに立つ

これが、Cで考えてLuaで書くことで得られるものです。<ループの代わりにを使用した<=ため、最後の分岐命令は省略されました。それはまだ間違った答えを取得しますが、今それを行うには数分かかります。:)
クリプティクはモニカと14年

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

編集する

いくつかの不要な削除publicprivate修飾子を:

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]);
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.