Skip to content

Commit a6f43be

Browse files
committed
define Core.Closure and make compiler-generated closures subtype of it
Performance issues related to code containing closures are frequently discussed (e.g., #15276, #56561). Several approaches can be considered to address this problem, one of which involves re-inferring code containing closures (#56687). To implement this idea, it is necessary to determine whether a given piece of code includes a closure. However, there is currently no explicit mechanism for making this determination (although there are some code that checks whether the function name contains `"#"` for this purpose, but this is an ad hoc solution). To address this, this commit lays the foundation for future optimizations targeting closures by defining closure functions as a subtype of the new type `Core.Closure <: Function`. This change allows the optimizer to apply targeted optimizations to code containing calls to functions that are subtype of `Core.Closure`.
1 parent 8e9c59f commit a6f43be

10 files changed

+23
-6
lines changed

src/builtins.c

+1
Original file line numberDiff line numberDiff line change
@@ -2531,6 +2531,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
25312531
add_builtin("IntrinsicFunction", (jl_value_t*)jl_intrinsic_type);
25322532
add_builtin("Function", (jl_value_t*)jl_function_type);
25332533
add_builtin("Builtin", (jl_value_t*)jl_builtin_type);
2534+
add_builtin("Closure", (jl_value_t*)jl_closure_type);
25342535
add_builtin("MethodInstance", (jl_value_t*)jl_method_instance_type);
25352536
add_builtin("CodeInfo", (jl_value_t*)jl_code_info_type);
25362537
add_builtin("LLVMPtr", (jl_value_t*)jl_llvmpointer_type);

src/datatype.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,8 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(
843843
}
844844
else {
845845
tn = jl_new_typename_in((jl_sym_t*)name, module, abstract, mutabl);
846-
if (super == jl_function_type || super == jl_builtin_type || is_anonfn_typename(jl_symbol_name(name))) {
846+
if (super == jl_function_type || super == jl_builtin_type ||
847+
super == jl_closure_type || is_anonfn_typename(jl_symbol_name(name))) {
847848
// Callable objects (including compiler-generated closures) get independent method tables
848849
// as an optimization
849850
tn->mt = jl_new_method_table(name, module);

src/ircode.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1602,7 +1602,7 @@ void jl_init_serializer(void)
16021602
jl_pointer_type, jl_abstractarray_type, jl_nothing_type,
16031603
jl_vararg_type,
16041604
jl_densearray_type, jl_function_type, jl_typename_type,
1605-
jl_builtin_type, jl_task_type, jl_uniontype_type,
1605+
jl_builtin_type, jl_closure_type, jl_task_type, jl_uniontype_type,
16061606
jl_array_any_type, jl_intrinsic_type,
16071607
jl_methtable_type, jl_typemap_level_type,
16081608
jl_voidpointer_type, jl_newvarnode_type, jl_abstractstring_type,

src/jl_exported_data.inc

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
XX(jl_bottom_type) \
3030
XX(jl_boundserror_type) \
3131
XX(jl_builtin_type) \
32+
XX(jl_closure_type) \
3233
XX(jl_char_type) \
3334
XX(jl_code_info_type) \
3435
XX(jl_code_instance_type) \

src/jltypes.c

+2
Original file line numberDiff line numberDiff line change
@@ -3248,8 +3248,10 @@ void jl_init_types(void) JL_GC_DISABLED
32483248

32493249
jl_function_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Function"), core, jl_any_type, jl_emptysvec);
32503250
jl_builtin_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Builtin"), core, jl_function_type, jl_emptysvec);
3251+
jl_closure_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Closure"), core, jl_function_type, jl_emptysvec);
32513252
jl_function_type->name->mt = NULL; // subtypes of Function have independent method tables
32523253
jl_builtin_type->name->mt = NULL; // so they don't share the Any type table
3254+
jl_closure_type->name->mt = NULL;
32533255

32543256
jl_svec_t *tv;
32553257

src/julia-syntax.scm

+2-2
Original file line numberDiff line numberDiff line change
@@ -4232,8 +4232,8 @@ f(x) = yt(x)
42324232
(filter ssavalue? fieldtypes)))
42334233
(fieldnames (append closure-param-names (filter (lambda (v) (not (is-var-boxed? v lam))) capt-vars))))
42344234
(if (null? para)
4235-
(type-for-closure type-name capt-vars '(core Function))
4236-
(type-for-closure-parameterized type-name para fieldnames capt-vars fieldtypes '(core Function)))))
4235+
(type-for-closure type-name capt-vars '(core Closure))
4236+
(type-for-closure-parameterized type-name para fieldnames capt-vars fieldtypes '(core Closure)))))
42374237
(mk-method ;; expression to make the method
42384238
(if short '()
42394239
(let* ((iskw ;; TODO jb/functions need more robust version of this

src/julia.h

+1
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ extern JL_DLLIMPORT jl_unionall_t *jl_anytuple_type_type JL_GLOBALLY_ROOTED;
907907
extern JL_DLLIMPORT jl_datatype_t *jl_vararg_type JL_GLOBALLY_ROOTED;
908908
extern JL_DLLIMPORT jl_datatype_t *jl_function_type JL_GLOBALLY_ROOTED;
909909
extern JL_DLLIMPORT jl_datatype_t *jl_builtin_type JL_GLOBALLY_ROOTED;
910+
extern JL_DLLIMPORT jl_datatype_t *jl_closure_type JL_GLOBALLY_ROOTED;
910911
extern JL_DLLIMPORT jl_unionall_t *jl_opaque_closure_type JL_GLOBALLY_ROOTED;
911912
extern JL_DLLIMPORT jl_typename_t *jl_opaque_closure_typename JL_GLOBALLY_ROOTED;
912913

src/staticdata.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ extern "C" {
101101
// TODO: put WeakRefs on the weak_refs list during deserialization
102102
// TODO: handle finalizers
103103

104-
#define NUM_TAGS 195
104+
#define NUM_TAGS 196
105105

106106
// An array of references that need to be restored from the sysimg
107107
// This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C.
@@ -162,6 +162,7 @@ jl_value_t **const*const get_tags(void) {
162162
INSERT_TAG(jl_unionall_type);
163163
INSERT_TAG(jl_typename_type);
164164
INSERT_TAG(jl_builtin_type);
165+
INSERT_TAG(jl_closure_type);
165166
INSERT_TAG(jl_code_info_type);
166167
INSERT_TAG(jl_opaque_closure_type);
167168
INSERT_TAG(jl_task_type);

stdlib/Serialization/src/Serialization.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ function should_send_whole_type(s, t::DataType)
561561
name = tn.mt.name
562562
mod = tn.module
563563
isanonfunction = mod === Main && # only Main
564-
t.super === Function && # only Functions
564+
t.super === Core.Closure && # only Functions
565565
unsafe_load(unsafe_convert(Ptr{UInt8}, tn.name)) == UInt8('#') && # hidden type
566566
(!isdefined(mod, name) || t != typeof(getglobal(mod, name))) # XXX: 95% accurate test for this being an inner function
567567
# TODO: more accurate test? (tn.name !== "#" name)

test/core.jl

+10
Original file line numberDiff line numberDiff line change
@@ -8398,3 +8398,13 @@ f_call_me() = invoke(f_invoke_me, f_invoke_me_ci)
83988398
f_invalidate_me() = 2
83998399
@test_throws ErrorException invoke(f_invoke_me, f_invoke_me_ci)
84008400
@test_throws ErrorException f_call_me()
8401+
8402+
function genenerate_closure(argtypes::Vector{Any})
8403+
local argtypesi
8404+
@inline function closure()
8405+
argtypesi = @noinline copy(argtypes)
8406+
return length(argtypesi)
8407+
end
8408+
return closure
8409+
end
8410+
@test genenerate_closure(Any[]) isa Core.Closure

0 commit comments

Comments
 (0)