那我们上 godbolt 看看 .NET 给我们的 Brainfuck 程序产生了怎样的机器代码Copypush rbp push r15 push r14 push r13 push rbx lea rbp, [rsp0x20] mov rbx, rsi mov r15, r8 movsxd rsi, edi add rsi, rbx add byte ptr [rsi], 49 ; 1 inc edi movsxd rsi, edi add rsi, rbx add byte ptr [rsi], 50 ; 2 inc edi movsxd rsi, edi add rsi, rbx add byte ptr [rsi], 51 ; 3 lea r14d, [rdi-0x02] movsxd rsi, r14d movzx rsi, byte ptr [rbxrsi] mov rdi, r15 mov rax, qword ptr [r15] mov r13, qword ptr [rax0x68] call [r13]System.IO.Stream:WriteByte(ubyte):this inc r14d movsxd rsi, r14d movzx rsi, byte ptr [rbxrsi] mov rdi, r15 call [r13]System.IO.Stream:WriteByte(ubyte):this inc r14d movsxd rsi, r14d movzx rsi, byte ptr [rbxrsi] mov rdi, r15 call [r13]System.IO.Stream:WriteByte(ubyte):this mov eax, r14d pop rbx pop r13 pop r14 pop r15 pop rbp ret这不就是Copy*(ptr) 1; *(ptr) 2; *ptr 3; ptr - 2; WriteByte(*(ptr)); WriteByte(*(ptr)); WriteByte(*ptr);吗可以看到我们代码里的抽象全都被 .NET 给优化干净了。而前面那个不怎么直观的 Hello World! 代码则编译出CopyAddData8, Loop AddPointer1, AddData4, Loop AddPointer1, AddData2, AddPointer1, AddData3, AddPointer1, AddData3, AddPointer1, AddData1, AddPointer-4, AddData-1, Stop, AddPointer1, AddData1, AddPointer1, AddData1, AddPointer1, AddData-1, AddPointer2, AddData1, LoopAddPointer-1, Stop, AddPointer-1, AddData-1, Stop , AddPointer2, OutputDataAddPointer1, AddData-3, OutputDataAddData7, OutputDataOutputDataAddData3, OutputDataAddPointer2, OutputDataAddPointer-1, AddData-1, OutputDataAddPointer-1, OutputDataAddData3, OutputDataAddData-6, OutputDataAddData-8, OutputDataAddPointer2, AddData1, OutputDataAddPointer1, AddData2, OutputDataStopJIT 编译#如果我们想以 JIT 的形式运行 Brainfuck 代码那如何在运行时生成类型然后运行代码呢我们在 .NET 中有完善的反射支持因此完全可以做到运行时创建类型。比如根据数字来生成数字类型Copyvar type GetNum(42); static Type GetHex(int hex) { return hex switch { 0 typeof(Hex0), 1 typeof(Hex1), 2 typeof(Hex2), 3 typeof(Hex3), 4 typeof(Hex4), 5 typeof(Hex5), 6 typeof(Hex6), 7 typeof(Hex7), 8 typeof(Hex8), 9 typeof(Hex9), 10 typeof(HexA), 11 typeof(HexB), 12 typeof(HexC), 13 typeof(HexD), 14 typeof(HexE), 15 typeof(HexF), _ throw new ArgumentOutOfRangeException(nameof(hex)), }; } static Type GetNum(int num) { var hex0 num 0xF; var hex1 (num 4) 0xF; var hex2 (num 8) 0xF; var hex3 (num 12) 0xF; var hex4 (num 16) 0xF; var hex5 (num 20) 0xF; var hex6 (num 24) 0xF; var hex7 (num 28) 0xF; return typeof(Int,,,,,,,).MakeGenericType(GetHex(hex7), GetHex(hex6), GetHex(hex5), GetHex(hex4), GetHex(hex3), GetHex(hex2), GetHex(hex1), GetHex(hex0)); }同理也可以用于生成各种程序结构上。最后我们只需要对构建好的类型进行反射然后调用Run方法即可Copyvar run (EntryPoint)Delegate.CreateDelegate(typeof(EntryPoint), type.GetMethod(Run)!); run(0, memory, input, output); delegate int EntryPoint(int address, Spanbyte memory, Stream input, Stream output);AOT 编译#那如果我不想 JIT而是想 AOT 编译出来一个可执行文件呢你会发现因为编译出的东西是类型因此我们不仅可以在 JIT 环境下跑还能直接把类型当作程序 AOT 编译出可执行文件只需要编写一个入口点方法调用Run即可Copyusing HelloWorld AddData8, Loop AddPointer1, AddData4, Loop AddPointer1, AddData2, AddPointer1, AddData3, AddPointer1, AddData3, AddPointer1, AddData1, AddPointer-4, AddData-1, Stop, AddPointer1, AddData1, AddPointer1, AddData1, AddPointer1, AddData-1, AddPointer2, AddData1, LoopAddPointer-1, Stop, AddPointer-1, AddData-1, Stop , AddPointer2, OutputDataAddPointer1, AddData-3, OutputDataAddData7, OutputDataOutputDataAddData3, OutputDataAddPointer2, OutputDataAddPointer-1, AddData-1, OutputDataAddPointer-1, OutputDataAddData3, OutputDataAddData-6, OutputDataAddData-8, OutputDataAddPointer2, AddData1, OutputDataAddPointer1, AddData2, OutputDataStop; static void Main() { HelloWorld.Run(0, stackalloc byte[16], Console.OpenStandardInput(), Console.OpenStandardOutput()); }然后调用 AOT 编译Copydotnet publish -c Release -r linux-x64 /p:PublishAottrue /p:IlcInstructionSetnative /p:OptimizationPreferenceSpeed上面的/p:IlcInstructionSetnative即 C 世界里的-marchnativeOptimizationPreferenceSpeed则是-O2。运行编译后的程序就能直接输出Hello World!。性能测试#这里我们采用一段用 Brainfuck 编写的 Mandelbrot 程序进行性能测试代码见 Pastebin。它运行之后会在屏幕上输出这段程序编译出来的类型也是非常的壮观