From 11fcf8beaeb051e964ef0f55cf6079a73558ae8f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 17 Oct 2020 17:56:06 -0400 Subject: [PATCH] Add a macro to opt into aggressive constprop Right now aggressive constprop is essentially tied to the inlining threshold (or to their name being `getproperty` or `setproperty!` respectively, which can be both somewhat brittle if the inlining cost changes and insufficient when you do really know that const prop would be beneficial even if the function is not inlineable. This adds a simple macro that can be used to manually annotate methods to force aggressive constprop on them. --- base/compiler/abstractinterpretation.jl | 2 +- base/expr.jl | 13 +++++++++++++ src/ast.c | 2 ++ src/dump.c | 2 ++ src/ircode.c | 4 +++- src/jltypes.c | 18 +++++++++++------- src/julia.h | 2 ++ src/julia_internal.h | 1 + src/method.c | 3 +++ test/core.jl | 12 ++++++++++++ 10 files changed, 50 insertions(+), 9 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index c82da9514afefa..c15287cc6c4c30 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -266,7 +266,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp istopfunction(f, :<<) || istopfunction(f, :>>)) return Any end - force_inference = allconst || InferenceParams(interp).aggressive_constant_propagation + force_inference = allconst || method.aggressive_constprop || InferenceParams(interp).aggressive_constant_propagation if istopfunction(f, :getproperty) || istopfunction(f, :setproperty!) force_inference = true end diff --git a/base/expr.jl b/base/expr.jl index ff5e92005b8dd0..dc36b53bdcc90d 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -242,6 +242,19 @@ macro pure(ex) esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex) end +""" + @aggressive_constprop ex + @aggressive_constprop(ex) + +`@aggressive_constprop` requests more aggressive interprocedural constant +propagation for the annotated function. For function where the return type +depends on the value of the arguments, this can yield improved inference results +at the cost of additional compile time. +""" +macro aggressive_constprop(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :aggressive_constprop) : ex) +end + """ @propagate_inbounds diff --git a/src/ast.c b/src/ast.c index 9f4ac6f934d9d5..224193ba52f3ae 100644 --- a/src/ast.c +++ b/src/ast.c @@ -56,6 +56,7 @@ jl_sym_t *static_parameter_sym; jl_sym_t *inline_sym; jl_sym_t *noinline_sym; jl_sym_t *generated_sym; jl_sym_t *generated_only_sym; jl_sym_t *isdefined_sym; jl_sym_t *propagate_inbounds_sym; jl_sym_t *specialize_sym; +jl_sym_t *aggressive_constprop_sym; jl_sym_t *nospecialize_sym; jl_sym_t *macrocall_sym; jl_sym_t *colon_sym; jl_sym_t *hygienicscope_sym; jl_sym_t *throw_undef_if_not_sym; jl_sym_t *getfield_undefref_sym; @@ -381,6 +382,7 @@ void jl_init_common_symbols(void) noinline_sym = jl_symbol("noinline"); polly_sym = jl_symbol("polly"); propagate_inbounds_sym = jl_symbol("propagate_inbounds"); + aggressive_constprop_sym = jl_symbol("aggressive_constprop"); isdefined_sym = jl_symbol("isdefined"); nospecialize_sym = jl_symbol("nospecialize"); specialize_sym = jl_symbol("specialize"); diff --git a/src/dump.c b/src/dump.c index 287bf2d44701bb..f694c488d774fc 100644 --- a/src/dump.c +++ b/src/dump.c @@ -594,6 +594,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li write_int32(s->s, m->nkw); write_int8(s->s, m->isva); write_int8(s->s, m->pure); + write_int8(s->s, m->aggressive_constprop); jl_serialize_value(s, (jl_value_t*)m->slot_syms); jl_serialize_value(s, (jl_value_t*)m->roots); jl_serialize_value(s, (jl_value_t*)m->ccallable); @@ -1410,6 +1411,7 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ m->nkw = read_int32(s->s); m->isva = read_int8(s->s); m->pure = read_int8(s->s); + m->aggressive_constprop = read_int8(s->s); m->slot_syms = jl_deserialize_value(s, (jl_value_t**)&m->slot_syms); jl_gc_wb(m, m->slot_syms); m->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->roots); diff --git a/src/ircode.c b/src/ircode.c index 62e38e72697f6e..10a4b843856704 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -702,7 +702,8 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) jl_get_ptls_states() }; - uint8_t flags = (code->inferred << 3) + uint8_t flags = (code->aggressive_constprop << 4) + | (code->inferred << 3) | (code->inlineable << 2) | (code->propagate_inbounds << 1) | (code->pure << 0); @@ -787,6 +788,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t jl_code_info_t *code = jl_new_code_info_uninit(); uint8_t flags = read_uint8(s.s); + code->aggressive_constprop = !!(flags & (1 << 4)); code->inferred = !!(flags & (1 << 3)); code->inlineable = !!(flags & (1 << 2)); code->propagate_inbounds = !!(flags & (1 << 1)); diff --git a/src/jltypes.c b/src/jltypes.c index 31a06e2bdc0929..2edcaf38a7708d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2239,7 +2239,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_info_type = jl_new_datatype(jl_symbol("CodeInfo"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(18, + jl_perm_symsvec(19, "code", "codelocs", "ssavaluetypes", @@ -2257,8 +2257,9 @@ void jl_init_types(void) JL_GC_DISABLED "inferred", "inlineable", "propagate_inbounds", - "pure"), - jl_svec(18, + "pure", + "aggressive_constprop"), + jl_svec(19, jl_array_any_type, jl_array_int32_type, jl_any_type, @@ -2276,13 +2277,14 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_bool_type, jl_bool_type, + jl_bool_type, jl_bool_type), - 0, 1, 18); + 0, 1, 19); jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(22, + jl_perm_symsvec(23, "name", "module", "file", @@ -2304,8 +2306,9 @@ void jl_init_types(void) JL_GC_DISABLED "nospecialize", "nkw", "isva", - "pure"), - jl_svec(22, + "pure", + "aggressive_constprop"), + jl_svec(23, jl_symbol_type, jl_module_type, jl_symbol_type, @@ -2327,6 +2330,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_int32_type, jl_int32_type, jl_bool_type, + jl_bool_type, jl_bool_type), 0, 1, 10); diff --git a/src/julia.h b/src/julia.h index 73cac24dee6dd4..00e841457659c7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -288,6 +288,7 @@ typedef struct _jl_code_info_t { uint8_t inlineable; uint8_t propagate_inbounds; uint8_t pure; + uint8_t aggressive_constprop; } jl_code_info_t; // This type describes a single method definition, and stores data @@ -327,6 +328,7 @@ typedef struct _jl_method_t { // of another method. uint8_t isva; uint8_t pure; + uint8_t aggressive_constprop; // hidden fields: // lock for modifications to the method diff --git a/src/julia_internal.h b/src/julia_internal.h index 369781b79bed3e..37eb05d7733290 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1256,6 +1256,7 @@ extern jl_sym_t *static_parameter_sym; extern jl_sym_t *inline_sym; extern jl_sym_t *noinline_sym; extern jl_sym_t *generated_sym; extern jl_sym_t *generated_only_sym; extern jl_sym_t *isdefined_sym; extern jl_sym_t *propagate_inbounds_sym; extern jl_sym_t *specialize_sym; +extern jl_sym_t *aggressive_constprop_sym; extern jl_sym_t *nospecialize_sym; extern jl_sym_t *macrocall_sym; extern jl_sym_t *colon_sym; extern jl_sym_t *hygienicscope_sym; extern jl_sym_t *throw_undef_if_not_sym; extern jl_sym_t *getfield_undefref_sym; diff --git a/src/method.c b/src/method.c index 5f4a954f882b89..67da78bb594f5c 100644 --- a/src/method.c +++ b/src/method.c @@ -254,6 +254,8 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) li->inlineable = 1; else if (ma == (jl_value_t*)propagate_inbounds_sym) li->propagate_inbounds = 1; + else if (ma == (jl_value_t*)aggressive_constprop_sym) + li->aggressive_constprop = 1; else jl_array_ptr_set(meta, ins++, ma); } @@ -512,6 +514,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) } m->called = called; m->pure = src->pure; + m->aggressive_constprop = src->aggressive_constprop; jl_add_function_name_to_lineinfo(src, (jl_value_t*)m->name); jl_array_t *copy = NULL; diff --git a/test/core.jl b/test/core.jl index 7a265c38e3457b..5e6252c6173c4a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7508,3 +7508,15 @@ let array = Int[] end @test compare_union37557(Ref{Union{Int,Vector{Int}}}(1), Ref{Union{Int,Vector{Int}}}(1)) + +# @aggressive_constprop ====== +g_nonaggressive(y, x) = (for i = 1:100; end; Val{x}()) +@Base.aggressive_constprop g_aggressive(y, x) = (for i = 1:100; end; Val{x}()) + +f_nonaggressive(x) = g_nonaggressive(x, 1) +f_aggressive(x) = g_aggressive(x, 1) + +# The first test just makes sure that improvements to the compiler don't +# render the annotation effectless. +@test Base.return_types(f_nonaggressive, Tuple{Int})[1] == Val +@test Base.return_types(f_aggressive, Tuple{Int})[1] == Val{1}