Skip to content

Commit

Permalink
replace ANY with @nospecialize annotation. part of #11339
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Jul 3, 2017
1 parent 4b345c1 commit fc7419d
Show file tree
Hide file tree
Showing 15 changed files with 107 additions and 41 deletions.
4 changes: 4 additions & 0 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ macro _noinline_meta()
Expr(:meta, :noinline)
end

macro nospecialize(x)
Expr(:meta, :nospecialize, x)
end

struct BoundsError <: Exception
a::Any
i::Any
Expand Down
23 changes: 23 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,29 @@ end
macro _noinline_meta()
Expr(:meta, :noinline)
end
"""
@nospecialize
Applied to a function argument name, hints to the compiler that the method
should not be specialized for different types of the specified argument.
This is only a hint for avoiding excess code generation.
Can be applied to an argument within a formal argument list, or in the
function body:
```julia
function example_function(@nospecialize x)
...
end
function example_function(x, y, z)
@nospecialize x y
...
end
```
"""
macro nospecialize(var, vars...)
Expr(:meta, :nospecialize, var, vars...)
end
macro _pure_meta()
Expr(:meta, :pure)
end
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,7 @@ export
@simd,
@inline,
@noinline,
@nospecialize,
@polly,

@assert,
Expand Down
3 changes: 2 additions & 1 deletion src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jl_sym_t *inert_sym; jl_sym_t *vararg_sym;
jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym;
jl_sym_t *polly_sym; jl_sym_t *inline_sym;
jl_sym_t *propagate_inbounds_sym;
jl_sym_t *isdefined_sym;
jl_sym_t *isdefined_sym; jl_sym_t *nospecialize_sym;

static uint8_t flisp_system_image[] = {
#include <julia_flisp.boot.inc>
Expand Down Expand Up @@ -433,6 +433,7 @@ void jl_init_frontend(void)
inline_sym = jl_symbol("inline");
propagate_inbounds_sym = jl_symbol("propagate_inbounds");
isdefined_sym = jl_symbol("isdefined");
nospecialize_sym = jl_symbol("nospecialize");
}

JL_DLLEXPORT void jl_lisp_prompt(void)
Expand Down
11 changes: 11 additions & 0 deletions src/ast.scm
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@
(if (not (symbol? (cadr v)))
(bad-formal-argument (cadr v)))
(decl-var v))
((meta) ;; allow certain per-argument annotations
(if (and (length= v 3) (eq? (cadr v) 'nospecialize))
(arg-name (caddr v))
(bad-formal-argument v)))
(else (bad-formal-argument v))))))

(define (arg-type v)
Expand All @@ -167,6 +171,10 @@
(if (not (symbol? (cadr v)))
(bad-formal-argument (cadr v)))
(decl-type v))
((meta) ;; allow certain per-argument annotations
(if (and (length= v 3) (eq? (cadr v) 'nospecialize))
(arg-type (caddr v))
(bad-formal-argument v)))
(else (bad-formal-argument v))))))

