Skip to content

Commit 3836a39

Browse files
committed
implement Method edge encoding
This allows us to distinguish between edges from failed `abstract_call_method` that don’t need to be recorded and those from `abstract_applicable` and `_hasmethod_tfunc` that do need to be recorded as backedges.
1 parent 3cf3252 commit 3836a39

File tree

3 files changed

+89
-55
lines changed

3 files changed

+89
-55
lines changed

base/compiler/stmtinfo.jl

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ struct MethodMatchInfo <: CallInfo
4444
return new(results, mt, atype, fullmatch, edges)
4545
end
4646
end
47-
function add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo)
47+
add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo) = _add_edges_impl(edges, info)
48+
function _add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, mi_edge::Bool=false)
4849
if !fully_covering(info)
4950
# add legacy-style missing backedge info also
5051
exists = false
@@ -66,7 +67,7 @@ function add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo)
6667
edge = info.edges[1]
6768
m = info.results[1]
6869
if edge === nothing
69-
mi = specialize_method(m)
70+
mi = specialize_method(m) # don't allow `Method`-edge for this optimized format
7071
edge = mi
7172
else
7273
mi = edge.def
@@ -88,13 +89,11 @@ function add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo)
8889
edge = info.edges[i]
8990
m = info.results[i]
9091
if edge === nothing
91-
# push!(edges, m.method)
92-
mi = specialize_method(m)
93-
push!(edges, mi)
92+
edge = mi_edge ? specialize_method(m) : m.method
9493
else
9594
@assert edge.def.def === m.method
96-
push!(edges, edge)
9795
end
96+
push!(edges, edge)
9897
end
9998
nothing
10099
end
@@ -112,11 +111,11 @@ function add_one_edge!(edges::Vector{Any}, edge::MethodInstance)
112111
end
113112
function add_one_edge!(edges::Vector{Any}, edge::CodeInstance)
114113
for i in 1:length(edges)
115-
edgeᵢ = edges[i]
114+
edgeᵢ_orig = edgeᵢ = edges[i]
116115
edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def)
117116
edgeᵢ isa MethodInstance || continue
118117
if edgeᵢ === edge.def && !(i > 1 && edges[i-1] isa Type)
119-
if edges[i] isa MethodInstance
118+
if edgeᵢ_orig isa MethodInstance
120119
# found edge we can upgrade
121120
edges[i] = edge
122121
return
@@ -149,7 +148,9 @@ struct UnionSplitInfo <: CallInfo
149148
split::Vector{MethodMatchInfo}
150149
end
151150
add_edges_impl(edges::Vector{Any}, info::UnionSplitInfo) =
152-
for split in info.split; add_edges!(edges, split); end
151+
_add_edges_impl(edges, info)
152+
_add_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, mi_edge::Bool=false) =
153+
for split in info.split; _add_edges_impl(edges, split, mi_edge); end
153154
nsplit_impl(info::UnionSplitInfo) = length(info.split)
154155
getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit(info.split[idx], 1)
155156
getresult_impl(::UnionSplitInfo, ::Int) = nothing
@@ -294,21 +295,21 @@ struct InvokeCallInfo <: CallInfo
294295
result::Union{Nothing,ConstResult}
295296
atype # ::Type
296297
end
297-
function add_edges_impl(edges::Vector{Any}, info::InvokeCallInfo)
298+
add_edges_impl(edges::Vector{Any}, info::InvokeCallInfo) =
299+
_add_edges_impl(edges, info)
300+
function _add_edges_impl(edges::Vector{Any}, info::InvokeCallInfo, mi_edge::Bool=false)
298301
edge = info.edge
299302
if edge === nothing
300-
mi = specialize_method(info.match)
301-
add_invoke_edge!(edges, info.atype, mi)
302-
else
303-
add_invoke_edge!(edges, info.atype, edge)
303+
edge = mi_edge ? specialize_method(info.match) : info.match.method
304304
end
305+
add_invoke_edge!(edges, info.atype, edge)
305306
nothing
306307
end
307-
function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::MethodInstance)
308+
function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::Union{MethodInstance,Method})
308309
for i in 2:length(edges)
309310
edgeᵢ = edges[i]
310311
edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def)
311-
edgeᵢ isa MethodInstance || continue
312+
edgeᵢ isa MethodInstance || edgeᵢ isa Method || continue
312313
if edgeᵢ === edge
313314
edge_minus_1 = edges[i-1]
314315
if edge_minus_1 isa Type && edge_minus_1 == atype
@@ -321,13 +322,13 @@ function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::Method
321322
end
322323
function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::CodeInstance)
323324
for i in 2:length(edges)
324-
edgeᵢ = edges[i]
325+
edgeᵢ_orig = edgeᵢ = edges[i]
325326
edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def)
326-
edgeᵢ isa MethodInstance || continue
327-
if edgeᵢ === edge.def
327+
if ((edgeᵢ isa MethodInstance && edgeᵢ === edge.def) ||
328+
(edgeᵢ isa Method && edgeᵢ === edge.def.def))
328329
edge_minus_1 = edges[i-1]
329330
if edge_minus_1 isa Type && edge_minus_1 == atype
330-
if edges[i] isa MethodInstance
331+
if edgeᵢ_orig isa MethodInstance || edgeᵢ_orig isa Method
331332
# found edge we can upgrade
332333
edges[i] = edge
333334
return
@@ -426,8 +427,9 @@ end
426427
add_edges_impl(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.info)
427428

