我在workshop里看到这样一段内容:
Steps for Julia Parsing and Execution
- The AST(abstract syntax tree) after parsing ← Macros
- The AST after lowering (@code_typed)
- The AST after type inference and optimization ← Generated Functions (@code_lowered)
- The LLVM IR ← Functions (@code_llvm)
- 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
都是什么含义?或者您能告诉我在哪里可以看到说明吗?