julia 执行顺序

我在workshop里看到这样一段内容:

Steps for Julia Parsing and Execution

  1. The AST(abstract syntax tree) after parsing ← Macros
  2. The AST after lowering (@code_typed)
  3. The AST after type inference and optimization ← Generated Functions (@code_lowered)
  4. The LLVM IR ← Functions (@code_llvm)
  5. The assembly code (@code_native)

感觉似懂非懂,又感觉对于理解julia语言特性很重要,请问有没有更加具体一些的解释呢?

这几个都是真实存在的宏,你可以用简单的函数,执行试试。

先用一用找找感觉。

julia> f(a::Int64, b::Int64) = a + b
f (generic function with 1 method)

julia> dump(quote f(a::Int64, b::Int64) = a + b end)
Expr
  head: Symbol block
  args: Array{Any}((2,))
    1: LineNumberNode
      line: Int64 1
      file: Symbol REPL[2]
    2: Expr
      head: Symbol =
      args: Array{Any}((2,))
        1: Expr
          head: Symbol call
          args: Array{Any}((3,))
            1: Symbol f
            2: Expr
              head: Symbol ::
              args: Array{Any}((2,))
                1: Symbol a
                2: Symbol Int64
            3: Expr
              head: Symbol ::
              args: Array{Any}((2,))
                1: Symbol b
                2: Symbol Int64
        2: Expr
          head: Symbol block
          args: Array{Any}((2,))
            1: LineNumberNode
              line: Int64 1
              file: Symbol REPL[2]
            2: Expr
              head: Symbol call
              args: Array{Any}((3,))
                1: Symbol +
                2: Symbol a
                3: Symbol b

julia> @code_typed f(1, 2)
CodeInfo(
1 ─ %1 = Base.add_int(a, b)::Int64
└──      return %1
) => Int64

julia> @code_lowered f(1, 2)
CodeInfo(
1 ─ %1 = a + b
└──      return %1
)

julia> @code_llvm f(1, 2)

;  @ REPL[1]:1 within `f'
; Function Attrs: uwtable
define i64 @julia_f_17467(i64, i64) #0 {
top:
; ┌ @ int.jl:53 within `+'
   %2 = add i64 %1, %0
; └
  ret i64 %2
}

julia> @code_native f(1, 2)
        .text
; ┌ @ REPL[1]:1 within `f'
        pushq   %rbp
        movq    %rsp, %rbp
; │┌ @ int.jl:53 within `+'
        leaq    (%rcx,%rdx), %rax
; │└
        popq    %rbp
        retq
        nopw    (%rax,%rax)
; └

文档就看开发者文档

code_lowered 可以指定代码底层中的方法。 并且可以用 code_typed 来进行类型推断。 code_warntype 增加 code_typed 输出的高亮。

更加接近于机器, 一个函数的 LLVM-IR 可以通过使用 code_llvm 打印出。 最终编译的机器码使用 code_native 查看(这将触发 之前未调用过的任何函数的 JIT 编译/代码生成)。

写写宏再 AST 的层面上就行了。再继续向下,一般就 code_warntype 常用一点,用来检查代码是否类型稳定。其他的感觉用的较少,也就是调试时看一看有没有可以优化的点,真的要深入就去看代码了。

谢谢回复!很有帮助!
说到类型稳定,我有个小问题。
@code_warntype 2^-5 表明结果应该是个Int64;但是实际上2^-5=0.03125得到的是个Float64,这是怎么回事呢。

这个应该是 bug,看源码整数的负数次幂应该报错的。

相关的 issue

目前的 workaround,先定义一个函数就好了

julia> f() = 2^-5
f (generic function with 1 method)

julia> @code_lowered f()
CodeInfo(
1 ─ %1 = Core.apply_type(Base.Val, -5)
│   %2 = (%1)()
│   %3 = Base.literal_pow(Main.:^, 2, %2)
└──      return %3
)

julia> @code_warntype f()
Variables
  #self#::Core.Compiler.Const(f, false)

Body::Float64
1 ─ %1 = Core.apply_type(Base.Val, -5)::Core.Compiler.Const(Val{-5}, false)
│   %2 = (%1)()::Core.Compiler.Const(Val{-5}(), false)
│   %3 = Base.literal_pow(Main.:^, 2, %2)::Float64
└──      return %3

嗯嗯,谢谢!
另外,code_warntype 的返回结果应该怎么读呢?顶角的 1 还有 %1 都是什么含义?或者您能告诉我在哪里可以看到说明吗?