 Constant propagation depth

Is there a limit in the depth of a tree for constant propagation? I have seen that rather long expressions involving only constants are not completely replaced by their result:

julia> c() = log(log(log(log(2*π) + 2) + π) + tanh(3 + π))
c (generic function with 1 method)

julia> @code_llvm c()
;  @ REPL:1 within `c'
define double @julia_c_874() {
top:
%0 = call double @j_log_876(double 0x400EB3F8E4325F5A)
; ┌ @ promotion.jl:321 within `+' @ float.jl:326
%1 = fadd double %0, 0x400921FB54442D18
; └
%2 = call double @j_log_877(double %1)
; ┌ @ float.jl:326 within `+'
%3 = fadd double %2, 0x3FEFFFEC95BFEC1C
; └
%4 = call double @j_log_878(double %3)
ret double %4
}
1 Like

julia> c() = log(log(log(log(2*π) + 2) + π) + tanh(3 + π))
c (generic function with 1 method)

julia> @code_llvm c()
;  @ REPL:1 within `c`
define double @julia_c_181() #0 {
top:
ret double 0x3FED55BF3C312E04
}
3 Likes

Also not an answer, but with @code_warntype you can see nicely where i breaks down:

julia> c() = sin(sin(sin(sin(1)))); @code_warntype c()
MethodInstance for c()
from c() in Main at REPL:1
Arguments
#self#::Core.Const(c)
Body::Float64
1 ─ %1 = Main.sin(1)::Core.Const(0.8414709848078965)
│   %2 = Main.sin(%1)::Core.Const(0.7456241416655579)
│   %3 = Main.sin(%2)::Float64
│   %4 = Main.sin(%3)::Float64
└──      return %4
2 Likes

That’s a good suggestion, compare v1.6 vs v1.7:

# v1.6
julia> @code_warntype c()
Variables
#self#::Core.Const(c)

Body::Float64
1 ─ %1  = (2 * Main.π)::Core.Const(6.283185307179586)
│   %2  = Main.log(%1)::Float64
│   %3  = (%2 + 2)::Float64
│   %4  = Main.log(%3)::Float64
│   %5  = (%4 + Main.π)::Float64
│   %6  = Main.log(%5)::Float64
│   %7  = (3 + Main.π)::Core.Const(6.141592653589793)
│   %8  = Main.tanh(%7)::Core.Const(0.9999907421873817)
│   %9  = (%6 + %8)::Float64
│   %10 = Main.log(%9)::Float64
└──       return %10

# v1.7
julia> @code_warntype c()
MethodInstance for c()
from c() in Main at REPL:1
Arguments
#self#::Core.Const(c)
Body::Float64
1 ─ %1  = (2 * Main.π)::Core.Const(6.283185307179586)
│   %2  = Main.log(%1)::Core.Const(1.8378770664093453)
│   %3  = (%2 + 2)::Core.Const(3.8378770664093453)
│   %4  = Main.log(%3)::Core.Const(1.3449193664339925)
│   %5  = (%4 + Main.π)::Core.Const(4.486512020023786)
│   %6  = Main.log(%5)::Core.Const(1.5010755669062448)
│   %7  = (3 + Main.π)::Core.Const(6.141592653589793)
│   %8  = Main.tanh(%7)::Core.Const(0.9999907421873817)
│   %9  = (%6 + %8)::Core.Const(2.5010663090936265)
│   %10 = Main.log(%9)::Core.Const(0.9167171645762555)
└──       return %10
4 Likes

So presumably this is something present in Julia 1.6 and solved already (at least up to this depth) in Julia 1.7. Another good reason to upgrade then…

Yes, details are given here: Julia 1.7 Highlights

1 Like

For this case const propgate was blocked by sqrt:

c() = sin(sin(sin(sin(1)))); @code_typed c()
CodeInfo(
1 ─ %1 = Base.Math.sqrt_llvm(2.220446049250313e-16)::Float64
│   %2 = Base.lt_float(0.7456241416655579, %1)::Bool
└──      goto #3 if not %2
2 ─      goto #4
3 ─      goto #4
4 ┄ %6 = φ (#2 => 0.7456241416655579, #3 => 0.6784304773607402)::Float64
│   %7 = invoke Main.sin(%6::Float64)::Float64
└──      return %7
) => Float64

And looks like we disable it for correctness, see (julia/optimize.jl at 3a3cf028eb5e3138b0dfe5e31efce7f87f5cbfe3 · JuliaLang/julia · GitHub)

2 Likes