428429
struct VirtualMethodMatchInfo <: CallInfo
429-
info::CallInfo
430+
info::Union{MethodMatchInfo,UnionSplitInfo,InvokeCallInfo}
430431
end
431-
add_edges_impl(edges::Vector{Any}, info::VirtualMethodMatchInfo) = add_edges!(edges, info.info)
432+
add_edges_impl(edges::Vector{Any}, info::VirtualMethodMatchInfo) =
433+
_add_edges_impl(edges, info.info, #=mi_edge=#true)
432434

433435
@specialize

base/compiler/utilities.jl

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -263,21 +263,32 @@ function iterate(iter::BackedgeIterator, i::Int=1)
263263
while true
264264
i > length(backedges) && return nothing
265265
item = backedges[i]
266-
item isa Int && (i += 2; continue) # ignore the query information if present
267-
# TODO: These `MethodInstance`s can be categorized into two types:
268-
# those that should be recorded as forward edges but are unnecessary as backedges
269-
# (such as cases where `abstract_call_method` fails), and
270-
# those that should be recorded as backedges, such as those provided by
271-
# `abstract_applicable` or user-provided edges.
272-
# Ideally, we should avoid recording the former cases as backedges and instead
273-
# prepare a special token to represent them.
274-
# isa(item, MethodInstance) && (i += 1; continue) # ignore edges which don't contribute info (future style edges)
275-
isa(item, CodeInstance) && (item = item.def)
276-
isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch
277-
isa(item, MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch (legacy style edges)
278-
target = backedges[i+1]
279-
isa(target, CodeInstance) && (target = target.def)
280-
return BackedgePair(item, target::MethodInstance), i+2 # `invoke` calls
266+
if item isa Int
267+
i += 2
268+
continue # ignore the query information if present
269+
elseif isa(item, Method)
270+
# ignore `Method`-edges (from e.g. failed `abstract_call_method`)
271+
i += 1
272+
continue
273+
end
274+
if isa(item, CodeInstance)
275+
item = item.def
276+
end
277+
if isa(item, MethodInstance) # regular dispatch
278+
return BackedgePair(nothing, item), i+1
279+
elseif isa(item, MethodTable) # abstract dispatch (legacy style edges)
280+
return BackedgePair(backedges[i+1], item), i+2
281+
else # `invoke` call
282+
callee = backedges[i+1]
283+
if isa(callee, Method)
284+
i += 2
285+
continue
286+
end
287+
if isa(callee, CodeInstance)
288+
callee = callee.def
289+
end
290+
return BackedgePair(item, callee::MethodInstance), i+2
291+
end
281292
end
282293
end
283294

src/staticdata_utils.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -750,20 +750,19 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key)
750750
}
751751
}
752752

