答案是肯定的。我已经简化了您的示例,以使编译器输出更易于理解:
let rec is_prime n =
let rec div_check i =
i * i > n || (i + i < n && div_check (i+1)) in
div_check n
鉴于此输入,编译器将发出以下程序集(我添加了一些注释):
camlIs_prime__div_check_1010:
.L102:
movq 16(%rbx), %rdi
movq %rax, %rsi
sarq $1, %rsi
movq %rax, %rdx
decq %rdx
imulq %rsi, %rdx ; i * i
incq %rdx
cmpq %rdi, %rdx ; if i * i <= n then return
jle .L101
movq $3, %rax
ret
.L101:
movq 16(%rbx), %rdi
leaq -1(%rax, %rax), %rsi ; i + i
cmpq %rdi, %rsi ; i + i ? n
jge .L100 ; if i + i >= n then return
addq $2, %rax ; else i := i + 1
jmp .L102 ; div_check i
.L100:
movq $1, %rax
ret
但要小心,这仅适用于普通 OCaml 的短路运算符。
即使是let (&&) = (&&) 将break 和jmp .L102 这样的无害更改也将替换为call camlIs_prime__div_check_...。
此外,这仅在调用位于短路运算符右侧时才有效。左表达式为 marked 非尾递归。我已经对其进行了测试,确实,将div_check 放在&& 运算符的左侧将发出非尾递归代码。
如果您不确定调用是否为尾,您可以随时在编译器中添加-annot 标志,并查看对应的*.annot 文件,以获取call (tail) 注释。 merlin 中有一些工具支持,但我还没有弄清楚如何正确使用它。请记住,最终的判断仍然是汇编输出。