Skip to content

Commit 64ea3e1

Browse files
vtjnashKristofferC
authored andcommitted
gf: add support for invalidating invoke edges (#48954)
Apparently we never actually implemented support for invalidation detection on invoke edges, and furthermore, it had erased part of the support for regular edges. Generalize our code for detecting replacement of a method, to be used when computing replacement of an invoke edge. Fix #48802 (cherry picked from commit 386b1b6)
1 parent e628534 commit 64ea3e1

File tree

1 file changed

+58
-51
lines changed

1 file changed

+58
-51
lines changed

src/gf.c

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,36 @@ static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **is
18151815
return 1;
18161816
}
18171817

1818+
enum morespec_options {
1819+
morespec_unknown,
1820+
morespec_isnot,
1821+
morespec_is
1822+
};
1823+
1824+
// check if `type` is replacing `m` with an ambiguity here, given other methods in `d` that already match it
1825+
// precondition: type is not more specific than `m`
1826+
static int is_replacing(jl_value_t *type, jl_method_t *m, jl_method_t *const *d, size_t n, jl_value_t *isect, jl_value_t *isect2, char *morespec)
1827+
{
1828+
size_t k;
1829+
for (k = 0; k < n; k++) {
1830+
jl_method_t *m2 = d[k];
1831+
// see if m2 also fully covered this intersection
1832+
if (m == m2 || !(jl_subtype(isect, m2->sig) || (isect2 && jl_subtype(isect2, m2->sig))))
1833+
continue;
1834+
if (morespec[k] == (char)morespec_unknown)
1835+
morespec[k] = (char)(jl_type_morespecific(m2->sig, type) ? morespec_is : morespec_isnot);
1836+
if (morespec[k] == (char)morespec_is)
1837+
// not actually shadowing this--m2 will still be better
1838+
return 0;
1839+
// since m2 was also a previous match over isect,
1840+
// see if m was also previously dominant over all m2
1841+
if (!jl_type_morespecific(m->sig, m2->sig))
1842+
// m and m2 were previously ambiguous over the full intersection of mi with type, and will still be ambiguous with type
1843+
return 0;
1844+
}
1845+
return 1;
1846+
}
1847+
18181848
JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype)
18191849
{
18201850
JL_TIMING(ADD_METHOD);
@@ -1849,7 +1879,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
18491879
oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry);
18501880