753-
static size_t verify_invokesig(jl_value_t *invokesig, jl_method_instance_t *expected, size_t minworld)
753+
static size_t verify_invokesig(jl_value_t *invokesig, jl_method_t *expected, size_t minworld)
754754
{
755755
assert(jl_is_type(invokesig));
756-
assert(jl_is_method_instance(expected));
757-
jl_method_t *m = ((jl_method_instance_t*)expected)->def.method;
756+
assert(jl_is_method(expected));
758757
size_t min_valid = 0;
759758
size_t max_valid = ~(size_t)0;
760-
if (jl_egal(invokesig, m->sig)) {
761-
// the invoke match is `m` for `m->sig`, unless `m` is invalid
762-
if (jl_atomic_load_relaxed(&m->deleted_world) < max_valid)
759+
if (jl_egal(invokesig, expected->sig)) {
760+
// the invoke match is `expected` for `expected->sig`, unless `expected` is invalid
761+
if (jl_atomic_load_relaxed(&expected->deleted_world) < max_valid)
763762
max_valid = 0;
764763
}
765764
else {
766-
jl_methtable_t *mt = jl_method_get_table(m);
765+
jl_methtable_t *mt = jl_method_get_table(expected);
767766
if ((jl_value_t*)mt == jl_nothing) {
768767
max_valid = 0;
769768
}
@@ -773,7 +772,7 @@ static size_t verify_invokesig(jl_value_t *invokesig, jl_method_instance_t *expe
773772
max_valid = 0;
774773
}
775774
else {
776-
if (((jl_method_match_t*)matches)->method != m) {
775+
if (((jl_method_match_t*)matches)->method != expected) {
777776
max_valid = 0;
778777
}
779778
}
@@ -808,8 +807,14 @@ static size_t verify_call(jl_value_t *sig, jl_svec_t *expecteds, size_t i, size_
808807
jl_value_t *t = jl_svecref(expecteds, j + i);
809808
if (jl_is_code_instance(t))
810809
t = (jl_value_t*)((jl_code_instance_t*)t)->def;
811-
assert(jl_is_method_instance(t));
812-
if (match == ((jl_method_instance_t*)t)->def.method)
810+
jl_method_t *meth;
811+
if (jl_is_method(t))
812+
meth = (jl_method_t*)t;
813+
else {
814+
assert(jl_is_method_instance(t));
815+
meth = ((jl_method_instance_t*)t)->def.method;
816+
}
817+
if (match == meth)
813818
break;
814819
}
815820
if (j == n) {
@@ -853,6 +858,7 @@ static size_t jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, ar
853858
for (size_t j = 0; j < jl_svec_len(callees); ) {
854859
jl_value_t *edge = jl_svecref(callees, j);
855860
size_t max_valid2;
861+
assert(!jl_is_method(edge)); // `Method`-edge isn't allowed for the optimized one-edge format
856862
if (jl_is_code_instance(edge))
857863
edge = (jl_value_t*)((jl_code_instance_t*)edge)->def;
858864
if (jl_is_method_instance(edge)) {
@@ -875,10 +881,17 @@ static size_t jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, ar
875881
continue;
876882
}
877883
else {
878-
jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(callees, j + 1);
879-
if (jl_is_code_instance(mi))
880-
mi = ((jl_code_instance_t*)mi)->def;
881-
max_valid2 = verify_invokesig(edge, mi, minworld);
884+
jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(callees, j + 1);
885+
jl_method_t *meth;
886+
if (jl_is_code_instance(callee))
887+
callee = ((jl_code_instance_t*)callee)->def;
888+
if (jl_is_method_instance(callee))
889+
meth = callee->def.method;
890+
else {
891+
assert(jl_is_method(callee));
892+
meth = (jl_method_t*)callee;
893+
}
894+
max_valid2 = verify_invokesig(edge, meth, minworld);
882895
j += 2;
883896
}
884897
if (max_valid2 < max_valid)
@@ -1069,16 +1082,20 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_ci_list, size
10691082
// if this callee is still valid, add all the backedges
10701083
for (size_t j = 0; j < jl_svec_len(callees); ) {
10711084
jl_value_t *edge = jl_svecref(callees, j);
1085+
if (jl_is_long(edge)) {
1086+
j += 2; // skip over signature and count but not methods
1087+
continue;
1088+
}
1089+
else if (jl_is_method(edge)) {
1090+
j += 1;
1091+
continue;
1092+
}
10721093
if (jl_is_code_instance(edge))
10731094
edge = (jl_value_t*)((jl_code_instance_t*)edge)->def;
10741095
if (jl_is_method_instance(edge)) {
10751096
jl_method_instance_add_backedge((jl_method_instance_t*)edge, NULL, codeinst);
10761097
j += 1;
10771098
}
1078-
else if (jl_is_long(edge)) {
1079-
j += 2; // skip over signature and count but not methods
1080-
continue;
1081-
}
10821099
else if (jl_is_mtable(edge)) {
10831100
jl_methtable_t *mt = (jl_methtable_t*)edge;
10841101
jl_value_t *sig = jl_svecref(callees, j + 1);
@@ -1089,6 +1106,10 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_ci_list, size
10891106
jl_value_t *callee = jl_svecref(callees, j + 1);
10901107
if (jl_is_code_instance(callee))
10911108
callee = (jl_value_t*)((jl_code_instance_t*)callee)->def;
1109+
else if (jl_is_method(callee)) {
1110+
j += 2;
1111+
continue;
1112+
}
10921113
jl_method_instance_add_backedge((jl_method_instance_t*)callee, edge, codeinst);
10931114
j += 2;
10941115
}

0 commit comments

Comments
 (0)