I frequently stumble about examples similar to the following, where I have a helper function which supports do
style anonymous functions
helper(cont, range) = for i ∈ range
cont(i)
end
and want to write something which uses it by for instance accumulating the results
function f()
a = 0
helper(1:4) do i
a += i
end
a
end
the generated code looks super complicated
julia> @code_llvm f()
julia> @code_llvm f()
; Function f
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:76
; Function Attrs: uwtable
define nonnull %jl_value_t addrspace(10)* @japi1_f_37345(%jl_value_t addrspace(10)*, %jl_value_t add
rspace(10)**, i32) #0 {
top:
%gcframe = alloca %jl_value_t addrspace(10)*, i32 4
%3 = bitcast %jl_value_t addrspace(10)** %gcframe to i8*
call void @llvm.memset.p0i8.i32(i8* %3, i8 0, i32 32, i32 0, i1 false)
%4 = alloca %jl_value_t addrspace(10)**, align 8
store volatile %jl_value_t addrspace(10)** %1, %jl_value_t addrspace(10)*** %4, align 8
%5 = call %jl_value_t*** inttoptr (i64 18155948864 to %jl_value_t*** ()*)() #6
; Function Type; {
; Location: boot.jl:328
%6 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %gcframe, i32 0
%7 = bitcast %jl_value_t addrspace(10)** %6 to i64*
store i64 4, i64* %7
%8 = getelementptr %jl_value_t**, %jl_value_t*** %5, i32 0
%9 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %gcframe, i32 1
%10 = bitcast %jl_value_t addrspace(10)** %9 to %jl_value_t***
%11 = load %jl_value_t**, %jl_value_t*** %8
store %jl_value_t** %11, %jl_value_t*** %10
%12 = bitcast %jl_value_t*** %8 to %jl_value_t addrspace(10)***
store %jl_value_t addrspace(10)** %gcframe, %jl_value_t addrspace(10)*** %12
%13 = bitcast %jl_value_t*** %5 to i8*
%14 = call noalias nonnull %jl_value_t addrspace(10)* @jl_gc_pool_alloc(i8* %13, i32 1488, i32 16)
#1
%15 = bitcast %jl_value_t addrspace(10)* %14 to %jl_value_t addrspace(10)* addrspace(10)*
%16 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(10)* %15, i64
-1
store %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 21670413472 to %jl_valu
e_t*) to %jl_value_t addrspace(10)*), %jl_value_t addrspace(10)* addrspace(10)* %16
%17 = addrspacecast %jl_value_t addrspace(10)* %14 to %jl_value_t addrspace(11)*
%18 = bitcast %jl_value_t addrspace(11)* %17 to %jl_value_t addrspace(10)* addrspace(11)*
;}
%19 = bitcast %jl_value_t addrspace(10)* %14 to %jl_value_t addrspace(10)* addrspace(10)*
store %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 70946288 to %jl_value_t
*) to %jl_value_t addrspace(10)*), %jl_value_t addrspace(10)* addrspace(10)* %19, align 8
%20 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %gcframe, i32 3
store %jl_value_t addrspace(10)* %14, %jl_value_t addrspace(10)** %20
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:77
%21 = call noalias nonnull %jl_value_t addrspace(10)* @jl_gc_pool_alloc(i8* %13, i32 1488, i32 16)
#1
%22 = bitcast %jl_value_t addrspace(10)* %21 to %jl_value_t addrspace(10)* addrspace(10)*
%23 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(10)* %22, i64
-1
store %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 135523168 to %jl_value_
t*) to %jl_value_t addrspace(10)*), %jl_value_t addrspace(10)* addrspace(10)* %23
%24 = bitcast %jl_value_t addrspace(10)* %21 to %jl_value_t addrspace(10)* addrspace(10)*
store %jl_value_t addrspace(10)* %14, %jl_value_t addrspace(10)* addrspace(10)* %24, align 8
%25 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %gcframe, i32 2
store %jl_value_t addrspace(10)* %21, %jl_value_t addrspace(10)** %25
; Function helper; {
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:62
%26 = call nonnull %jl_value_t addrspace(10)* @"julia_#31_37346"(%jl_value_t addrspace(10)* nonnull %21, i64 1)
%27 = call nonnull %jl_value_t addrspace(10)* @"julia_#31_37346"(%jl_value_t addrspace(10)* nonnull %21, i64 2)
%28 = call nonnull %jl_value_t addrspace(10)* @"julia_#31_37346"(%jl_value_t addrspace(10)* nonnull %21, i64 3)
%29 = call nonnull %jl_value_t addrspace(10)* @"julia_#31_37346"(%jl_value_t addrspace(10)* nonnull %21, i64 4)
;}
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:80
%30 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(11)* %18, align 8
%31 = icmp eq %jl_value_t addrspace(10)* %30, null
br i1 %31, label %err, label %pass
pass: ; preds = %top
%32 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %gcframe, i32 1
%33 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %32
%34 = getelementptr %jl_value_t**, %jl_value_t*** %5, i32 0
%35 = bitcast %jl_value_t*** %34 to %jl_value_t addrspace(10)**
store %jl_value_t addrspace(10)* %33, %jl_value_t addrspace(10)** %35
ret %jl_value_t addrspace(10)* %30
err: ; preds = %top
call void @jl_undefined_var_error(%jl_value_t addrspace(12)* addrspacecast (%jl_value_t* inttoptr (i64 70912160 to %jl_value_t*) to %jl_value_t addrspace(12)*))
unreachable
}
with the little help of Ref
, everything condenses nicely to a constant function
function g()
a = Ref(0)
helper(1:4) do i
a.x += i
end
a.x
end
gives
julia> @code_llvm g()
; Function g
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:67
; Function Attrs: uwtable
define i64 @julia_g_37350() #0 {
top:
; Location: D:\ProjectsJulia\Traits\test\tmp.jl:71
ret i64 10
}
What is the decisive difference here?
Can I adapt f()
so that it gains the same powerup as g()
?
extra: @code_warntype look similar
here the @code_warntype
- I cannot spot the decisive difference unfortunately, it looks astonishingly similar to me
julia> @code_warntype f()
Body::Any
76 1 ── %1 = %new(Core.Box)::Core.Box │╻ Type
│ (Core.setfield!)(%1, :contents, 0) │
77 │ %3 = %new(Main.:(##35#36), %1)::getfield(Main, Symbol("##35#36")) │
│ (Base.ifelse)(true, 4, 0) ││╻╷ Type
│ %5 = (Base.slt_int)(4, 1)::Bool ││╻╷╷╷ iterate
└─── goto #3 if not %5 │││
2 ── goto #4 │││
3 ── goto #4 │││
4 ┄─ %9 = φ (#2 => true, #3 => false)::Bool ││
│ %10 = φ (#3 => 1)::Int64 ││
│ %11 = φ (#3 => 1)::Int64 ││
│ %12 = (Base.not_int)(%9)::Bool ││
└─── goto #10 if not %12 ││
5 ┄─ %14 = φ (#4 => %10, #9 => %22)::Int64 ││
│ %15 = φ (#4 => %11, #9 => %23)::Int64 ││
│ invoke %3(%14::Int64) ││
│ %17 = (%15 === 4)::Bool │││╻ ==
└─── goto #7 if not %17 │││
6 ── goto #8 │││
7 ── %20 = (Base.add_int)(%15, 1)::Int64 │││╻ +
└─── goto #8 ││╻ iterate
8 ┄─ %22 = φ (#7 => %20)::Int64 ││
│ %23 = φ (#7 => %20)::Int64 ││
│ %24 = φ (#6 => true, #7 => false)::Bool ││
│ %25 = (Base.not_int)(%24)::Bool ││
└─── goto #10 if not %25 ││
9 ── goto #5 ││
10 ─ goto #11 ││
80 11 ─ %29 = (Core.isdefined)(%1, :contents)::Bool │
└─── goto #13 if not %29 │
12 ─ goto #14 │
13 ─ $(Expr(:throw_undef_if_not, :a, false)) │
14 ┄ %33 = (Core.getfield)(%1, :contents)::Any │
└─── return %33 │
julia> @code_warntype g()
Body::Int64
67 1 ── %1 = %new(Base.RefValue{Int64}, 0)::Base.RefValue{Int64} │╻╷╷ Type
68 │ (Base.ifelse)(true, 4, 0) │╻╷╷ Colon
│ %3 = (Base.slt_int)(4, 1)::Bool ││╻╷╷╷ iterate
└─── goto #3 if not %3 │││
2 ── goto #4 │││
3 ── goto #4 │││
4 ┄─ %7 = φ (#2 => true, #3 => false)::Bool ││
│ %8 = φ (#3 => 1)::Int64 ││
│ %9 = φ (#3 => 1)::Int64 ││
│ %10 = (Base.not_int)(%7)::Bool ││
└─── goto #10 if not %10 ││
5 ┄─ %12 = φ (#4 => %8, #9 => %22)::Int64 ││
│ %13 = φ (#4 => %9, #9 => %23)::Int64 ││
│ %14 = (Base.getfield)(%1, :x)::Int64 ││╻╷ #29
│ %15 = (Base.add_int)(%14, %12)::Int64 │││╻ +
│ (Base.setfield!)(%1, :x, %15) │││╻ setproperty!
│ %17 = (%13 === 4)::Bool │││╻ ==
└─── goto #7 if not %17 │││
6 ── goto #8 │││
7 ── %20 = (Base.add_int)(%13, 1)::Int64 │││╻ +
└─── goto #8 ││╻ iterate
8 ┄─ %22 = φ (#7 => %20)::Int64 ││
│ %23 = φ (#7 => %20)::Int64 ││
│ %24 = φ (#6 => true, #7 => false)::Bool ││
│ %25 = (Base.not_int)(%24)::Bool ││
└─── goto #10 if not %25 ││
9 ── goto #5 ││
10 ─ goto #11 ││
71 11 ─ %29 = (Base.getfield)(%1, :x)::Int64 │╻ getproperty
└─── return %29