Skip to content

Commit

Permalink
Introduce a recursive/more aggressive allocation elimination pass
Browse files Browse the repository at this point in the history
The hardest part for running non-local optimization passes
(i.e. the transformation does not rely only on one or a few neighboring expressions)
is to avoid re-analyse the code. Our current IR, though easy for linear scanning,
interpreting, codegen and, to a certain degree, storage, is not very easy for making random
updates. Try to workaround this issue in two ways,

1. Never resize the code array when doing updates.
   Instead, inserting nested arrays that we'll later splice back in for code addition and
   use `nothing` for code deletion. This way, the array index we cached for other metadata
   about the code can stay valid.
2. Based on the previous approach, pre-scan the use-def info for all variables before starting
   the optimization and run the optimization recursively.
   Code changes will also update this use-def data so that it's always valid for the user.
   Changes that can affect the use or def of another value will re-trigger the optimization
   so that we can take advantage of new optimization opportunities.

This optimization pass should now handle most of the control-flow insensitive optimizations.
Code patterns that are handled partially by this pass but will benefit greatly from an
control-flow sensitive version includes,

1. Split slots (based on control flow)

   This way we can completely eliminate the surprising cost due to variable name conflicts,
   even when one of the def-use is not type stable.
   (This pass currently handles the case where all the def/uses are type stable)

2. Delay allocations

   There are cases where the allocation escapes but only in some branches.
   This will be especially for error path since we cannot eliminate some `SubArray` allocation
   only because we want to maintain them for the bounds error. This is very stupid and we should
   be able to do the allocation only when we throw the error, leaving the performance critical
   non-error path allocation-free.

3. Reordering assignments

   It is in general illegal to move an assignment when the slot assigned to is not SSA.
   However, there are many case that is actually legal
   (i.e. if there's no other use or def in between) to do so. This shows up a lot in code like

   ```
   SSA = alloc
   slot = SSA
   ```

   which we currently can't optimize since the slot can't see the assignment is actually
   an allocation and not a generic black box. We should be able to merge this and eliminate the
   SSA based on control flow info. For this case, a def info that looks through SSA values
   can also help.
  • Loading branch information
yuyichao authored and JeffBezanson committed Nov 16, 2017
1 parent 2c59cc1 commit ec20c0b
Show file tree
Hide file tree
Showing 8 changed files with 1,930 additions and 579 deletions.
2,478 changes: 1,916 additions & 562 deletions base/inference.jl

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion base/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,6 @@ precompile(Tuple{typeof(Core.Inference.record_slot_assign!), Core.Inference.Infe
precompile(Tuple{typeof(Core.Inference.type_annotate!), Core.Inference.InferenceState})
precompile(Tuple{typeof(Core.Inference.inlining_pass!), Core.Inference.InferenceState})
precompile(Tuple{typeof(Core.Inference.alloc_elim_pass!), Core.Inference.InferenceState})
precompile(Tuple{typeof(Core.Inference.getfield_elim_pass!), Core.Inference.InferenceState})
precompile(Tuple{typeof(Core.Inference.popmeta!), Array{Any, 1}, Symbol})
precompile(Tuple{typeof(Core.Inference.widen_all_consts!), CodeInfo})
precompile(Tuple{typeof(Core.Inference.stupdate!), Array{Any, 1}, Array{Any, 1}})
Expand Down
5 changes: 5 additions & 0 deletions src/rtutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, jl_value_t *expec
jl_type_error_rt(fname, "", expected, got);
}

JL_DLLEXPORT void JL_NORETURN jl_type_error_new_expr(jl_value_t *ty, jl_value_t *got)
{
jl_type_error_rt("Type", "new", ty, got);
}

JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var)
{
jl_throw(jl_new_struct(jl_undefvarerror_type, var));
Expand Down
6 changes: 1 addition & 5 deletions test/backtrace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,7 @@ for sfs in lkup
end
end
@test hasbt
if Base.JLOptions().can_inline != 0
@test_broken hasbt2
else
@test hasbt2
end
@test hasbt2

function btmacro()
ret = @timed backtrace()
Expand Down
4 changes: 1 addition & 3 deletions test/meta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ function foundfunc(bt, funcname)
end
false
end
if inlining_on
@test !foundfunc(h_inlined(), :g_inlined)
end
@test foundfunc(h_inlined(), :g_inlined)
@test foundfunc(h_noinlined(), :g_noinlined)

using Base.pushmeta!, Base.popmeta!
Expand Down
12 changes: 6 additions & 6 deletions test/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,19 @@ logging(kind=:error)


logging(DevNull, Logging, :bar)
@test sprint(Logging.bar) == ""
@test_broken sprint(Logging.bar) == ""
@test all(contains.(sprint(Logging.pooh), ["INFO: poohinfo", "WARNING: poohwarn", "ERROR: \"pooherror\""]))
@test all(contains.(sprint(foo), ["INFO: fooinfo", "WARNING: foowarn", "ERROR: \"fooerror\""]))

logging(DevNull, Logging)
@test sprint(Logging.bar) == ""
@test sprint(Logging.pooh) == ""
@test_broken sprint(Logging.bar) == ""
@test_broken sprint(Logging.pooh) == ""
@test all(contains.(sprint(foo), ["INFO: fooinfo", "WARNING: foowarn", "ERROR: \"fooerror\""]))

logging(DevNull)
@test sprint(Logging.bar) == ""
@test sprint(Logging.pooh) == ""
@test sprint(foo) == ""
@test_broken sprint(Logging.bar) == ""
@test_broken sprint(Logging.pooh) == ""
@test_broken sprint(foo) == ""

logging()
@test all(contains.(sprint(Logging.bar), ["INFO: barinfo", "WARNING: barwarn", "ERROR: \"barerror\""]))
Expand Down
1 change: 0 additions & 1 deletion test/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,3 @@ end
@test tryparse(Float64, "1.23") === Nullable(1.23)
@test tryparse(Float32, "1.23") === Nullable(1.23f0)
@test tryparse(Float16, "1.23") === Nullable(Float16(1.23))

2 changes: 1 addition & 1 deletion test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ f2_exprs = get_expr_list(@code_typed(f2(1))[1])
@test is_pop_loc(f2_exprs[end])
@test Meta.isexpr(f2_exprs[end - 1], :return)

if Base.JLOptions().code_coverage != 0 && Base.JLOptions().can_inline != 0
if Base.JLOptions().can_inline != 0
@test count_meta_loc(f1_exprs) == 1
@test count_meta_loc(f2_exprs) == 2
else
Expand Down

0 comments on commit ec20c0b

Please sign in to comment.