Closed
Description
The following example generates bad code on recent Julia releases (including master):
function foo(args...)
Base.pointerset(args[1], 1, 1, 1)
return
end
code_llvm(foo, Tuple{Ptr{Int}, Type{Int}})
define void @julia_foo_11773(i64, %jl_value_t addrspace(10)* nonnull) #0 {
top:
%2 = alloca %jl_value_t addrspace(10)*, i32 3
%gcframe = alloca %jl_value_t addrspace(10)*, i32 3, align 16
...
%19 = call nonnull %jl_value_t addrspace(10)* @jl_f_tuple(%jl_value_t addrspace(10)* addrspacecast (%jl_value_t* null to %jl_value_t addrspace(10)*), %jl_value_t addrspace(10)** %2, i32 2)
...
%24 = call nonnull %jl_value_t addrspace(10)* @jl_f_getfield(%jl_value_t addrspace(10)* addrspacecast (%jl_value_t* null to %jl_value_t addrspace(10)*), %jl_value_t addrspace(10)** %2, i32 3)
...
%25 = bitcast %jl_value_t addrspace(10)* %24 to i64* addrspace(10)*
%26 = load i64*, i64* addrspace(10)* %25, align 8
store i64 1, i64* %26, align 1
...
ret void
}
Not passing a Type{Int}
(or any other non-concrete argument), or getting rid of the varargs, results in the expected clean code.
This doesn't look like an inference/specialization problem to me (i.e. not #34365):
julia> first(code_typed(foo, Tuple{Ptr{Int}, Type{Int}})[])
CodeInfo(
1 ─ %1 = Base.pointerset::Core.Compiler.Const(Core.Intrinsics.pointerset, false)
│ %2 = Base.getfield(args, 1, true)::Ptr{Int64}
│ (%1)(%2, 1, 1, 1)::Ptr{Int64}
└── return
)
julia> first(code_typed(foo, Tuple{Ptr{Int}, Type{Int}})[]).parent.specTypes
Tuple{typeof(foo),Ptr{Int64},Type{Int64}}
Instead, we get the tuple from the specsig but non-concrete varargs code path here:
Lines 6177 to 6202 in 0549bf1
This penalizes a lot of Cassette-based code, where splatting is used heavily for every function call (overdub(ctx, args...)
) and we get plenty calls to jl_f_tuple
and jl_f_getfield
as soon as passing a non-concrete argument (as observed in JuliaGPU/CUDAnative.jl#334 where these calls break compilation).