リーダーボード-JITコンパイル済み(低いほど良い)
- es1024-81.2ポイント(動作中のコンパイラを含む!)
- キース・ランドール-116ポイント
- Ell-121ポイント
リーダーボード-解釈済み(低いほど良い)
- マーティン・ブットナー-706654ポイント(約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。
専用のランタイム/言語を使用している場合は、リンクを投稿してください。
- 利害関係者のために、テストコード(C#で記述)をここに投稿しました:http : //pastebin.com/WYCG5Uqu
テストプログラム
ここからアイデアが生まれたので、プログラムを多少変更したバージョンを使用します。
プログラムの正しい出力は、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要件をごまかすために考えられる他の方法。
CMP
以下かどうかをチェックしますか?そして、その結果はどうなりますか?
MUL
そして、DIV
もunderspecifiedされています。署名する必要がありますか?乗算オーバーフローはどうなりますか?