diff --git a/base/inference.jl b/base/inference.jl index 5479eb5ca91f9d..41f8a33ebddd0f 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -5217,6 +5217,23 @@ function occurs_outside_getfield(e::ANY, sym::ANY, if head === :(=) return occurs_outside_getfield(e.args[2], sym, sv, field_count, field_names) + elseif head === :foreigncall + args = e.args + nccallargs = args[5]::Int + # Only arguments escape, GC root arguments do not escape. + for i in 1:length(args) + a = args[i] + if i > 5 + nccallargs && symequal(a, sym) + ty = widenconst(exprtype(a, sv.src, sv.mod)) + if isleaftype(ty) && nfields(ty) <= field_count + continue + end + return true + end + if occurs_outside_getfield(a, sym, sv, field_count, field_names) + return true + end + end else if (head === :block && isa(sym, Slot) && sv.src.slotflags[slot_id(sym)] & Slot_UsedUndef == 0) @@ -5800,8 +5817,11 @@ end function replace_getfield!(e::Expr, tupname, vals, field_names, sv::InferenceState) for i = 1:length(e.args) a = e.args[i] - if isa(a,Expr) && is_known_call(a, getfield, sv.src, sv.mod) && - symequal(a.args[2],tupname) + if !isa(a, Expr) + continue + end + a = a::Expr + if is_known_call(a, getfield, sv.src, sv.mod) && symequal(a.args[2], tupname) idx = if isa(a.args[3], Int) a.args[3] else @@ -5830,8 +5850,22 @@ function replace_getfield!(e::Expr, tupname, vals, field_names, sv::InferenceSta end end e.args[i] = val - elseif isa(a, Expr) - replace_getfield!(a::Expr, tupname, vals, field_names, sv) + else + if a.head === :foreigncall + args = a.args + nccallargs = args[5]::Int + le = length(args) + next_i = 6 + nccallargs + while next_i <= le + i = next_i + next_i += 1 + + symequal(args[i], tupname) || continue + splice!(args, i, vals) + next_i += length(vals) - 1 + end + end + replace_getfield!(a, tupname, vals, field_names, sv) end end end diff --git a/test/codegen.jl b/test/codegen.jl index da80ff1661f039..96397a5651d3f0 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -128,10 +128,21 @@ function compare_large_struct(a) end end +mutable struct MutableStruct + a::Int + MutableStruct() = new() +end + +breakpoint_mutable(a::MutableStruct) = ccall(:jl_breakpoint, Void, (Ref{MutableStruct},), a) + if opt_level > 0 @test !contains(get_llvm(isequal, Tuple{Nullable{BigFloat}, Nullable{BigFloat}}), "%gcframe") @test !contains(get_llvm(pointer_not_safepoint, Tuple{}), "%gcframe") compare_large_struct_ir = get_llvm(compare_large_struct, Tuple{typeof(create_ref_struct())}) @test contains(compare_large_struct_ir, "call i32 @memcmp") @test !contains(compare_large_struct_ir, "%gcframe") + breakpoint_mutable_ir = get_llvm(breakpoint_mutable, Tuple{MutableStruct}) + @test !contains(breakpoint_mutable_ir, "%gcframe") + @test !contains(breakpoint_mutable_ir, "jl_gc_pool_alloc") + @test contains(get_llvm(MutableStruct, Tuple{}), "jl_gc_pool_alloc") end