如何在julia中进行常量传播?

如何在julia中进行常量传播?,julia,Julia,我希望像f(1,2)这样的调用可以直接编译为f2(1),而不需要分支,因为2是一个常量 @noinline f1(x::Int) = x + 1 @noinline f2(x::Int) = x + 2 @Base.pure function f(x::Int, p::Int) if p == 1 return f1(x) else return f2(x) end end 然而,从它生成的代码的外观来看,恒定传播并没有发生。有没有可能

我希望像
f(1,2)
这样的调用可以直接编译为
f2(1)
,而不需要分支,因为
2
是一个常量

@noinline f1(x::Int) = x + 1
@noinline f2(x::Int) = x + 2

@Base.pure function f(x::Int, p::Int)
    if p == 1
        return f1(x)
    else
        return f2(x)
    end
end

然而,从它生成的代码的外观来看,恒定传播并没有发生。有没有可能在现实生活中会发生常量传播,但诸如
@code\u native
@code\u warntype
之类的监控工具无法判断,因为它们不将
2
视为常量。

如果在带有常量参数的代码编译部分调用
f
,则会发生常量传播(例如,从函数调用)

因此,在您的情况下,您有:

@code_warntype f(1, 2)
Body::Int64
│╻ ==5 1 ─ %1 = (p === 1)::Bool
│   └──      goto #3 if not %1
│ 6 2 ─ %3 = invoke Main.f1(_2::Int64)::Int64
│   └──      return %3
│ 8 3 ─ %5 = invoke Main.f2(_2::Int64)::Int64
│   └──      return %5

@code_native f(1, 2)
    .text
; Function f {
; Location: In[1]:5
; Function ==; {
; Location: In[1]:5
    pushq   %rax
    cmpq    $1, %rsi
;}
    jne L21
; Location: In[1]:6
    movabsq $julia_f1_35810, %rax
    callq   *%rax
    popq    %rcx
    retq
; Location: In[1]:8
L21:
    movabsq $julia_f2_35811, %rax
    callq   *%rax
    popq    %rcx
    retq
    nopw    %cs:(%rax,%rax)
;}
作为旁注,AFAIK
@pure
宏不应与调用泛型函数的函数一起使用,如
f
的情况

编辑:我在这里发现了一个有趣的案例:

julia> @noinline f1(x::Int) = x + 1
f1 (generic function with 1 method)

julia> @noinline f2(x::Int) = x + 2
f2 (generic function with 1 method)

julia> function f(x::Int, p::Int)
           if p == 1
               return f1(x)
           else
               return f2(x)
           end
       end
f (generic function with 1 method)

julia> @code_warntype f(1,2)
Body::Int64
2 1 ─ %1 = (p === 1)::Bool                                                                                                                                                                     │╻ ==
  └──      goto #3 if not %1                                                                                                                                                                   │
3 2 ─ %3 = invoke Main.f1(_2::Int64)::Int64                                                                                                                                                    │
  └──      return %3                                                                                                                                                                           │
5 3 ─ %5 = invoke Main.f2(_2::Int64)::Int64                                                                                                                                                    │
  └──      return %5                                                                                                                                                                           │

julia> g() = f(1,2)
g (generic function with 1 method)

julia> @code_warntype g()
Body::Int64
1 1 ─     return 3

julia> h(x) = f(x,2)
h (generic function with 1 method)

julia> @code_warntype h(10)
Body::Int64
1 1 ─ %1 = invoke Main.f2(_2::Int64)::Int64                                                                                                                                                     │╻ f
  └──      return %1
有趣的是,对于
g
而言,常量传播如上所述,但对于
h
而言,不是这样,但是如果将
h
包装到函数中,它会再次发生

因此,一般来说,结论可能是,在编译代码的标准情况下,可以预期会发生不断的传播,但在复杂情况下,编译器可能不够聪明(当然,这在将来可能会有所改进)

julia> f(x,p) = (p==1 ? sin : cos)(x)
f (generic function with 1 method)

julia> @code_warntype f(10, 2)
Body::Any
1 1 ─ %1 = (p === 1)::Bool                                                                                                                                                                     │╻ ==
  └──      goto #3 if not %1                                                                                                                                                                   │
  2 ─ %3 = Main.sin::Core.Compiler.Const(sin, false)                                                                                                                                           │
  └──      goto #4                                                                                                                                                                             │
  3 ─ %5 = Main.cos::Core.Compiler.Const(cos, false)                                                                                                                                           │
  4 ┄ %6 = φ (#2 => %3, #3 => %5)::Union{typeof(cos), typeof(sin)}                                                                                                                             │
  │   %7 = (%6)(x)::Any                                                                                                                                                                        │
  └──      return %7                                                                                                                                                                           │

julia> g() = f(10, 2)
g (generic function with 1 method)

julia> @code_warntype g()
Body::Float64
1 1 ─ %1 = invoke Base.Math.cos(10.0::Float64)::Float64                                                                                                                                        │╻╷ f
  └──      return %1                                                                                                                                                                           │

julia> h(x) = f(x, 2)
h (generic function with 1 method)

julia> @code_warntype h(10)
Body::Any
1 1 ─ %1 = invoke Main.f(_2::Int64, 2::Int64)::Any                                                                                                                                                 │
  └──      return %1

julia> z() = h(10)
z (generic function with 1 method)

julia> @code_warntype z()
Body::Float64
1 1 ─ %1 = invoke Base.Math.cos(10.0::Float64)::Float64                                                                                                                                       │╻╷╷ h
  └──      return %1