Skip to content

Commit 0505c2e

Browse files
authored
[release-1.10] Avoid dropping call edges in presence of identical invoke edges (#57079)
Backport of #57077 to Julia 1.10
1 parent 213feae commit 0505c2e

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

src/staticdata_utils.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,8 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra
510510
// (invokeTypes, c) => invoke
511511
// (nullptr, invokeTypes) => missing call
512512
// (invokeTypes, nullptr) => missing invoke (unused--inferred as Any)
513-
void *target = ptrhash_get(&edges_map2, invokeTypes ? (void*)invokeTypes : (void*)callee);
513+
void *key = invokeTypes ? (void*)invokeTypes : (void*)callee;
514+
void *target = ptrhash_get(&edges_map2, key);
514515
if (target == HT_NOTFOUND) {
515516
size_t min_valid = 0;
516517
size_t max_valid = ~(size_t)0;
@@ -554,7 +555,7 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra
554555
jl_array_ptr_1d_push(ext_targets, callee);
555556
jl_array_ptr_1d_push(ext_targets, matches);
556557
target = (void*)((char*)HT_NOTFOUND + jl_array_len(ext_targets) / 3);
557-
ptrhash_put(&edges_map2, (void*)callee, target);
558+
ptrhash_put(&edges_map2, key, target);
558559
}
559560
idxs[++nt] = (char*)target - (char*)HT_NOTFOUND - 1;
560561
}

test/precompile.jl

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,17 @@ precompile_test_harness("invoke") do dir
10071007
f44320(::Any) = 2
10081008
g44320() = invoke(f44320, Tuple{Any}, 0)
10091009
g44320()
1010+
# Issue #57115
1011+
f57115(@nospecialize(::Any)) = error("unimplemented")
1012+
function g57115(@nospecialize(x))
1013+
if @noinline rand(Bool)
1014+
# Add an 'invoke' edge from 'foo' to 'bar'
1015+
Core.invoke(f57115, Tuple{Any}, x)
1016+
else
1017+
# ... and also an identical 'call' edge
1018+
@noinline f57115(x)
1019+
end
1020+
end
10101021
10111022
# Adding new specializations should not invalidate `invoke`s
10121023
function getlast(itr)
@@ -1023,6 +1034,8 @@ precompile_test_harness("invoke") do dir
10231034
"""
10241035
module $CallerModule
10251036
using $InvokeModule
1037+
import $InvokeModule: f57115, g57115
1038+
10261039
# involving external modules
10271040
callf(x) = f(x)
10281041
callg(x) = x < 5 ? g(x) : invoke(g, Tuple{Real}, x)
@@ -1043,6 +1056,8 @@ precompile_test_harness("invoke") do dir
10431056
10441057
# Issue #44320
10451058
f44320(::Real) = 3
1059+
# Issue #57115
1060+
f57115(::Int) = 1
10461061
10471062
call_getlast(x) = getlast(x)
10481063
@@ -1063,6 +1078,7 @@ precompile_test_harness("invoke") do dir
10631078
internalnc(3)
10641079
call_getlast([1,2,3])
10651080
end
1081+
precompile(g57115, (Any,))
10661082
10671083
# Now that we've precompiled, invalidate with a new method that overrides the `invoke` dispatch
10681084
$InvokeModule.h(x::Integer) = -1
@@ -1083,7 +1099,7 @@ precompile_test_harness("invoke") do dir
10831099
for m in methods(func)
10841100
m.sig.parameters[end] === T && return m
10851101
end
1086-
error("no ::Real method found for $func")
1102+
error("no ::$T method found for $func")
10871103
end
10881104
function nvalid(mi::Core.MethodInstance)
10891105
isdefined(mi, :cache) || return 0
@@ -1129,6 +1145,27 @@ precompile_test_harness("invoke") do dir
11291145
m = only(methods(M.g44320))
11301146
@test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt)
11311147

1148+
m = only(methods(M.g57115))
1149+
mi = m.specializations::Core.MethodInstance
1150+
1151+
f_m = get_method_for_type(M.f57115, Any)
1152+
f_mi = f_m.specializations::Core.MethodInstance
1153+
1154+
# Make sure that f57115(::Any) has a 'call' backedge to 'g57115'
1155+
has_f_call_backedge = false
1156+
it = Core.Compiler.BackedgeIterator(f_mi.backedges)
1157+
# Manually-written iterate(...) protocol, since this is in Core.Compiler
1158+
item = Core.Compiler.iterate(it)
1159+
while item !== nothing
1160+
(; sig, caller) = item[1]
1161+
if sig === nothing && caller === mi
1162+
has_f_call_backedge = true
1163+
break
1164+
end
1165+
item = Core.Compiler.iterate(it, item[2])
1166+
end
1167+
@test has_f_call_backedge
1168+
11321169
m = which(MI.getlast, (Any,))
11331170
@test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt)
11341171

0 commit comments

Comments
 (0)