用Rust和Golang写一个Brainfuck JIT
看老哥的视频真是太爽了,OnlyFans,将所学的知识打通,找到应用的地方。
BrainF*ck JIT
I made JIT Compiler for Brainf*ck lol - YouTube
Brain F**k in 100 Seconds | Prime Reacts: - YouTube
flat assembler
# Byte Positions Are Better Than Line Numbers
ChromiumOS - Linux Syscall Table -
Backpatching in Compiler Design - GeeksforGeeks
[[Golang怎么通过mmap执行汇编代码]]
网络字节序都是用的大端,x86_64 机器码用的小端
stdin(fd:0), stdout(fd:1)
搞汇编想起了之前学的一些关键知识点,寄存器的 caller, callee,哪些用于参数传递,返回值。
再用 rust 写的时候,有个有意思的 bug。
一开始我初始化 memory 时,是这样的。
写的时候完全没有想到这个地方,所以先看下调试过程
用 rust-gdb
```sh
(gdb) p memory
$1 = Vec(size=10000000) = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0...}
(gdb) p &memory
$2 = (*mut alloc::vec::Vec<u8, alloc::alloc::Global>) 0x7fffffffd350
(gdb) layout asm
(gdb) si
0x00007ffff7ffa000 in ?? ()
(gdb) info registers rdi
rdi 0x7fffffffd350 140737488343888
(gdb) x/10xb $rdi
0x7fffffffd350: 0x80 0x96 0x98 0x00 0x00 0x00 0x00 0x00
0x7fffffffd358: 0x10 0x60
(gdb) si
0x00007ffff7ffa003 in ?? ()
(gdb) x/10xb $rdi
0x7fffffffd350: 0x81 0x96 0x98 0x00 0x00 0x00 0x00 0x00
0x7fffffffd358: 0x10 0x60
为什么在原生的数据表示形式不全是初始化的 0?因为用的是 vec(动态数组)想想都应该知道底层有个 len, capacity,items,更不用说在 rust 中是怎么样的。
然后又写出了这种,let mut memory: [u8; JIT_MEMORY_CAP] = [0; JIT_MEMORY_CAP];
这是在 stack 上分配的,空间不会很大。
- Linux 和 macOS: 默认的栈大小通常为 2 MiB。
- Windows: 默认的栈大小通常为 1 MiB。
这里分配了~=10MB
然后使用 box 将分配到 heap 上的写法。
let mut memory: Box<[u8; JIT_MEMORY_CAP]> = Box::new([0; JIT_MEMORY_CAP]);
会报 stack overflowed,问问 chatgpt 是咋回事。
正确的写法
ChatGPT:
- 使用 `vec![0; JIT_MEMORY_CAP].into_boxed_slice()` 代替 `Box::new([0; JIT_MEMORY_CAP])`。这样可以确保在堆上分配内存,并且避免编译器在栈上分配大量临时空间的问题。
- 使用 `Box<[u8]>` 而不是 `Box<[u8; JIT_MEMORY_CAP]>`,这样可以避免固定大小数组在编译时带来的栈分配问题。