From b6bfb2fef102c390d0227be7d9fe3fa0d99223bf Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 18 May 2016 00:31:28 -0400 Subject: [PATCH 1/2] make the TypeMap capable of splitting on any concrete type previously, the TypeMap could only split on leaf types now it also has the ability to split on concrete types this allows it to ensure that each type gets its own linear list to scan to find its constructors, instead of needing to search through all constructors --- base/reflection.jl | 13 ++ src/dump.c | 20 +-- src/jltypes.c | 18 ++- src/julia.h | 8 +- src/typemap.c | 301 ++++++++++++++++++++++++++++++++++----------- 5 files changed, 276 insertions(+), 84 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 9e1e27a517d96..1883a43a5d8e9 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -301,6 +301,19 @@ function visit(f, mc::TypeMapLevel) isdefined(e, i) && visit(f, e[i]) end end + mc.linear_leaf !== nothing && visit(f, mc.linear_leaf) + if mc.tname !== nothing + e = mc.tname::Vector{Any} + for i in 1:length(e) + isdefined(e, i) && visit(f, e[i]) + end + end + if mc.name1 !== nothing + e = mc.name1::Vector{Any} + for i in 1:length(e) + isdefined(e, i) && visit(f, e[i]) + end + end mc.list !== nothing && visit(f, mc.list) mc.any !== nothing && visit(f, mc.any) nothing diff --git a/src/dump.c b/src/dump.c index 4780a126f3b0a..e718698900eb3 100644 --- a/src/dump.c +++ b/src/dump.c @@ -939,16 +939,22 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) // (which will need to be rehashed during deserialization anyhow) jl_typemap_level_t *node = (jl_typemap_level_t*)v; assert( // make sure this type has the expected ordering - offsetof(jl_typemap_level_t, arg1) == 0 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, targ) == 2 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, linear) == 4 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, any) == 5 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, key) == 6 * sizeof(jl_value_t*) && - sizeof(jl_typemap_level_t) == 7 * sizeof(jl_value_t*)); + offsetof(jl_typemap_level_t, targ) == 0 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, arg1) == 2 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, tname) == 4 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, name1) == 6 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, linear) == 8 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, any) == 9 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, key) == 10 * sizeof(jl_value_t*) && + sizeof(jl_typemap_level_t) == 11 * sizeof(jl_value_t*)); + jl_serialize_value(s, jl_nothing); + jl_serialize_value(s, node->targ.values); jl_serialize_value(s, jl_nothing); jl_serialize_value(s, node->arg1.values); jl_serialize_value(s, jl_nothing); - jl_serialize_value(s, node->targ.values); + jl_serialize_value(s, node->tname.values); + jl_serialize_value(s, jl_nothing); + jl_serialize_value(s, node->name1.values); jl_serialize_value(s, node->linear); jl_serialize_value(s, node->any.unknown); jl_serialize_value(s, node->key); diff --git a/src/jltypes.c b/src/jltypes.c index c3975601a4b15..f2360fd605910 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3678,15 +3678,23 @@ void jl_init_types(void) jl_typemap_level_type = jl_new_datatype(jl_symbol("TypeMapLevel"), jl_any_type, jl_emptysvec, - jl_svec(7, - jl_symbol("index_arg1"), - jl_symbol("arg1"), + jl_svec(11, jl_symbol("index_targ"), jl_symbol("targ"), + jl_symbol("index_arg1"), + jl_symbol("arg1"), + jl_symbol("index_tname"), + jl_symbol("tname"), + jl_symbol("index_name1"), + jl_symbol("name1"), jl_symbol("list"), jl_symbol("any"), jl_symbol("key")), - jl_svec(7, + jl_svec(11, + jl_any_type, + jl_any_type, + jl_any_type, + jl_any_type, jl_any_type, jl_any_type, jl_any_type, @@ -3694,7 +3702,7 @@ void jl_init_types(void) jl_any_type, jl_any_type, jl_any_type), - 0, 1, 6); + 0, 1, 10); jl_typemap_entry_type = jl_new_datatype(jl_symbol("TypeMapEntry"), jl_any_type, jl_emptysvec, diff --git a/src/julia.h b/src/julia.h index 61cfceada5f39..373ff75615447 100644 --- a/src/julia.h +++ b/src/julia.h @@ -300,7 +300,7 @@ typedef struct { jl_value_t *primary; jl_svec_t *cache; // sorted array jl_svec_t *linearcache; // unsorted array - intptr_t uid; + uint32_t uid; struct _jl_methtable_t *mt; } jl_typename_t; @@ -426,8 +426,10 @@ struct jl_ordereddict_t { }; typedef struct _jl_typemap_level_t { JL_DATA_TYPE - struct jl_ordereddict_t arg1; - struct jl_ordereddict_t targ; + struct jl_ordereddict_t targ; // contains Type{LeafType} + struct jl_ordereddict_t arg1; // contains LeafType + struct jl_ordereddict_t tname; // contains non-abstract Type{TypeName} + struct jl_ordereddict_t name1; // contains non-abstract TypeName jl_typemap_entry_t *linear; // union jl_typemap_t (but no more levels) union jl_typemap_t any; // type at offs is Any jl_value_t *key; // [nullable] diff --git a/src/typemap.c b/src/typemap.c index 466eb26d0556f..c7bc356aa323f 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -25,6 +25,55 @@ static int jl_is_any(jl_value_t *t1) !((jl_tvar_t*)t1)->bound)); } +// the goal here is to compute if sig is more-specific +// than something we can put into the TypeName-based hash tables +static int jl_args_morespecific_typename(jl_value_t *t1) +{ + if (jl_is_typector(t1)) + t1 = (jl_value_t*)((jl_typector_t*)t1)->body; + if (jl_is_uniontype(t1)) { + jl_uniontype_t *ut = (jl_uniontype_t*)t1; + size_t i, l = jl_svec_len(ut->types); + for (i = 0; i < l; i++) { + if (jl_args_morespecific_typename(jl_svecref(ut->types, i))) + return 1; + } + return 0; + } + else if (jl_is_typevar(t1)) { + return jl_args_morespecific_typename(((jl_tvar_t*)t1)->ub); + } + else if (jl_is_tuple(t1)) { + return 0; + } + else if (jl_is_type_type(t1)) { + return jl_args_morespecific_typename(jl_tparam0(t1)); + } + else if (jl_is_datatype(t1)) { + return !((jl_datatype_t*)t1)->abstract && !is_kind(t1); + } + return 0; +} + +static jl_value_t* jl_type_extract_name(jl_value_t *t1) +{ + if (jl_is_typector(t1)) + t1 = (jl_value_t*)((jl_typector_t*)t1)->body; + if (jl_is_vararg_type(t1)) { + return jl_type_extract_name(jl_tparam0(t1)); + } + else if (jl_is_typevar(t1)) { + return jl_type_extract_name(((jl_tvar_t*)t1)->ub); + } + else if (jl_is_datatype(t1)) { + jl_datatype_t *dt = (jl_datatype_t*)t1; + if (!dt->abstract && !is_kind(t1) && dt->name != jl_tuple_typename) + return (jl_value_t*)dt->name; + } + return NULL; +} + + // ----- Type Signature Subtype Testing ----- // static int sig_match_by_type_leaf(jl_value_t **types, jl_tupletype_t *sig, size_t n) @@ -238,9 +287,13 @@ static jl_array_t *jl_alloc_int_1d(size_t np, size_t len) static inline union jl_typemap_t mtcache_hash_lookup(const struct jl_ordereddict_t *a, jl_value_t *ty, int8_t tparam, int8_t offs) { - uintptr_t uid = ((jl_datatype_t*)ty)->uid; + uintptr_t uid; union jl_typemap_t ml; ml.unknown = jl_nothing; + if (tparam & 2) + uid = ((jl_typename_t*)ty)->uid; + else + uid = ((jl_datatype_t*)ty)->uid; if (!uid) return ml; size_t idx = jl_intref(a->indexes, uid & (a->indexes->nrows-1)); @@ -255,8 +308,10 @@ union jl_typemap_t mtcache_hash_lookup(const struct jl_ordereddict_t *a, jl_valu else { assert(jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_entry_type); t = jl_field_type(ml.leaf->sig, offs); - if (tparam) + if (tparam & 1) t = jl_tparam0(t); + if (tparam & 2) + t = jl_type_extract_name(t); } if (t != ty) ml.unknown = jl_nothing; @@ -284,28 +339,33 @@ static void mtcache_rehash(struct jl_ordereddict_t *pa, size_t newlen, jl_value_ for (i = 1; i <= nval; i++) { union jl_typemap_t ml; ml.unknown = jl_array_ptr_ref(pa->values, i - 1); - if (ml.unknown == jl_nothing) - continue; - jl_value_t *t; - if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { - t = ml.node->key; - } - else { - assert(jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_entry_type); - t = jl_field_type(ml.leaf->sig, offs); - if (tparam) - t = jl_tparam0(t); - } - uintptr_t uid = ((jl_datatype_t*)t)->uid; - size_t newi = uid & (newlen - 1); - if (jl_intref(n, newi) == 0) { - jl_intset(n, newi, i); - } - else { - // hash collision: start over after doubling the size again - i = 0; - newlen *= 2; - n = jl_alloc_int_1d(nval + 1, newlen); + if (ml.unknown != jl_nothing) { + jl_value_t *t; + if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { + t = ml.node->key; + } + else { + t = jl_field_type(ml.leaf->sig, offs); + if (tparam & 1) + t = jl_tparam0(t); + if (tparam & 2) + t = jl_type_extract_name(t); + } + uintptr_t uid; + if (tparam & 2) + uid = ((jl_typename_t*)t)->uid; + else + uid = ((jl_datatype_t*)t)->uid; + size_t newi = uid & (newlen - 1); + if (jl_intref(n, newi) == 0) { + jl_intset(n, newi, i); + } + else { + // hash collision: start over after doubling the size again + i = 0; + newlen *= 2; + n = jl_alloc_int_1d(nval + 1, newlen); + } } } pa->indexes = n; @@ -330,6 +390,10 @@ void jl_typemap_rehash(union jl_typemap_t ml, int8_t offs) { jl_typemap_rehash_array(&ml.node->targ, ml.unknown, 1, offs); if (ml.node->arg1.values != (void*)jl_nothing) jl_typemap_rehash_array(&ml.node->arg1, ml.unknown, 0, offs); + if (ml.node->tname.values != (void*)jl_nothing) + jl_typemap_rehash_array(&ml.node->tname, ml.unknown, 3, offs); + if (ml.node->name1.values != (void*)jl_nothing) + jl_typemap_rehash_array(&ml.node->name1, ml.unknown, 2, offs); jl_typemap_rehash(ml.node->any, offs+1); } } @@ -337,48 +401,57 @@ void jl_typemap_rehash(union jl_typemap_t ml, int8_t offs) { static union jl_typemap_t *mtcache_hash_bp(struct jl_ordereddict_t *pa, jl_value_t *ty, int8_t tparam, int8_t offs, jl_value_t *parent) { - if (jl_is_datatype(ty)) { - uintptr_t uid = ((jl_datatype_t*)ty)->uid; + uintptr_t uid; + if (tparam & 2) { + //if (ty == jl_datatype_type->name || ty == jl_typector_type->name) + // return NULL; // this is already handled in jl_type_extract_name + uid = ((jl_typename_t*)ty)->uid; + } + else { + if (!jl_is_datatype(ty)) + return NULL; + uid = ((jl_datatype_t*)ty)->uid; if (!uid || is_kind(ty) || jl_has_typevars(ty)) // be careful not to put non-leaf types or DataType/TypeConstructor in the cache here, // since they should have a lower priority and need to go into the sorted list return NULL; - if (pa->values == (void*)jl_nothing) { - pa->indexes = jl_alloc_int_1d(0, INIT_CACHE_SIZE); - jl_gc_wb(parent, pa->indexes); - pa->values = jl_alloc_vec_any(0); - jl_gc_wb(parent, pa->values); + } + if (pa->values == (void*)jl_nothing) { + pa->indexes = jl_alloc_int_1d(0, INIT_CACHE_SIZE); + jl_gc_wb(parent, pa->indexes); + pa->values = jl_alloc_vec_any(0); + jl_gc_wb(parent, pa->values); + } + while (1) { + size_t slot = uid & (pa->indexes->nrows - 1); + size_t idx = jl_intref(pa->indexes, slot); + if (idx == 0) { + jl_array_ptr_1d_push(pa->values, jl_nothing); + idx = jl_array_len(pa->values); + if (idx > jl_max_int(pa->indexes)) + mtcache_rehash(pa, jl_array_len(pa->indexes), parent, tparam, offs); + jl_intset(pa->indexes, slot, idx); + return &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; } - while (1) { - size_t slot = uid & (pa->indexes->nrows - 1); - size_t idx = jl_intref(pa->indexes, slot); - if (idx == 0) { - jl_array_ptr_1d_push(pa->values, jl_nothing); - idx = jl_array_len(pa->values); - if (idx > jl_max_int(pa->indexes)) - mtcache_rehash(pa, jl_array_len(pa->indexes), parent, tparam, offs); - jl_intset(pa->indexes, slot, idx); - return &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; - } - union jl_typemap_t *pml = &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; - if (pml->unknown == jl_nothing) - return pml; - jl_value_t *t; - if (jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_level_type) { - t = pml->node->key; - } - else { - assert(jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_entry_type); - t = jl_field_type(pml->leaf->sig, offs); - if (tparam) - t = jl_tparam0(t); - } - if (t == ty) - return pml; - mtcache_rehash(pa, jl_array_len(pa->indexes) * 2, parent, tparam, offs); + union jl_typemap_t *pml = &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; + if (pml->unknown == jl_nothing) + return pml; + jl_value_t *t; + if (jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_level_type) { + t = pml->node->key; } + else { + assert(jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_entry_type); + t = jl_field_type(pml->leaf->sig, offs); + if (tparam & 1) + t = jl_tparam0(t); + if (tparam & 2) + t = jl_type_extract_name(t); + } + if (t == ty) + return pml; + mtcache_rehash(pa, jl_array_len(pa->indexes) * 2, parent, tparam, offs); } - return NULL; } @@ -445,6 +518,12 @@ int jl_typemap_visitor(union jl_typemap_t cache, jl_typemap_visitor_fptr fptr, v if (cache.node->arg1.values != (void*)jl_nothing) if (!jl_typemap_array_visitor(&cache.node->arg1, fptr, closure)) return 0; + if (cache.node->tname.values != (void*)jl_nothing) + if (!jl_typemap_array_visitor(&cache.node->tname, fptr, closure)) + return 0; + if (cache.node->name1.values != (void*)jl_nothing) + if (!jl_typemap_array_visitor(&cache.node->name1, fptr, closure)) + return 0; if (!jl_typemap_node_visitor(cache.node->linear, fptr, closure)) return 0; return jl_typemap_visitor(cache.node->any, fptr, closure); @@ -468,11 +547,32 @@ static int jl_typemap_intersection_array_visitor(struct jl_ordereddict_t *a, jl_ union jl_typemap_t *data = (union jl_typemap_t*)jl_array_data(a->values); for (i = 0; i < l; i++) { union jl_typemap_t ml = data[i]; - if (ml.unknown == jl_nothing) - continue; jl_value_t *t; - if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { - t = ml.node->key; + if (ml.unknown != jl_nothing) { + if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { + t = ml.node->key; + } + else { + t = jl_field_type(ml.leaf->sig, offs); + if (tparam & 1) + t = jl_tparam0(t); + } + if (tparam & 2) { + // TODO: fast path rejection test + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) + return 0; + } else { + if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches + (tparam ? // need to compute `ty <: Type{t}` + (jl_is_uniontype(ty) || // punt on Union{...} right now + jl_typeof(t) == ty || // deal with kinds (e.g. ty == DataType && t == Type{t}) + (jl_is_type_type(ty) && (jl_is_typevar(jl_tparam0(ty)) ? + jl_subtype(t, ((jl_tvar_t*)jl_tparam0(ty))->ub, 0) : // deal with ty == Type{<:T} + jl_subtype(t, jl_tparam0(ty), 0)))) // deal with ty == Type{T{#<:T}} + : jl_subtype(t, ty, 0))) // `t` is a leaftype, so intersection test becomes subtype + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) + return 0; + } } else { t = jl_field_type(ml.leaf->sig, offs); @@ -576,6 +676,17 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, } } } + if (ty) { + if (cache->tname.values != (void*)jl_nothing) { + // TODO: optimized fast path + //jl_value_t *typetype = jl_is_type_type(ty) ? jl_tparam0(ty) : NULL; + if (!jl_typemap_intersection_array_visitor(&cache->tname, ty, 3, offs, closure)) return 0; + } + if (cache->name1.values != (void*)jl_nothing) { + // TODO: optimized fast path + if (!jl_typemap_intersection_array_visitor(&cache->name1, ty, 2, offs, closure)) return 0; + } + } if (!jl_typemap_intersection_node_visitor(map.node->linear, closure)) return 0; if (ty) @@ -735,7 +846,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ else if (l > offs) { ty = jl_tparam(types, offs); } - // If there is a type at offs, look in the optimized caches + // If there is a type at offs, look in the optimized leaf type caches if (!subtype) { if (ty && jl_is_any(ty)) return jl_typemap_assoc_by_type(cache->any, types, penv, subtype_inexact__sigseq_useenv, subtype, offs+1); @@ -765,6 +876,31 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } if (!subtype && is_cache_leaf(ty)) return NULL; } + // if there is a type at offs, now look at the optimized TypeName caches + if (ty) { + if (cache->tname.values != (void*)jl_nothing && jl_is_type_type(ty)) { + jl_value_t *a0 = jl_type_extract_name(jl_tparam0(ty)); + if (a0) { + union jl_typemap_t ml = mtcache_hash_lookup(&cache->tname, a0, 3, offs); + if (ml.unknown != jl_nothing) { + jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, types, penv, + subtype_inexact__sigseq_useenv, subtype, offs+1); + if (li) return li; + } + } + } + if (cache->name1.values != (void*)jl_nothing) { + jl_value_t *a0 = jl_type_extract_name(ty); + if (a0) { + union jl_typemap_t ml = mtcache_hash_lookup(&cache->name1, a0, 2, offs); + if (ml.unknown != jl_nothing) { + jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, types, penv, + subtype_inexact__sigseq_useenv, subtype, offs+1); + if (li) return li; + } + } + } + } // Always check the list (since offs doesn't always start at 0) if (subtype) { jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, subtype_inexact__sigseq_useenv, penv); @@ -858,9 +994,10 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_value_t **args, size_t n, int8_t offs) { + jl_value_t *a1 = NULL, *ty = NULL; if (n > offs) { - jl_value_t *a1 = args[offs]; - jl_value_t *ty = (jl_value_t*)jl_typeof(a1); + a1 = args[offs]; + ty = (jl_value_t*)jl_typeof(a1); assert(jl_is_datatype(ty)); if (ty == (jl_value_t*)jl_datatype_type && cache->targ.values != (void*)jl_nothing) { union jl_typemap_t ml_or_cache = mtcache_hash_lookup(&cache->targ, a1, 1, offs); @@ -872,6 +1009,16 @@ jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_v jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, args, n, offs+1); if (ml) return ml; } + if (ty == (jl_value_t*)jl_datatype_type && cache->tname.values != (void*)jl_nothing) { + union jl_typemap_t ml_or_cache = mtcache_hash_lookup(&cache->tname, (jl_value_t*)((jl_datatype_t*)a1)->name, 3, offs); + jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, args, n, offs+1); + if (ml) return ml; + } + if (cache->name1.values != (void*)jl_nothing) { + union jl_typemap_t ml_or_cache = mtcache_hash_lookup(&cache->name1, (jl_value_t*)((jl_datatype_t*)ty)->name, 2, offs); + jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, args, n, offs+1); + if (ml) return ml; + } } if (cache->linear != (jl_typemap_entry_t*)jl_nothing) { jl_typemap_entry_t *ml = jl_typemap_entry_assoc_exact(cache->linear, args, n); @@ -912,6 +1059,10 @@ static jl_typemap_level_t *jl_new_typemap_level(void) cache->targ.values = (jl_array_t*)jl_nothing; cache->arg1.indexes = (jl_array_t*)jl_nothing; cache->arg1.values = (jl_array_t*)jl_nothing; + cache->tname.indexes = (jl_array_t*)jl_nothing; + cache->tname.values = (jl_array_t*)jl_nothing; + cache->name1.indexes = (jl_array_t*)jl_nothing; + cache->name1.values = (jl_array_t*)jl_nothing; return cache; } @@ -1000,16 +1151,28 @@ static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry return jl_typemap_insert_generic(&cache->any, (jl_value_t*)cache, newrec, (jl_value_t*)jl_any_type, offs+1, tparams); // Don't put Varargs in the optimized caches (too hard to handle in lookup and bp) if (t1 && !isva) { - // if t1 != jl_typetype_type and the argument is Type{...}, this - // method has specializations for singleton kinds and we use - // the table indexed for that purpose. + // try to put in leaf type caches if (t1 != (jl_value_t*)jl_typetype_type && jl_is_type_type(t1)) { + // if t1 != jl_typetype_type and the argument is Type{...}, this + // method has specializations for singleton kinds and we use + // the table indexed for that purpose. jl_value_t *a0 = jl_tparam0(t1); if (jl_typemap_array_insert_(&cache->targ, a0, newrec, (jl_value_t*)cache, 1, offs, tparams)) return; } if (jl_typemap_array_insert_(&cache->arg1, t1, newrec, (jl_value_t*)cache, 0, offs, tparams)) return; + + // try to put in TypeName caches + jl_value_t *a0; + if (jl_is_type_type(t1)) { + a0 = jl_type_extract_name(jl_tparam0(t1)); + if (a0 && jl_typemap_array_insert_(&cache->tname, a0, newrec, (jl_value_t*)cache, 3, offs, tparams)) + return; + } + a0 = jl_type_extract_name(t1); + if (a0 && jl_typemap_array_insert_(&cache->name1, a0, newrec, (jl_value_t*)cache, 2, offs, tparams)) + return; } jl_typemap_list_insert_(&cache->linear, (jl_value_t*)cache, newrec, tparams); } From 1618919f09994428815d7d7ade1e1beb05191bf6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 18 May 2016 15:38:12 -0400 Subject: [PATCH 2/2] some type signatures are only partially more specific than TypeName this require creation of extra entries to split the method between multiple tables [ci skip] [av skip] --- base/reflection.jl | 2 +- src/dump.c | 20 +-- src/jltypes.c | 16 +- src/julia.h | 8 +- src/typemap.c | 379 ++++++++++++++++++++++++++++----------------- 5 files changed, 264 insertions(+), 161 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 1883a43a5d8e9..bd4b8303f7ed5 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -289,6 +289,7 @@ function visit(f, mt::MethodTable) nothing end function visit(f, mc::TypeMapLevel) + mc.bottom !== nothing && visit(f, mc.bottom) if mc.targ !== nothing e = mc.targ::Vector{Any} for i in 1:length(e) @@ -301,7 +302,6 @@ function visit(f, mc::TypeMapLevel) isdefined(e, i) && visit(f, e[i]) end end - mc.linear_leaf !== nothing && visit(f, mc.linear_leaf) if mc.tname !== nothing e = mc.tname::Vector{Any} for i in 1:length(e) diff --git a/src/dump.c b/src/dump.c index e718698900eb3..ffaa703c8cbf3 100644 --- a/src/dump.c +++ b/src/dump.c @@ -939,14 +939,16 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) // (which will need to be rehashed during deserialization anyhow) jl_typemap_level_t *node = (jl_typemap_level_t*)v; assert( // make sure this type has the expected ordering - offsetof(jl_typemap_level_t, targ) == 0 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, arg1) == 2 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, tname) == 4 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, name1) == 6 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, linear) == 8 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, any) == 9 * sizeof(jl_value_t*) && - offsetof(jl_typemap_level_t, key) == 10 * sizeof(jl_value_t*) && - sizeof(jl_typemap_level_t) == 11 * sizeof(jl_value_t*)); + offsetof(jl_typemap_level_t, bottom) == 0 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, targ) == 1 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, arg1) == 3 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, tname) == 5 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, name1) == 7 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, linear) == 9 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, any) == 10 * sizeof(jl_value_t*) && + offsetof(jl_typemap_level_t, key) == 11 * sizeof(jl_value_t*) && + sizeof(jl_typemap_level_t) == 12 * sizeof(jl_value_t*)); + jl_serialize_value(s, node->bottom); jl_serialize_value(s, jl_nothing); jl_serialize_value(s, node->targ.values); jl_serialize_value(s, jl_nothing); @@ -993,7 +995,7 @@ struct jl_serialize_methcache_from_mod_env { static int jl_serialize_methcache_from_mod(jl_typemap_entry_t *ml, void *closure) { struct jl_serialize_methcache_from_mod_env *env = (struct jl_serialize_methcache_from_mod_env*)closure; - if (module_in_worklist(ml->func.method->module)) { + if (module_in_worklist(ml->func.method->module) && !ml->weak) { jl_serialize_value(env->s, ml->func.method); jl_serialize_value(env->s, ml->simplesig); } diff --git a/src/jltypes.c b/src/jltypes.c index f2360fd605910..4a7d19f34b34f 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3678,7 +3678,8 @@ void jl_init_types(void) jl_typemap_level_type = jl_new_datatype(jl_symbol("TypeMapLevel"), jl_any_type, jl_emptysvec, - jl_svec(11, + jl_svec(12, + jl_symbol("bottom"), jl_symbol("index_targ"), jl_symbol("targ"), jl_symbol("index_arg1"), @@ -3690,7 +3691,8 @@ void jl_init_types(void) jl_symbol("list"), jl_symbol("any"), jl_symbol("key")), - jl_svec(11, + jl_svec(12, + jl_any_type, jl_any_type, jl_any_type, jl_any_type, @@ -3702,11 +3704,11 @@ void jl_init_types(void) jl_any_type, jl_any_type, jl_any_type), - 0, 1, 10); + 0, 1, 11); jl_typemap_entry_type = jl_new_datatype(jl_symbol("TypeMapEntry"), jl_any_type, jl_emptysvec, - jl_svec(9, jl_symbol("next"), + jl_svec(10, jl_symbol("next"), jl_symbol("sig"), jl_symbol("tvars"), jl_symbol("simplesig"), @@ -3714,8 +3716,9 @@ void jl_init_types(void) jl_symbol("func"), jl_symbol("isleafsig"), jl_symbol("issimplesig"), - jl_symbol("va")), - jl_svec(9, jl_any_type, // Union{TypeMapEntry, Void} + jl_symbol("va"), + jl_symbol("weak")), + jl_svec(10, jl_any_type, // Union{TypeMapEntry, Void} jl_type_type, // TupleType jl_any_type, // Union{SimpleVector{TypeVar}, TypeVar} jl_any_type, // TupleType @@ -3723,6 +3726,7 @@ void jl_init_types(void) jl_any_type, // Any jl_bool_type, jl_bool_type, + jl_bool_type, jl_bool_type), 0, 1, 5); diff --git a/src/julia.h b/src/julia.h index 373ff75615447..0ccb05815bc1a 100644 --- a/src/julia.h +++ b/src/julia.h @@ -416,6 +416,7 @@ typedef struct _jl_typemap_entry_t { int8_t isleafsig; // isleaftype(sig) & !any(isType, sig) : unsorted and very fast int8_t issimplesig; // all(isleaftype | isAny | isType | isVararg, sig) : sorted and fast int8_t va; // isVararg(sig) + int8_t weak; // will never overwrite a signature } jl_typemap_entry_t; // one level in a TypeMap tree @@ -426,11 +427,12 @@ struct jl_ordereddict_t { }; typedef struct _jl_typemap_level_t { JL_DATA_TYPE + jl_typemap_entry_t *bottom; // union jl_typemap_t (but no more levels) for entries which have no type at offs struct jl_ordereddict_t targ; // contains Type{LeafType} struct jl_ordereddict_t arg1; // contains LeafType struct jl_ordereddict_t tname; // contains non-abstract Type{TypeName} struct jl_ordereddict_t name1; // contains non-abstract TypeName - jl_typemap_entry_t *linear; // union jl_typemap_t (but no more levels) + jl_typemap_entry_t *linear; // union jl_typemap_t (but no more levels) - most entries usually end up here union jl_typemap_t any; // type at offs is Any jl_value_t *key; // [nullable] } jl_typemap_level_t; @@ -999,7 +1001,9 @@ STATIC_INLINE int jl_is_leaf_type_(jl_value_t *v) // type constructors JL_DLLEXPORT jl_typename_t *jl_new_typename(jl_sym_t *name); -JL_DLLEXPORT jl_tvar_t *jl_new_typevar(jl_sym_t *name,jl_value_t *lb,jl_value_t *ub); +JL_DLLEXPORT jl_tvar_t *jl_new_typevar(jl_sym_t *name, jl_value_t *lb, jl_value_t *ub); +JL_DLLEXPORT jl_tvar_t *jl_new_typevar_(jl_sym_t *name, jl_value_t *lb, + jl_value_t *ub, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_apply_type(jl_value_t *tc, jl_svec_t *params); JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params); JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); diff --git a/src/typemap.c b/src/typemap.c index c7bc356aa323f..75ccc283367fb 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -25,36 +25,6 @@ static int jl_is_any(jl_value_t *t1) !((jl_tvar_t*)t1)->bound)); } -// the goal here is to compute if sig is more-specific -// than something we can put into the TypeName-based hash tables -static int jl_args_morespecific_typename(jl_value_t *t1) -{ - if (jl_is_typector(t1)) - t1 = (jl_value_t*)((jl_typector_t*)t1)->body; - if (jl_is_uniontype(t1)) { - jl_uniontype_t *ut = (jl_uniontype_t*)t1; - size_t i, l = jl_svec_len(ut->types); - for (i = 0; i < l; i++) { - if (jl_args_morespecific_typename(jl_svecref(ut->types, i))) - return 1; - } - return 0; - } - else if (jl_is_typevar(t1)) { - return jl_args_morespecific_typename(((jl_tvar_t*)t1)->ub); - } - else if (jl_is_tuple(t1)) { - return 0; - } - else if (jl_is_type_type(t1)) { - return jl_args_morespecific_typename(jl_tparam0(t1)); - } - else if (jl_is_datatype(t1)) { - return !((jl_datatype_t*)t1)->abstract && !is_kind(t1); - } - return 0; -} - static jl_value_t* jl_type_extract_name(jl_value_t *t1) { if (jl_is_typector(t1)) @@ -73,7 +43,6 @@ static jl_value_t* jl_type_extract_name(jl_value_t *t1) return NULL; } - // ----- Type Signature Subtype Testing ----- // static int sig_match_by_type_leaf(jl_value_t **types, jl_tupletype_t *sig, size_t n) @@ -339,33 +308,33 @@ static void mtcache_rehash(struct jl_ordereddict_t *pa, size_t newlen, jl_value_ for (i = 1; i <= nval; i++) { union jl_typemap_t ml; ml.unknown = jl_array_ptr_ref(pa->values, i - 1); - if (ml.unknown != jl_nothing) { - jl_value_t *t; - if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { - t = ml.node->key; - } - else { - t = jl_field_type(ml.leaf->sig, offs); - if (tparam & 1) - t = jl_tparam0(t); - if (tparam & 2) - t = jl_type_extract_name(t); - } - uintptr_t uid; + if (ml.unknown == jl_nothing) + continue; + jl_value_t *t; + if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { + t = ml.node->key; + } + else { + t = jl_field_type(ml.leaf->sig, offs); + if (tparam & 1) + t = jl_tparam0(t); if (tparam & 2) - uid = ((jl_typename_t*)t)->uid; - else - uid = ((jl_datatype_t*)t)->uid; - size_t newi = uid & (newlen - 1); - if (jl_intref(n, newi) == 0) { - jl_intset(n, newi, i); - } - else { - // hash collision: start over after doubling the size again - i = 0; - newlen *= 2; - n = jl_alloc_int_1d(nval + 1, newlen); - } + t = jl_type_extract_name(t); + } + uintptr_t uid; + if (tparam & 2) + uid = ((jl_typename_t*)t)->uid; + else + uid = ((jl_datatype_t*)t)->uid; + size_t newi = uid & (newlen - 1); + if (jl_intref(n, newi) == 0) { + jl_intset(n, newi, i); + } + else { + // hash collision: start over after doubling the size again + i = 0; + newlen *= 2; + n = jl_alloc_int_1d(nval + 1, newlen); } } pa->indexes = n; @@ -512,6 +481,9 @@ static int jl_typemap_node_visitor(jl_typemap_entry_t *ml, jl_typemap_visitor_fp int jl_typemap_visitor(union jl_typemap_t cache, jl_typemap_visitor_fptr fptr, void *closure) { if (jl_typeof(cache.unknown) == (jl_value_t*)jl_typemap_level_type) { + if (cache.node->bottom != (void*)jl_nothing) + if (!jl_typemap_node_visitor(cache.node->bottom, fptr, closure)) + return 0; if (cache.node->targ.values != (void*)jl_nothing) if (!jl_typemap_array_visitor(&cache.node->targ, fptr, closure)) return 0; @@ -547,48 +519,33 @@ static int jl_typemap_intersection_array_visitor(struct jl_ordereddict_t *a, jl_ union jl_typemap_t *data = (union jl_typemap_t*)jl_array_data(a->values); for (i = 0; i < l; i++) { union jl_typemap_t ml = data[i]; + if (ml.unknown == jl_nothing) + continue; jl_value_t *t; - if (ml.unknown != jl_nothing) { - if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { - t = ml.node->key; - } - else { - t = jl_field_type(ml.leaf->sig, offs); - if (tparam & 1) - t = jl_tparam0(t); - } - if (tparam & 2) { - // TODO: fast path rejection test - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) - return 0; - } else { - if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - (tparam ? // need to compute `ty <: Type{t}` - (jl_is_uniontype(ty) || // punt on Union{...} right now - jl_typeof(t) == ty || // deal with kinds (e.g. ty == DataType && t == Type{t}) - (jl_is_type_type(ty) && (jl_is_typevar(jl_tparam0(ty)) ? - jl_subtype(t, ((jl_tvar_t*)jl_tparam0(ty))->ub, 0) : // deal with ty == Type{<:T} - jl_subtype(t, jl_tparam0(ty), 0)))) // deal with ty == Type{T{#<:T}} - : jl_subtype(t, ty, 0))) // `t` is a leaftype, so intersection test becomes subtype - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) - return 0; - } + if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { + t = ml.node->key; } else { t = jl_field_type(ml.leaf->sig, offs); - if (tparam) + if (tparam & 1) t = jl_tparam0(t); } - if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - (tparam ? // need to compute `ty <: Type{t}` - (jl_is_uniontype(ty) || // punt on Union{...} right now - jl_typeof(t) == ty || // deal with kinds (e.g. ty == DataType && t == Type{t}) - (jl_is_type_type(ty) && (jl_is_typevar(jl_tparam0(ty)) ? - jl_subtype(t, ((jl_tvar_t*)jl_tparam0(ty))->ub, 0) : // deal with ty == Type{<:T} - jl_subtype(t, jl_tparam0(ty), 0)))) // deal with ty == Type{T{#<:T}} - : jl_subtype(t, ty, 0))) // `t` is a leaftype, so intersection test becomes subtype + if (tparam & 2) { + // TODO: fast path rejection test if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + } else { + if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches + (tparam ? // need to compute `ty <: Type{t}` + (jl_is_uniontype(ty) || // punt on Union{...} right now + jl_typeof(t) == ty || // deal with kinds (e.g. ty == DataType && t == Type{t}) + (jl_is_type_type(ty) && (jl_is_typevar(jl_tparam0(ty)) ? + jl_subtype(t, ((jl_tvar_t*)jl_tparam0(ty))->ub, 0) : // deal with ty == Type{<:T} + jl_subtype(t, jl_tparam0(ty), 0)))) // deal with ty == Type{T{#<:T}} + : jl_subtype(t, ty, 0))) // `t` is a leaftype, so intersection test becomes subtype + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) + return 0; + } } return 1; } @@ -639,59 +596,58 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, size_t l = jl_field_count(closure->type); if (closure->va && l <= offs + 1) { ty = closure->va; + if (!jl_typemap_intersection_node_visitor(map.node->bottom, closure)) + return 0; } else if (l > offs) { ty = jl_tparam(closure->type, offs); } - if (ty) { - if (cache->targ.values != (void*)jl_nothing) { - jl_value_t *typetype = jl_is_type_type(ty) ? jl_tparam0(ty) : NULL; - if (typetype && !jl_has_typevars(typetype)) { - if (is_cache_leaf(typetype)) { - // direct lookup of leaf types - union jl_typemap_t ml = mtcache_hash_lookup(&cache->targ, typetype, 1, offs); - if (ml.unknown != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; - } - } - } - else { - // else an array scan is required to check subtypes - // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type - if (typetype || jl_type_intersection((jl_value_t*)jl_type_type, ty) != jl_bottom_type) - if (!jl_typemap_intersection_array_visitor(&cache->targ, ty, 1, offs, closure)) return 0; - } - } - if (cache->arg1.values != (void*)jl_nothing) { - if (is_cache_leaf(ty)) { + else { + return jl_typemap_intersection_node_visitor(map.node->bottom, closure); + } + if (cache->targ.values != (void*)jl_nothing) { + jl_value_t *typetype = jl_is_type_type(ty) ? jl_tparam0(ty) : NULL; + if (typetype && !jl_has_typevars(typetype)) { + if (is_cache_leaf(typetype)) { // direct lookup of leaf types - union jl_typemap_t ml = mtcache_hash_lookup(&cache->arg1, ty, 0, offs); + union jl_typemap_t ml = mtcache_hash_lookup(&cache->targ, typetype, 1, offs); if (ml.unknown != jl_nothing) { if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; } } - else { - // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(&cache->arg1, ty, 0, offs, closure)) return 0; - } + } + else { + // else an array scan is required to check subtypes + // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type + if (typetype || jl_type_intersection((jl_value_t*)jl_type_type, ty) != jl_bottom_type) + if (!jl_typemap_intersection_array_visitor(&cache->targ, ty, 1, offs, closure)) return 0; } } - if (ty) { - if (cache->tname.values != (void*)jl_nothing) { - // TODO: optimized fast path - //jl_value_t *typetype = jl_is_type_type(ty) ? jl_tparam0(ty) : NULL; - if (!jl_typemap_intersection_array_visitor(&cache->tname, ty, 3, offs, closure)) return 0; + if (cache->arg1.values != (void*)jl_nothing) { + if (is_cache_leaf(ty)) { + // direct lookup of leaf types + union jl_typemap_t ml = mtcache_hash_lookup(&cache->arg1, ty, 0, offs); + if (ml.unknown != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + } } - if (cache->name1.values != (void*)jl_nothing) { - // TODO: optimized fast path - if (!jl_typemap_intersection_array_visitor(&cache->name1, ty, 2, offs, closure)) return 0; + else { + // else an array scan is required to check subtypes + if (!jl_typemap_intersection_array_visitor(&cache->arg1, ty, 0, offs, closure)) return 0; } } + if (cache->tname.values != (void*)jl_nothing) { + // TODO: optimized fast path + //jl_value_t *typetype = jl_is_type_type(ty) ? jl_tparam0(ty) : NULL; + if (!jl_typemap_intersection_array_visitor(&cache->tname, ty, 3, offs, closure)) return 0; + } + if (cache->name1.values != (void*)jl_nothing) { + // TODO: optimized fast path + if (!jl_typemap_intersection_array_visitor(&cache->name1, ty, 2, offs, closure)) return 0; + } if (!jl_typemap_intersection_node_visitor(map.node->linear, closure)) return 0; - if (ty) - return jl_typemap_intersection_visitor(map.node->any, offs+1, closure); - return 1; + return jl_typemap_intersection_visitor(map.node->any, offs+1, closure); } else { return jl_typemap_intersection_node_visitor(map.leaf, closure); @@ -838,22 +794,34 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ if (jl_is_vararg_type(ty)) { ty = jl_tparam0(ty); isva = 1; + jl_typemap_entry_t *li = subtype ? + jl_typemap_assoc_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv, penv) : + jl_typemap_lookup_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv); + if (li) return li; } else if (l <= offs) { - ty = NULL; + return subtype ? + jl_typemap_assoc_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv, penv) : + jl_typemap_lookup_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv); } } else if (l > offs) { ty = jl_tparam(types, offs); } + else { + return subtype ? + jl_typemap_assoc_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv, penv) : + jl_typemap_lookup_by_type_(cache->bottom, types, subtype_inexact__sigseq_useenv); + } // If there is a type at offs, look in the optimized leaf type caches if (!subtype) { - if (ty && jl_is_any(ty)) + if (jl_is_any(ty)) return jl_typemap_assoc_by_type(cache->any, types, penv, subtype_inexact__sigseq_useenv, subtype, offs+1); if (isva) // in lookup mode, want to match Vararg exactly, not as a subtype ty = NULL; } if (ty) { + // now look at the optimized leaftype caches if (jl_is_type_type(ty)) { jl_value_t *a0 = jl_tparam0(ty); if (cache->targ.values != (void*)jl_nothing && jl_is_datatype(a0)) { @@ -875,9 +843,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } } if (!subtype && is_cache_leaf(ty)) return NULL; - } - // if there is a type at offs, now look at the optimized TypeName caches - if (ty) { + // now look at the optimized TypeName caches if (cache->tname.values != (void*)jl_nothing && jl_is_type_type(ty)) { jl_value_t *a0 = jl_type_extract_name(jl_tparam0(ty)); if (a0) { @@ -994,10 +960,11 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_value_t **args, size_t n, int8_t offs) { - jl_value_t *a1 = NULL, *ty = NULL; - if (n > offs) { - a1 = args[offs]; - ty = (jl_value_t*)jl_typeof(a1); + if (n <= offs) { + return jl_typemap_entry_assoc_exact(cache->bottom, args, n); + } else { + jl_value_t *a1 = args[offs]; + jl_value_t *ty = (jl_value_t*)jl_typeof(a1); assert(jl_is_datatype(ty)); if (ty == (jl_value_t*)jl_datatype_type && cache->targ.values != (void*)jl_nothing) { union jl_typemap_t ml_or_cache = mtcache_hash_lookup(&cache->targ, a1, 1, offs); @@ -1042,7 +1009,8 @@ static unsigned jl_typemap_list_count(jl_typemap_entry_t *ml) return count; } -static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry_t *newrec, int8_t offs, const struct jl_typemap_info *tparams); +static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry_t *newrec, int8_t offs, + const struct jl_typemap_info *tparams); static void jl_typemap_list_insert_sorted(jl_typemap_entry_t **pml, jl_value_t *parent, jl_typemap_entry_t *newrec, const struct jl_typemap_info *tparams); @@ -1053,6 +1021,7 @@ static jl_typemap_level_t *jl_new_typemap_level(void) (jl_typemap_level_t*)jl_gc_alloc(ptls, sizeof(jl_typemap_level_t), jl_typemap_level_type); cache->key = NULL; + cache->bottom = (jl_typemap_entry_t*)jl_nothing; cache->linear = (jl_typemap_entry_t*)jl_nothing; cache->any.unknown = jl_nothing; cache->targ.indexes = (jl_array_t*)jl_nothing; @@ -1066,7 +1035,8 @@ static jl_typemap_level_t *jl_new_typemap_level(void) return cache; } -static jl_typemap_level_t *jl_method_convert_list_to_cache(jl_typemap_entry_t *ml, jl_value_t *key, int8_t offs) +static jl_typemap_level_t *jl_method_convert_list_to_cache(jl_typemap_entry_t *ml, jl_value_t *key, int8_t offs, + const struct jl_typemap_info *tparams) { jl_typemap_level_t *cache = jl_new_typemap_level(); cache->key = key; @@ -1075,7 +1045,7 @@ static jl_typemap_level_t *jl_method_convert_list_to_cache(jl_typemap_entry_t *m while (ml != (void*)jl_nothing) { next = ml->next; ml->next = (jl_typemap_entry_t*)jl_nothing; - jl_typemap_level_insert_(cache, ml, offs, 0); + jl_typemap_level_insert_(cache, ml, offs, tparams); ml = next; } JL_GC_POP(); @@ -1107,7 +1077,7 @@ static void jl_typemap_insert_generic(union jl_typemap_t *pml, jl_value_t *paren unsigned count = jl_typemap_list_count(pml->leaf); if (count > MAX_METHLIST_COUNT) { - pml->node = jl_method_convert_list_to_cache(pml->leaf, key, offs); + pml->node = jl_method_convert_list_to_cache(pml->leaf, key, offs, tparams); jl_gc_wb(parent, pml->node); jl_typemap_level_insert_(pml->node, newrec, offs, tparams); return; @@ -1126,6 +1096,118 @@ static int jl_typemap_array_insert_(struct jl_ordereddict_t *cache, jl_value_t * return pml != NULL; } +static void jl_split_morespecific_typename(jl_typemap_entry_t *newrec, int8_t offs, + jl_typemap_level_t *cache, const struct jl_typemap_info *tparams) +{ + size_t siglen = jl_svec_len(newrec->sig->parameters); + if (siglen <= offs) // bound Vararg to the length of the signature + offs = siglen - 1; + jl_value_t *t1 = jl_tparam(newrec->sig, offs); + jl_tvar_t *tvar = NULL; + jl_value_t *vararg = NULL; + int vararg_pos = 0; + if (jl_is_typector(t1)) { + t1 = (jl_value_t*)((jl_typector_t*)t1)->body; + } + if (jl_is_vararg_type(t1)) { + vararg = t1; + vararg_pos = 1; + t1 = jl_tparam0(t1); + } + if (jl_is_typevar(t1)) { + tvar = (jl_tvar_t*)t1; + t1 = tvar->ub; + } + if (jl_is_vararg_type(t1)) { + assert(vararg == NULL); + vararg = t1; + vararg_pos = 2; + t1 = jl_tparam0(t1); + } + + if (jl_is_uniontype(t1)) { + jl_ptls_t ptls = jl_get_ptls_states(); + jl_value_t *splitsig = NULL; + jl_value_t *splitenv = NULL; + jl_value_t *temp = NULL; + JL_GC_PUSH3(&splitsig, &splitenv, &temp); + jl_uniontype_t *ut = (jl_uniontype_t*)t1; + size_t i, l = jl_svec_len(ut->types); + for (i = 0; i < l; i++) { + jl_value_t *elem = jl_svecref(ut->types, i); + jl_value_t *t0 = jl_is_type_type(elem) ? jl_type_extract_name(jl_tparam0(elem)) : NULL; + jl_value_t *a0 = jl_type_extract_name(elem); + if (t0 || a0) { + temp = elem; + if (vararg_pos == 2) + temp = (jl_value_t*)jl_wrap_vararg(temp, jl_tparam1(vararg)); + if (tvar != NULL && tvar->bound) { + temp = (jl_value_t*)jl_new_typevar_(tvar->name, tvar->lb, temp, jl_true); + jl_value_t *env[2]; + env[0] = (jl_value_t*)tvar; + env[1] = temp; + splitsig = jl_instantiate_type_with((jl_value_t*)newrec->sig, env, 1); + + if (jl_is_typevar(newrec->tvars)) { + splitenv = temp; + } + else { + size_t tv; + splitenv = (jl_value_t*)jl_alloc_svec_uninit(jl_svec_len(newrec->tvars)); + for (tv = 0; tv < jl_svec_len(newrec->tvars); tv++) { + jl_value_t *t = jl_svecref(newrec->tvars, tv); + jl_svecset(splitenv, tv, t == (jl_value_t*)tvar ? temp : t); + } + } + } + else { + if (tvar != NULL) + temp = (jl_value_t*)jl_new_typevar(tvar->name, tvar->lb, temp); + if (vararg_pos == 1) + temp = (jl_value_t*)jl_wrap_vararg(temp, jl_tparam1(vararg)); + splitsig = (jl_value_t*)jl_svec_copy(newrec->sig->parameters); + jl_svecset(splitsig, offs, temp); + splitsig = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)splitsig); + splitenv = (jl_value_t*)newrec->tvars; + } + + jl_typemap_entry_t *newrec2 = + (jl_typemap_entry_t*)jl_gc_alloc(ptls, sizeof(jl_typemap_entry_t), + jl_typemap_entry_type); + jl_set_typeof(newrec2, jl_typemap_entry_type); + *newrec2 = *newrec; // copy newrec to newrec2 + newrec2->sig = (jl_datatype_t*)splitsig; + newrec2->tvars = (jl_svec_t*)splitenv; + // TODO: ignore weak entries for some operations + newrec2->weak = 1; + temp = (jl_value_t*)newrec2; // gc-root + + int inserted = 0; + if (t0) { + union jl_typemap_t *pml = mtcache_hash_bp(&cache->tname, t0, 3, offs, (jl_value_t*)cache); + if (pml) { + jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*pml, newrec2->sig, NULL, 1, 0, offs+1); + if (ml == NULL) // don't overwrite an existing entry + jl_typemap_insert_generic(pml, (jl_value_t*)cache->tname.values, newrec2, t0, offs+1, tparams); + inserted = 1; + } + } + if (!inserted && a0) { + union jl_typemap_t *pml = mtcache_hash_bp(&cache->name1, a0, 2, offs, (jl_value_t*)cache); + if (pml) { + jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*pml, newrec2->sig, NULL, 1, 0, offs+1); + if (ml == NULL) // don't overwrite an existing entry + jl_typemap_insert_generic(pml, (jl_value_t*)cache->name1.values, newrec2, a0, offs+1, tparams); + inserted = 1; + } + } + } + } + JL_GC_POP(); + } +} + + static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry_t *newrec, int8_t offs, const struct jl_typemap_info *tparams) { @@ -1146,11 +1228,15 @@ static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry else if (l > offs) { t1 = jl_tparam(newrec->sig, offs); } + if (t1 == NULL) { + jl_typemap_list_insert_(&cache->bottom, (jl_value_t*)cache, newrec, tparams); + return; + } // If the type at `offs` is Any, put it in the Any list - if (t1 && jl_is_any(t1)) + if (jl_is_any(t1)) return jl_typemap_insert_generic(&cache->any, (jl_value_t*)cache, newrec, (jl_value_t*)jl_any_type, offs+1, tparams); // Don't put Varargs in the optimized caches (too hard to handle in lookup and bp) - if (t1 && !isva) { + if (!isva) { // try to put in leaf type caches if (t1 != (jl_value_t*)jl_typetype_type && jl_is_type_type(t1)) { // if t1 != jl_typetype_type and the argument is Type{...}, this @@ -1174,6 +1260,11 @@ static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry if (a0 && jl_typemap_array_insert_(&cache->name1, a0, newrec, (jl_value_t*)cache, 2, offs, tparams)) return; } + // need to split this union into components that go into TypeName, and those less-specific + jl_split_morespecific_typename(newrec, offs, cache, tparams); + // always fall-through. newrec is unmodified since lessspec_sig <: newrec->sig, + // and the user might try to look up the original sig directly + // (either through invoke or exact extraction) jl_typemap_list_insert_(&cache->linear, (jl_value_t*)cache, newrec, tparams); } @@ -1202,6 +1293,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_gc_wb(ml, ml->sig); ml->simplesig = simpletype; jl_gc_wb(ml, ml->simplesig); + assert((ml->tvars == jl_emptysvec) == (tvars == jl_emptysvec)); ml->tvars = tvars; jl_gc_wb(ml, ml->tvars); ml->va = jl_is_va_tuple(type); @@ -1219,6 +1311,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_typemap_entry_t *newrec = (jl_typemap_entry_t*)jl_gc_alloc(ptls, sizeof(jl_typemap_entry_t), jl_typemap_entry_type); + newrec->weak = 0; newrec->sig = type; newrec->simplesig = simpletype; newrec->tvars = tvars;