diff --git a/src/codegen/lowering.jl b/src/codegen/lowering.jl index f8fe0f9e..7fab4286 100644 --- a/src/codegen/lowering.jl +++ b/src/codegen/lowering.jl @@ -1065,7 +1065,16 @@ function determine_eltype(ls::LoopSet, ortypdefined::Bool)::Union{Symbol,Expr} push!(pt.args, Expr(:call, lv(:eltype), array)) end push_outer_reduct_types!(pt, ls, ortypdefined) - return pt + # Nest calls of the two-argument `promote_type` instead of calling it once with all + # types: on Julia >= 1.11, `promote_type` with many (>= 36) arguments is not inferred + # (`Base.afoldl` has `max_args = 34`), which makes the vector width and thus the whole + # generated loop type-unstable. `ls.includedactualarrays` has one entry per array + # reference, so loops with many references to only a few arrays are affected. + nested = pt.args[2] + for i in 3:length(pt.args) + nested = Expr(:call, lv(:promote_type), nested, pt.args[i]) + end + return nested end @inline _eltype(x) = eltype(x) @inline _eltype(::BitArray) = VectorizationBase.Bit diff --git a/test/manyarrayrefs.jl b/test/manyarrayrefs.jl index b4ba68d0..77c04cbd 100644 --- a/test/manyarrayrefs.jl +++ b/test/manyarrayrefs.jl @@ -21,3 +21,14 @@ end @test isapprox(sum_way_too_unrolled(A, Val(17), Val(16)), sum(A)) end + +@testset "Many Array References: type stability" begin + # The element type of the loop is determined by `promote_type` over all array + # references. With many references, a single `promote_type` call with many + # arguments is not inferred on Julia >= 1.11, which made the generated code + # type-unstable (dynamic dispatch and allocations in every iteration). + A = rand(6, 7, 10) + sum_way_too_unrolled(A, Val(6), Val(7)) # compile + @test isapprox(sum_way_too_unrolled(A, Val(6), Val(7)), sum(A)) + @test (@allocated sum_way_too_unrolled(A, Val(6), Val(7))) == 0 +end