Skip to content

Commit

Permalink
memoize more type properties (#16320)
Browse files Browse the repository at this point in the history
has-typevars is called pretty heavily during intersection and subtyping
so it can be a hot-path

fix lowering of TypeVar in TypeConstructor typealias expression:
instead have lowering emit the correct TypeVar expression instead of mutating (aka corrupting)
the immutable TypeVars in the TypeConstructor constructor

avoid corrupting immutable TypeVar after building a DataType for them:
instead have lowering emit the correct TypeVar expression
instead of modifying them in the DataType constructor
fix #12238
fix #16301
  • Loading branch information
vtjnash committed May 13, 2016
1 parent 906e06b commit 0ffba40
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 34 deletions.
8 changes: 1 addition & 7 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1011,14 +1015,22 @@ 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)));
tc->parameters = params;
#ifndef NDEBUG
size_t i, np = jl_svec_len(p);
for (i = 0; i < np; i++) {
jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(p, i);
assert(jl_is_typevar(tv) && !tv->bound);
}
#endif
jl_typector_t *tc = (jl_typector_t*)newobj((jl_value_t*)jl_typector_type, NWORDS(sizeof(jl_typector_t)));
tc->parameters = p;
tc->body = body;
return (jl_typector_t*)tc;
return (jl_value_t*)tc;
}


// bits constructors ----------------------------------------------------------

#define BOXN_FUNC(nb,nw) \
Expand Down
9 changes: 0 additions & 9 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down
9 changes: 6 additions & 3 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +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(&para, &super, &temp, &dt);
temp = eval(args[2], locals, lam); // field names
#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]));
Expand Down Expand Up @@ -377,9 +383,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);
Expand Down
81 changes: 75 additions & 6 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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))))))))
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4113,6 +4113,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]
Expand Down

0 comments on commit 0ffba40

Please sign in to comment.