18511881
int invalidated = 0;
1852-
jl_method_t **d;
1882+
jl_method_t *const *d;
18531883
size_t j, n;
18541884
if (oldvalue == NULL) {
18551885
d = NULL;
@@ -1878,6 +1908,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
18781908
// -> less specific or ambiguous with any one of them: can ignore the missing edge (not missing)
18791909
// -> some may have been ambiguous: still are
18801910
// -> some may have been called: they may be partly replaced (will be detected in the loop later)
1911+
// c.f. `is_replacing`, which is a similar query, but with an existing method match to compare against
18811912
missing = 1;
18821913
size_t j;
18831914
for (j = 0; j < n; j++) {
@@ -1912,11 +1943,6 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19121943
}
19131944
if (oldvalue) {
19141945
oldmi = jl_alloc_vec_any(0);
1915-
enum morespec_options {
1916-
morespec_unknown,
1917-
morespec_isnot,
1918-
morespec_is
1919-
};
19201946
char *morespec = (char*)alloca(n);
19211947
memset(morespec, morespec_unknown, n);
19221948
for (j = 0; j < n; j++) {
@@ -1933,6 +1959,11 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19331959
continue;
19341960
isect3 = jl_type_intersection(m->sig, (jl_value_t*)mi->specTypes);
19351961
if (jl_type_intersection2(type, isect3, &isect, &isect2)) {
1962+
// TODO: this only checks pair-wise for ambiguities, but the ambiguities could arise from the interaction of multiple methods
1963+
// and thus might miss a case where we introduce an ambiguity between two existing methods
1964+
// We could instead work to sort this into 3 groups `morespecific .. ambiguous .. lesspecific`, with `type` in ambiguous,
1965+
// such that everything in `morespecific` dominates everything in `ambiguous`, and everything in `ambiguous` dominates everything in `lessspecific`
1966+
// And then compute where each isect falls, and whether it changed group--necessitating invalidation--or not.
19361967
if (morespec[j] == (char)morespec_unknown)
19371968
morespec[j] = (char)(jl_type_morespecific(m->sig, type) ? morespec_is : morespec_isnot);
19381969
if (morespec[j] == (char)morespec_is)
@@ -1941,62 +1972,38 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19411972
if (ambig == morespec_unknown)
19421973
ambig = jl_type_morespecific(type, m->sig) ? morespec_is : morespec_isnot;
19431974
// replacing a method--see if this really was the selected method previously
1944-
// over the intersection
1945-
if (ambig == morespec_isnot) {
1946-
size_t k;
1947-
for (k = 0; k < n; k++) {
1948-
jl_method_t *m2 = d[k];
1949-
if (m == m2 || !(jl_subtype(isect, m2->sig) || (isect && jl_subtype(isect, m2->sig))))
1950-
continue;
1951-
if (morespec[k] == (char)morespec_unknown)
1952-
morespec[k] = (char)(jl_type_morespecific(m2->sig, type) ? morespec_is : morespec_isnot);
1953-
if (morespec[k] == (char)morespec_is)
1954-
// not actually shadowing this--m2 will still be better
1955-
break;
1956-
// since m2 was also a previous match over isect,
1957-
// see if m was also previously dominant over all m2
1958-
if (!jl_type_morespecific(m->sig, m2->sig))
1959-
break;
1960-
}
1961-
if (k != n)
1962-
continue;
1963-
}
1964-
// Before deciding whether to invalidate `mi`, check each backedge for `invoke`s
1965-
if (mi->backedges) {
1966-
jl_array_t *backedges = mi->backedges;
1975+
// over the intersection (not ambiguous) and the new method will be selected now (morespec_is)
1976+
int replaced_dispatch = ambig == morespec_is || is_replacing(type, m, d, n, isect, isect2, morespec);
1977+
// found that this specialization dispatch got replaced by m
1978+
// call invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert");
1979+
// but ignore invoke-type edges
1980+
jl_array_t *backedges = mi->backedges;
1981+
if (backedges) {
19671982
size_t ib = 0, insb = 0, nb = jl_array_len(backedges);
19681983
jl_value_t *invokeTypes;
19691984
jl_method_instance_t *caller;
19701985
while (ib < nb) {
19711986
ib = get_next_edge(backedges, ib, &invokeTypes, &caller);
1972-
if (!invokeTypes) {
1973-
// ordinary dispatch, invalidate
1987+
int replaced_edge;
1988+
if (invokeTypes) {
1989+
// n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes
1990+
replaced_edge = jl_subtype(invokeTypes, type) && (ambig == morespec_is || is_replacing(type, m, d, n, invokeTypes, NULL, morespec));
1991+
}
1992+
else {
1993+
replaced_edge = replaced_dispatch;
1994+
}
1995+
if (replaced_edge) {
19741996
invalidate_method_instance(&do_nothing_with_codeinst, caller, max_world, 1);
19751997
invalidated = 1;
1976-
} else {
1977-
// invoke-dispatch, check invokeTypes for validity
1978-
struct jl_typemap_assoc search = {invokeTypes, method->primary_world, NULL, 0, ~(size_t)0};
1979-
oldentry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, /*offs*/0, /*subtype*/0);
1980-
if (oldentry && oldentry->func.method == mi->def.method) {
1981-
// We can safely keep this method
1982-
jl_array_ptr_set(backedges, insb++, invokeTypes);
1983-
jl_array_ptr_set(backedges, insb++, caller);
1984-
} else {
1985-
invalidate_method_instance(&do_nothing_with_codeinst, caller, max_world, 1);
1986-
invalidated = 1;
1987-
}
1998+
}
1999+
else {
2000+
insb = set_next_edge(backedges, insb, invokeTypes, caller);
19882001
}
19892002
}
19902003
jl_array_del_end(backedges, nb - insb);
19912004
}
1992-
if (!mi->backedges || jl_array_len(mi->backedges) == 0) {
1993-
jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi);
1994-
invalidate_external(mi, max_world);
1995-
if (mi->backedges) {
1996-
invalidated = 1;
1997-
invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert");
1998-
}
1999-
}
2005+
jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi);
2006+
invalidate_external(mi, max_world);
20002007
}
20012008
}
20022009
}

0 commit comments

Comments
 (0)