From 9906495d2a40df3db5ce09b691d4a4f0ac1cf0bb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 May 2016 16:10:12 -0400 Subject: [PATCH 1/4] memoize more type properties has-typevars is called pretty heavily during intersection and subtyping so it can be a hot-path also fix jl_new_type_constructor to stop mutating (aka corrupting) the immutable TypeVars it is given as a parameter --- base/inference.jl | 8 +---- src/alloc.c | 28 +++++++++++++-- src/builtins.c | 9 ----- src/dump.c | 9 ++++- src/jltypes.c | 81 ++++++++++++++++++++++++++++++++++++++++---- src/julia.h | 4 +++ src/julia_internal.h | 2 +- 7 files changed, 114 insertions(+), 27 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 1e49b64e90d07..a69b46319ea80 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -374,13 +374,7 @@ function type_depth(t::ANY) t === Bottom && return 0 return maximum(type_depth, t.types) + 1 elseif isa(t, DataType) - t = t::DataType - P = t.parameters - isempty(P) && return 0 - if t.depth == 0 - t.depth = maximum(type_depth, P) + 1 - end - return t.depth + return (t::DataType).depth end return 0 end diff --git a/src/alloc.c b/src/alloc.c index e3d2eadd50df3..4a5ce469fee07 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -816,6 +816,9 @@ JL_DLLEXPORT jl_datatype_t *jl_new_uninitialized_datatype(size_t nfields, int8_t t->haspadding = 0; t->pointerfree = 0; t->depth = 0; + t->hastypevars = 0; + t->haswildcard = 0; + t->isleaftype = 1; return t; } @@ -983,6 +986,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super t->name->primary = (jl_value_t*)t; jl_gc_wb(t->name, t); } + jl_precompute_memoized_dt(t); if (abstract || jl_svec_len(parameters) > 0) { t->uid = 0; @@ -1011,14 +1015,32 @@ JL_DLLEXPORT jl_datatype_t *jl_new_bitstype(jl_value_t *name, jl_datatype_t *sup // type constructor ----------------------------------------------------------- -jl_typector_t *jl_new_type_ctor(jl_svec_t *params, jl_value_t *body) +JL_DLLEXPORT jl_value_t *jl_new_type_constructor(jl_svec_t *p, jl_value_t *body) { - jl_typector_t *tc = (jl_typector_t*)newobj((jl_value_t*)jl_typector_type,NWORDS(sizeof(jl_typector_t))); + size_t i, np = jl_svec_len(p); + jl_value_t **env; + JL_GC_PUSHARGS(env, np * 2 + 2); + jl_svec_t *params = jl_alloc_svec(np); + env[np * 2] = (jl_value_t*)params; + for (i = 0; i < np; i++) { + // recreate body with the TypeVar unbound + jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(p, i); + assert(jl_is_typevar(tv)); + env[2 * i] = (jl_value_t*)tv; + tv = jl_new_typevar(tv->name, tv->lb, tv->ub); + env[2 * i + 1] = (jl_value_t*)tv; + jl_svecset(params, i, tv); + } + body = jl_instantiate_type_with(body, env, np); + env[np * 2 + 1] = body; + + jl_typector_t *tc = (jl_typector_t*)newobj((jl_value_t*)jl_typector_type, NWORDS(sizeof(jl_typector_t))); tc->parameters = params; tc->body = body; - return (jl_typector_t*)tc; + return (jl_value_t*)tc; } + // bits constructors ---------------------------------------------------------- #define BOXN_FUNC(nb,nw) \ diff --git a/src/builtins.c b/src/builtins.c index 6198b22e549f6..924eba9a3756c 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -928,15 +928,6 @@ JL_CALLABLE(jl_f_apply_type) return jl_apply_type_(args[0], &args[1], nargs-1); } -JL_DLLEXPORT jl_value_t *jl_new_type_constructor(jl_svec_t *p, jl_value_t *t) -{ - jl_value_t *tc = (jl_value_t*)jl_new_type_ctor(p, t); - int i; - for(i=0; i < jl_svec_len(p); i++) - ((jl_tvar_t*)jl_svecref(p,i))->bound = 0; - return tc; -} - // generic function reflection ------------------------------------------------ static void jl_check_type_tuple(jl_value_t *t, jl_sym_t *name, const char *ctx) diff --git a/src/dump.c b/src/dump.c index 07290e2e14bf6..96c70bfb9021f 100644 --- a/src/dump.c +++ b/src/dump.c @@ -546,7 +546,9 @@ static void jl_serialize_datatype(ios_t *s, jl_datatype_t *dt) write_uint16(s, nf); write_int32(s, dt->size); int has_instance = !!(dt->instance != NULL); - write_uint8(s, dt->abstract | (dt->mutabl<<1) | (dt->pointerfree<<2) | (has_instance<<3)); + write_uint8(s, dt->abstract | (dt->mutabl<<1) | (dt->pointerfree<<2) | (has_instance<<3) | + (dt->hastypevars<<4) | (dt->haswildcard<<5) | (dt->isleaftype<<6)); + write_int32(s, dt->depth); write_int8(s, dt->fielddesc_type); if (!dt->abstract) { write_uint16(s, dt->ninitialized); @@ -1173,6 +1175,7 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) uint16_t nf = read_uint16(s); size_t size = read_int32(s); uint8_t flags = read_uint8(s); + uint8_t depth = read_int32(s); uint8_t fielddesc_type = read_int8(s); jl_datatype_t *dt; if (tag == 2) @@ -1194,6 +1197,10 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) dt->abstract = flags&1; dt->mutabl = (flags>>1)&1; dt->pointerfree = (flags>>2)&1; + dt->hastypevars = (flags>>4)&1; + dt->haswildcard = (flags>>5)&1; + dt->isleaftype = (flags>>6)&1; + dt->depth = depth; if (!dt->abstract) { dt->ninitialized = read_uint16(s); dt->uid = mode != MODE_MODULE && mode != MODE_MODULE_POSTWORK ? read_int32(s) : 0; diff --git a/src/jltypes.c b/src/jltypes.c index 71091ea4bf79f..69a2c3f04e622 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -95,12 +95,22 @@ static int jl_has_typevars__(jl_value_t *v, int incl_wildcard, jl_value_t **p, s if (jl_is_typector(v)) return incl_wildcard; jl_svec_t *t; + int expect = -1; if (jl_is_uniontype(v)) { t = ((jl_uniontype_t*)v)->types; } else if (jl_is_datatype(v)) { if (is_unspec((jl_datatype_t*)v)) return 0; + if (p == NULL) { + if (incl_wildcard) + expect = ((jl_datatype_t*)v)->haswildcard; + else + expect = ((jl_datatype_t*)v)->hastypevars; +#ifdef NDEBUG + return expect; +#endif + } t = ((jl_datatype_t*)v)->parameters; } else { @@ -110,14 +120,17 @@ static int jl_has_typevars__(jl_value_t *v, int incl_wildcard, jl_value_t **p, s for(i=0; i < l; i++) { jl_value_t *elt = jl_svecref(t, i); if (elt != v) { - if (jl_has_typevars__(elt, incl_wildcard, p, np)) + if (jl_has_typevars__(elt, incl_wildcard, p, np)) { + if (expect >= 0) assert(expect); return 1; + } } } // probably not necessary; no reason to use match() instead of subtype() // on the unconstrained version of a type //if (jl_is_typector(v)) // return jl_svec_len((((jl_typector_t*)v)->parameters) > 0); + if (expect >= 0) assert(!expect); return 0; } @@ -148,26 +161,39 @@ JL_DLLEXPORT int jl_has_typevars(jl_value_t *v) JL_DLLEXPORT int jl_is_leaf_type(jl_value_t *v) { if (jl_is_datatype(v)) { + int isleaf = ((jl_datatype_t*)v)->isleaftype; +#ifdef NDEBUG + return isleaf; +#else if (((jl_datatype_t*)v)->abstract) { - if (jl_is_type_type(v)) - return !jl_is_typevar(jl_tparam0(v)); - return 0; + int x = 0; + if (jl_is_type_type(v)) { + x = !jl_is_typevar(jl_tparam0(v)); + } + assert(x == isleaf); + return x; } jl_svec_t *t = ((jl_datatype_t*)v)->parameters; size_t l = jl_svec_len(t); if (((jl_datatype_t*)v)->name == jl_tuple_typename) { for(int i=0; i < l; i++) { - if (!jl_is_leaf_type(jl_svecref(t,i))) + if (!jl_is_leaf_type(jl_svecref(t,i))) { + assert(!isleaf); return 0; + } } } else { for(int i=0; i < l; i++) { - if (jl_is_typevar(jl_svecref(t,i))) + if (jl_is_typevar(jl_svecref(t,i))) { + assert(!isleaf); return 0; + } } } + assert(isleaf); return 1; +#endif } return 0; } @@ -2039,6 +2065,45 @@ static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, s return NULL; } +static size_t jl_type_depth(jl_value_t *dt) +{ + if (jl_is_uniontype(dt)) { + jl_svec_t *t = ((jl_uniontype_t*)dt)->types; + size_t i, l = jl_svec_len(t); + size_t depth = 0; + for (i = 0; i < l; i++) { + jl_value_t *p = jl_svecref(t, i); + size_t d = jl_type_depth(p); + if (d > depth) + depth = d; + } + return depth; + } + else if (jl_is_datatype(dt)) { + return ((jl_datatype_t*)dt)->depth; + } + return 0; +} + +void jl_precompute_memoized_dt(jl_datatype_t *dt) +{ + int istuple = dt->name == jl_tuple_typename; + size_t i, l = jl_nparams(dt); + dt->isleaftype = !dt->abstract || (jl_type_type != NULL && dt->name == jl_type_type->name); + for (i = 0; i < l; i++) { + jl_value_t *p = jl_tparam(dt, i); + size_t d = jl_type_depth(p) + 1; + if (d > dt->depth) + dt->depth = d; + if (!dt->hastypevars) + dt->hastypevars = jl_has_typevars__(p, 0, NULL, 0); + if (!dt->haswildcard) + dt->haswildcard = jl_has_typevars__(p, 1, NULL, 0); + if (dt->isleaftype) + dt->isleaftype = (istuple ? jl_is_leaf_type(p) : !jl_is_typevar(p)); + } +} + static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, int cacheable, int isabstract, jl_typestack_t *stack, jl_value_t **env, size_t n) @@ -2115,6 +2180,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i ndt->ditype = NULL; ndt->size = 0; ndt->alignment = 1; + jl_precompute_memoized_dt(ndt); // assign uid as early as possible if (cacheable && !ndt->abstract && ndt->uid==0) @@ -3340,6 +3406,9 @@ void jl_init_types(void) //jl_anytuple_type->parameters = jl_svec(1, jl_wrap_vararg((jl_value_t*)NULL, (jl_value_t*)NULL)); jl_anytuple_type->types = jl_anytuple_type->parameters; jl_anytuple_type->nfields = 1; + jl_anytuple_type->hastypevars = 1; + jl_anytuple_type->haswildcard = 1; + jl_anytuple_type->isleaftype = 0; jl_tvar_t *tttvar = jl_new_typevar(jl_symbol("T"), (jl_value_t*)jl_bottom_type,(jl_value_t*)jl_any_type); diff --git a/src/julia.h b/src/julia.h index a642ab33bf8c4..672037cbe5e45 100644 --- a/src/julia.h +++ b/src/julia.h @@ -329,7 +329,11 @@ typedef struct _jl_datatype_t { uint8_t mutabl; uint8_t pointerfree; int32_t ninitialized; + // memoized properties int32_t depth; + int8_t hastypevars; // bound + int8_t haswildcard; // unbound + int8_t isleaftype; // hidden fields: uint32_t nfields; uint32_t alignment : 29; // strictest alignment over all fields diff --git a/src/julia_internal.h b/src/julia_internal.h index a0c242fd96593..486f94b241591 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -182,11 +182,11 @@ STATIC_INLINE int jl_is_type(jl_value_t *v) } jl_value_t *jl_type_intersection_matching(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, jl_svec_t *tvars); -jl_typector_t *jl_new_type_ctor(jl_svec_t *params, jl_value_t *body); jl_value_t *jl_apply_type_(jl_value_t *tc, jl_value_t **params, size_t n); jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); jl_datatype_t *jl_new_abstracttype(jl_value_t *name, jl_datatype_t *super, jl_svec_t *parameters); +void jl_precompute_memoized_dt(jl_datatype_t *dt); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} jl_datatype_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n); void jl_assign_bits(void *dest, jl_value_t *bits); From 458297bc04454b70e38d64c1f044c16960484109 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 May 2016 16:55:04 -0400 Subject: [PATCH 2/4] avoid corrupting immutable TypeVar after building a DataType for them corrupt them before instead :) when it's less likely someone is looking fix #12238 fix #16301 --- src/interpreter.c | 6 +++--- test/core.jl | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index f7a0c71439579..756e5e154fe90 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -345,6 +345,9 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *la jl_datatype_t *dt = NULL; JL_GC_PUSH4(¶, &super, &temp, &dt); temp = eval(args[2], locals, lam); // field names + for(size_t i=0; i < jl_svec_len(para); i++) { + ((jl_tvar_t*)jl_svecref(para,i))->bound = 0; + } dt = jl_new_datatype((jl_sym_t*)name, jl_any_type, (jl_svec_t*)para, (jl_svec_t*)temp, NULL, 0, args[5]==jl_true ? 1 : 0, jl_unbox_long(args[6])); @@ -377,9 +380,6 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *la b->value = temp; jl_rethrow(); } - for(size_t i=0; i < jl_svec_len(para); i++) { - ((jl_tvar_t*)jl_svecref(para,i))->bound = 0; - } jl_compute_field_offsets(dt); if (para == (jl_value_t*)jl_emptysvec && jl_is_datatype_singleton(dt)) { dt->instance = newstruct(dt); diff --git a/test/core.jl b/test/core.jl index 43051a8358e6c..8092a1e55bce9 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4112,6 +4112,14 @@ f16090() = typeof(undefined_x16090::Tuple{Type{Int}}) undefined_x16090 = (Int,) @test_throws TypeError f16090() +# issue #12238 +type A12238{T} end +type B12238{T,S} + a::A12238{B12238{Int,S}} +end +@test B12238.types[1] === A12238{B12238{Int}} +@test A12238{B12238{Int}}.instance === B12238.types[1].instance + # issue #16315 let a = Any[] @noinline f() = a[end] From 98159c75b30e741fadd049808e84a06c2056b965 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 12 May 2016 13:43:23 -0400 Subject: [PATCH 3/4] fix lowering of TypeVar in TypeConstructor typealias expression have lowering emit the correct TypeVar expression instead of fixing it in the TypeConstructor constructor --- src/alloc.c | 18 ++++-------------- src/julia-syntax.scm | 2 +- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 4a5ce469fee07..8869a996ca84e 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1017,25 +1017,15 @@ JL_DLLEXPORT jl_datatype_t *jl_new_bitstype(jl_value_t *name, jl_datatype_t *sup JL_DLLEXPORT jl_value_t *jl_new_type_constructor(jl_svec_t *p, jl_value_t *body) { +#ifndef NDEBUG size_t i, np = jl_svec_len(p); - jl_value_t **env; - JL_GC_PUSHARGS(env, np * 2 + 2); - jl_svec_t *params = jl_alloc_svec(np); - env[np * 2] = (jl_value_t*)params; for (i = 0; i < np; i++) { - // recreate body with the TypeVar unbound jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(p, i); - assert(jl_is_typevar(tv)); - env[2 * i] = (jl_value_t*)tv; - tv = jl_new_typevar(tv->name, tv->lb, tv->ub); - env[2 * i + 1] = (jl_value_t*)tv; - jl_svecset(params, i, tv); + assert(jl_is_typevar(tv) && !tv->bound); } - body = jl_instantiate_type_with(body, env, np); - env[np * 2 + 1] = body; - +#endif jl_typector_t *tc = (jl_typector_t*)newobj((jl_value_t*)jl_typector_type, NWORDS(sizeof(jl_typector_t))); - tc->parameters = params; + tc->parameters = p; tc->body = body; return (jl_value_t*)tc; } diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index bd85e64d2965d..1eff899a37b2c 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1174,7 +1174,7 @@ ,@(map (lambda (v) `(local ,v)) params) ,@(map (lambda (l r) (make-assignment l (expand-forms r))) params - (symbols->typevars params bounds #t)) + (symbols->typevars params bounds #f)) (call (core TypeConstructor) (call (core svec) ,@params) ,(expand-forms type-ex)))))))) From fa6e7514953517b602ca74836de4b3d6582609f0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 12 May 2016 16:26:50 -0400 Subject: [PATCH 4/4] fix lowering of TypeVars in DataType declations have lowering emit the correct TypeVar expression instead of fixing it in the DataType constructor --- src/interpreter.c | 7 +++++-- src/julia-syntax.scm | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index 756e5e154fe90..8950f396937e8 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -345,9 +345,12 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, jl_lambda_info_t *la jl_datatype_t *dt = NULL; JL_GC_PUSH4(¶, &super, &temp, &dt); temp = eval(args[2], locals, lam); // field names - for(size_t i=0; i < jl_svec_len(para); i++) { - ((jl_tvar_t*)jl_svecref(para,i))->bound = 0; +#ifndef NDEBUG + size_t i, l = jl_svec_len(para); + for (i = 0; i < l; i++) { + assert(!((jl_tvar_t*)jl_svecref(para, i))->bound); } +#endif dt = jl_new_datatype((jl_sym_t*)name, jl_any_type, (jl_svec_t*)para, (jl_svec_t*)temp, NULL, 0, args[5]==jl_true ? 1 : 0, jl_unbox_long(args[6])); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 1eff899a37b2c..aea36c9c0c85b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -791,7 +791,7 @@ (block (global ,name) (const ,name) ,@(map (lambda (v) `(local ,v)) params) - ,@(map make-assignment params (symbols->typevars params bounds #t)) + ,@(map make-assignment params (symbols->typevars params bounds #f)) (composite_type ,name (call (core svec) ,@params) (call (core svec) ,@(map (lambda (x) `',x) field-names)) ,super (call (core svec) ,@field-types) ,mut ,min-initialized))) @@ -2487,7 +2487,7 @@ f(x) = yt(x) ((,@(map (lambda (p) `(,p Any 18)) P)) () 0 ()) (body (global ,name) (const ,name) - ,@(map (lambda (p) `(= ,p (call (core TypeVar) ',p (core Any) true))) P) + ,@(map (lambda (p) `(= ,p (call (core TypeVar) ',p (core Any) false))) P) (composite_type ,name (call (core svec) ,@P) (call (core svec) ,@(map (lambda (v) `',v) fields)) ,super