Skip to content

Commit bcdbde2

Browse files
vtjnashXnartharax
authored andcommitted
gf: add support for invalidating invoke edges (JuliaLang#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 JuliaLang#48802
1 parent 9bb5dcc commit bcdbde2

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
@@ -1816,6 +1816,36 @@ static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **is
18161816
return 1;
18171817
}
18181818

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

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

0 commit comments

Comments
 (0)