;; convert a lambda list into a list of just symbols
Expand Down Expand Up @@ -310,6 +318,9 @@
(define (kwarg? e)
(and (pair? e) (eq? (car e) 'kw)))

(define (nospecialize-meta? e)
(and (length> e 2) (eq? (car e) 'meta) (eq? (cadr e) 'nospecialize)))

;; flatten nested expressions with the given head
;; (op (op a b) c) => (op a b c)
(define (flatten-ex head e)
Expand Down
20 changes: 5 additions & 15 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,8 @@ static void jl_cacheable_sig(

int notcalled_func = (i > 0 && i <= 8 && !(definition->called & (1 << (i - 1))) &&
jl_subtype(elt, (jl_value_t*)jl_function_type));
if (decl_i == jl_ANY_flag) {
// don't specialize on slots marked ANY
if (i > 0 && i <= 8 && (definition->nospec & (1 << (i - 1))) &&
decl_i == (jl_value_t*)jl_any_type) { // TODO: nospecialize with other types
if (!*newparams) *newparams = jl_svec_copy(type->parameters);
jl_svecset(*newparams, i, (jl_value_t*)jl_any_type);
*need_guard_entries = 1;
Expand Down Expand Up @@ -714,9 +714,9 @@ JL_DLLEXPORT int jl_is_cacheable_sig(
continue;
if (jl_is_kind(elt)) // kind slots always need guard entries (checking for subtypes of Type)
continue;
if (decl_i == jl_ANY_flag) {
// don't specialize on slots marked ANY
if (elt != (jl_value_t*)jl_any_type && elt != jl_ANY_flag)
if (i > 0 && i <= 8 && (definition->nospec & (1 << (i - 1))) &&
decl_i == (jl_value_t*)jl_any_type) { // TODO: nospecialize with other types
if (elt != (jl_value_t*)jl_any_type)
return 0;
continue;
}
Expand Down Expand Up @@ -2258,16 +2258,6 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio
break;
}
}
// don't analyze slots declared with ANY
// TODO
/*
l = jl_nparams(ml->sig);
size_t m = jl_nparams(ti);
for(i=0; i < l && i < m; i++) {
if (jl_tparam(ml->sig, i) == jl_ANY_flag)
jl_tupleset(ti, i, jl_any_type);
}
*/
}
if (!skip) {
/*
Expand Down
12 changes: 5 additions & 7 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ static int typeenv_has(jl_typeenv_t *env, jl_tvar_t *v)
static int has_free_typevars(jl_value_t *v, jl_typeenv_t *env)
{
if (jl_typeis(v, jl_tvar_type)) {
if (v == jl_ANY_flag) return 0;
return !typeenv_has(env, (jl_tvar_t*)v);
}
if (jl_is_uniontype(v))
Expand Down Expand Up @@ -181,7 +180,6 @@ JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v)
static void find_free_typevars(jl_value_t *v, jl_typeenv_t *env, jl_array_t *out)
{
if (jl_typeis(v, jl_tvar_type)) {
if (v == jl_ANY_flag) return;
if (!typeenv_has(env, (jl_tvar_t*)v))
jl_array_ptr_1d_push(out, v);
}
Expand Down Expand Up @@ -238,7 +236,7 @@ static int jl_has_bound_typevars(jl_value_t *v, jl_typeenv_t *env)
return ans;
}
if (jl_is_datatype(v)) {
if (!((jl_datatype_t*)v)->hasfreetypevars && !(env && env->var == (jl_tvar_t*)jl_ANY_flag))
if (!((jl_datatype_t*)v)->hasfreetypevars)
return 0;
size_t i;
for (i=0; i < jl_nparams(v); i++) {
Expand Down Expand Up @@ -669,8 +667,6 @@ static int is_cacheable(jl_datatype_t *type)
assert(jl_is_datatype(type));
jl_svec_t *t = type->parameters;
if (jl_svec_len(t) == 0) return 0;
if (jl_has_typevar((jl_value_t*)type, (jl_tvar_t*)jl_ANY_flag))
return 0;
// cache abstract types with no free type vars
if (jl_is_abstracttype(type))
return !jl_has_free_typevars((jl_value_t*)type);
Expand Down Expand Up @@ -1939,7 +1935,7 @@ void jl_init_types(void)
jl_method_type =
jl_new_datatype(jl_symbol("Method"), core,
jl_any_type, jl_emptysvec,
jl_perm_symsvec(19,
jl_perm_symsvec(20,
"name",
"module",
"file",
Expand All @@ -1956,10 +1952,11 @@ void jl_init_types(void)
"invokes",
"nargs",
"called",
"nospec",
"isva",
"isstaged",
"pure"),
jl_svec(19,
jl_svec(20,
jl_sym_type,
jl_module_type,
jl_sym_type,
Expand All @@ -1976,6 +1973,7 @@ void jl_init_types(void)
jl_any_type,
jl_int32_type,
jl_int32_type,
jl_int32_type,
jl_bool_type,
jl_bool_type,
jl_bool_type),
Expand Down
15 changes: 13 additions & 2 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,14 @@
(string "function Base.broadcast(::typeof(" (deparse op_) "), ...)")))
op_))
(name (if op '(|.| Base (inert broadcast)) name))
(annotations (map (lambda (a) `(meta nospecialize ,(arg-name a)))
(filter nospecialize-meta? argl)))
(body (if (null? annotations)
(caddr e)
(insert-after-meta (caddr e) annotations)))
(argl (map (lambda (a)
(if (nospecialize-meta? a) (caddr a) a))
argl))
(argl (if op (cons `(|::| (call (core Typeof) ,op)) argl) argl))
(sparams (map analyze-typevar (cond (has-sp (cddr head))
(where where)
Expand All @@ -1046,7 +1054,7 @@
(name (if (or (decl? name) (and (pair? name) (eq? (car name) 'curly)))
#f name)))
(expand-forms
(method-def-expr name sparams argl (caddr e) isstaged rett))))
(method-def-expr name sparams argl body isstaged rett))))
(else
(error (string "invalid assignment location \"" (deparse name) "\""))))))

Expand Down Expand Up @@ -1170,7 +1178,7 @@
(|::| __module__ (core Module))
,@(map (lambda (v)
(if (symbol? v)
`(|::| ,v (core ANY))
`(|::| ,v (core ANY)) ;; TODO: ANY deprecation
v))
anames))
,@(cddr e)))))
Expand Down Expand Up @@ -3780,6 +3788,9 @@ f(x) = yt(x)
((and (pair? e) (eq? (car e) 'outerref))
(let ((idx (get sp-table (cadr e) #f)))
(if idx `(static_parameter ,idx) (cadr e))))
((and (length> e 2) (eq? (car e) 'meta) (eq? (cadr e) 'nospecialize))
;; convert nospecialize vars to slot numbers
`(meta nospecialize ,@(map renumber-slots (cddr e))))
((or (atom? e) (quoted? e)) e)
((ssavalue? e)
(let ((idx (or (get ssavalue-table (cadr e) #f)
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ typedef struct _jl_method_t {

int32_t nargs;
int32_t called; // bit flags: whether each of the first 8 arguments is called
int32_t nospec; // bit flags: which arguments should not be specialized
uint8_t isva;
uint8_t isstaged;
uint8_t pure;
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym;
extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym;
extern jl_sym_t *polly_sym; extern jl_sym_t *inline_sym;
extern jl_sym_t *propagate_inbounds_sym;
extern jl_sym_t *isdefined_sym;
extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym;

void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets,
jl_method_instance_t **linfos, size_t n);
Expand Down
4 changes: 4 additions & 0 deletions src/macroexpand.scm
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@
(case (car v)
((... kw |::|) (try-arg-name (cadr v)))
((escape) (list v))
((meta) ;; allow certain per-argument annotations
(if (and (length= v 3) (eq? (cadr v) 'nospecialize))
(try-arg-name (caddr v))
'()))
(else '())))))

;; get names from a formal argument list, specifying whether to include escaped ones
Expand Down
22 changes: 21 additions & 1 deletion src/method.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,15 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src)
set_lineno = 1;
}
}
else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == meta_sym &&
jl_expr_nargs(st) > 1 && jl_exprarg(st,0) == (jl_value_t*)nospecialize_sym) {
for(size_t j=1; j < jl_expr_nargs(st); j++) {
jl_value_t *aj = jl_exprarg(st, j);
if (jl_is_slot(aj))
m->nospec |= (1 << (jl_slot_number(aj) - 2));
}
st = jl_nothing;
}
else {
st = jl_resolve_globals(st, m->module, sparam_vars);
}
Expand Down Expand Up @@ -465,6 +474,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module)
m->file = empty_sym;
m->line = 0;
m->called = 0xff;
m->nospec = 0;
m->invokes.unknown = NULL;
m->isstaged = 0;
m->isva = 0;
Expand Down Expand Up @@ -642,6 +652,16 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata,
jl_methtable_t *mt;
jl_sym_t *name;
jl_method_t *m = NULL;
size_t i, na = jl_svec_len(atypes);
int32_t nospec = 0;
for(i=1; i < na; i++) {
jl_value_t *ti = jl_svecref(atypes, i);
if (ti == jl_ANY_flag ||
(jl_is_vararg_type(ti) && jl_tparam0(jl_unwrap_unionall(ti)) == jl_ANY_flag)) {
nospec |= (1 << (i - 1));
jl_svecset(atypes, i, jl_substitute_var(ti, (jl_tvar_t*)jl_ANY_flag, (jl_value_t*)jl_any_type));
}
}
jl_value_t *argtype = (jl_value_t*)jl_apply_tuple_type(atypes);
JL_GC_PUSH3(&f, &m, &argtype);

Expand Down Expand Up @@ -675,6 +695,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata,
}

m = jl_new_method(f, name, module, (jl_tupletype_t*)argtype, nargs, isva, tvars, isstaged == jl_true);
m->nospec |= nospec;

if (jl_has_free_typevars(argtype)) {
jl_exceptionf(jl_argumenterror_type,
Expand All @@ -686,7 +707,6 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata,

jl_check_static_parameter_conflicts(m, f, tvars);

size_t i, na = jl_svec_len(atypes);
for (i = 0; i < na; i++) {
jl_value_t *elt = jl_svecref(atypes, i);
if (!jl_is_type(elt) && !jl_is_typevar(elt)) {
Expand Down
12 changes: 2 additions & 10 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -790,8 +790,6 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e);
// diagonal rule (record_var_occurrence).
static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
{
if (x == jl_ANY_flag) x = (jl_value_t*)jl_any_type;
if (y == jl_ANY_flag) y = (jl_value_t*)jl_any_type;
if (jl_is_uniontype(x)) {
if (x == y) return 1;
x = pick_union_element(x, e, 0);
Expand Down Expand Up @@ -1826,8 +1824,6 @@ static jl_value_t *intersect_type_type(jl_value_t *x, jl_value_t *y, jl_stenv_t
static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
{
if (x == y) return y;
if (x == jl_ANY_flag) x = (jl_value_t*)jl_any_type;
if (y == jl_ANY_flag) y = (jl_value_t*)jl_any_type;
if (jl_is_typevar(x)) {
if (jl_is_typevar(y)) {
jl_varbinding_t *xx = lookup(e, (jl_tvar_t*)x);
Expand Down Expand Up @@ -2219,10 +2215,6 @@ JL_DLLEXPORT jl_svec_t *jl_env_from_type_intersection(jl_value_t *a, jl_value_t

static int eq_msp(jl_value_t *a, jl_value_t *b, jl_typeenv_t *env)
{
// equate ANY and Any for specificity purposes, #16153
if ((a == (jl_value_t*)jl_any_type && b == jl_ANY_flag) ||
(b == (jl_value_t*)jl_any_type && a == jl_ANY_flag))
return 1;
if (!(jl_is_type(a) || jl_is_typevar(a)) ||
!(jl_is_type(b) || jl_is_typevar(b)))
return jl_egal(a, b);
Expand Down Expand Up @@ -2508,8 +2500,8 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty
}

if (!invariant) {
if ((jl_datatype_t*)a == jl_any_type || a == jl_ANY_flag) return 0;
if ((jl_datatype_t*)b == jl_any_type || b == jl_ANY_flag) return 1;
if ((jl_datatype_t*)a == jl_any_type) return 0;
if ((jl_datatype_t*)b == jl_any_type) return 1;
}

if (jl_is_datatype(a) && jl_is_datatype(b)) {
Expand Down
7 changes: 3 additions & 4 deletions src/typemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extern "C" {
// compute whether the specificity of this type is equivalent to Any in the sort order
static int jl_is_any(jl_value_t *t1)
{
return (t1 == (jl_value_t*)jl_any_type || t1 == jl_ANY_flag ||
return (t1 == (jl_value_t*)jl_any_type ||
(jl_is_typevar(t1) &&
((jl_tvar_t*)t1)->ub == (jl_value_t*)jl_any_type));
}
Expand Down Expand Up @@ -69,7 +69,7 @@ static int sig_match_by_type_simple(jl_value_t **types, size_t n, jl_tupletype_t
return 0;
}
}
else if (decl == (jl_value_t*)jl_any_type || decl == jl_ANY_flag) {
else if (decl == (jl_value_t*)jl_any_type) {
}
else {
if (jl_is_type_type(a)) // decl is not Type, because it would be caught above
Expand Down Expand Up @@ -122,8 +122,7 @@ static inline int sig_match_simple(jl_value_t **args, size_t n, jl_value_t **sig
for (i = 0; i < lensig; i++) {
jl_value_t *decl = sig[i];
jl_value_t *a = args[i];
if (decl == (jl_value_t*)jl_any_type || decl == jl_ANY_flag ||
((jl_value_t*)jl_typeof(a) == decl)) {
if (decl == (jl_value_t*)jl_any_type || ((jl_value_t*)jl_typeof(a) == decl)) {
/*
we are only matching concrete types here, and those types are
hash-consed, so pointer comparison should work.
Expand Down
Loading

0 comments on commit fc7419d

Please sign in to comment.