我正在编写一个针对 x86_64 的 Lisp 编译器。我目前的目标是实现特殊形式lambda
。我最近发现您可以获取标签的地址,将其存储在寄存器中,然后从中调用它:
lea rax, [f + rip]
call rax
凉爽的!这有很大的潜力。但是我在做的测试中遇到了一个错误:
当我给我的编译器这个时,
(define f (lambda (x) (+ x 1)))
(display_num (f 2))
它产生了这个。
.global _main
.text
_main:
call _begin_gc
and rsp, -16
jmp after_lambda_1
lambda_1:
push rbp
mov rbp, rsp
push 1 # push argument to +
push [rbp + 16] # push argument to +
call plus
add rsp, 16 # discard 2 local arguments
mov rbp, rsp
pop rbp
ret
after_lambda_1:
lea rax, [lambda_1 + rip]
mov [f + rip], rax
push 2 # push argument to f
call f
add rsp, 8 # discard 1 local argument
push rax # result of f
call display_num
add rsp, 8 # discard 1 local argument
and rsp, -16
call _end_gc
xor rdi, rdi
mov rax, 0x2000001
syscall
.data
f:
.quad 0
起初这似乎没问题。运行时出现总线错误:
$ make run
./out/test
make: *** [run] Bus error: 10
嗯!诡异的。接下来我通过 LLDB 运行它:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x100008050)
* frame #0: 0x0000000100008050 test`f
frame #1: 0x0000000100003d9c test`after_lambda_1 + 21
frame #2: 0x0000000100003d67 test`main + 5
frame #3: 0x00007fff72c13cc9 libdyld.dylib`start + 1
frame #4: 0x00007fff72c13cc9 libdyld.dylib`start + 1
它说它在这里失败了,
test`f:
-> 0x100008050 <+0>: jo 0x10000808f ; gc + 55
0x100008052 <+2>: add byte ptr [rax], al
0x100008054 <+4>: add dword ptr [rax], eax
0x100008056 <+6>: add byte ptr [rax], al
但我在我的代码中没有看到这样的行。我完全糊涂了。有谁知道这里发生了什么,为什么我会遇到总线错误,以及我需要调整什么才能使我的代码正常工作?我正在使用 Clang 在 MacOS 上进行组装。