1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
-module(hipe_llvm_liveness).
-export([analyze/1]).
%% @doc Find gc roots and explicitly mark when they go out of scope, based
%% on the liveness analyzis performed by the hipe_rtl_liveness:analyze/1.
analyze(RtlCfg) ->
Liveness = hipe_rtl_liveness:analyze(RtlCfg),
Roots = find_roots(RtlCfg, Liveness),
%% erlang:display(Roots),
NewRtlCfg = mark_dead_roots(RtlCfg, Liveness, Roots),
{NewRtlCfg, Roots}.
%% @doc Determine which are the GC Roots.Possible roots are all
%% RTL variables (rtl_var). However, since safe points are function calls, we
%% consider as possible GC roots only RTL variables that are live around
%% function calls.
find_roots(Cfg, Liveness) ->
Labels = hipe_rtl_cfg:postorder(Cfg),
Roots = find_roots_bb(Labels, Cfg, Liveness, []),
lists:usort(lists:flatten(Roots)).
find_roots_bb([], _Cfg, _Liveness, RootAcc) ->
RootAcc;
find_roots_bb([L|Ls], Cfg, Liveness, RootAcc) ->
Block = hipe_rtl_cfg:bb(Cfg, L),
BlockCode = hipe_bb:code(Block),
LiveIn = ordsets:from_list(strip(hipe_rtl_liveness:livein(Liveness, L))),
LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
Roots = do_find_roots_bb(BlockCode, L, LiveOut, LiveIn, []),
find_roots_bb(Ls, Cfg, Liveness, Roots++RootAcc).
%% For each call inside a BB the GC roots are those RTL variables that
%% are live before and after the call.
%% --> Live Before Call: These are the RTL variables that belong to the
%% LiveIn list or are initialized inside the BB before the call
%% --> Live After Call: These are the RTL variables that belong to the
%% LiveOut list or are used after the call inside the BB (they die
%% inside the BB and so do not belong to the LiveOut list)
do_find_roots_bb([], _Label, _LiveOut, _LiveBefore, RootAcc) ->
RootAcc;
do_find_roots_bb([I|Is], L, LiveOut, LiveBefore, RootAcc) ->
case hipe_rtl:is_call(I) of
true ->
%% Used inside the BB after the call
UsedAfterCall_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
UsedAfterCall = ordsets:from_list(UsedAfterCall_),
LiveAfter = ordsets:union(UsedAfterCall, LiveOut),
%% The Actual Roots
Roots = ordsets:intersection(LiveBefore, LiveAfter),
%% The result of the instruction
Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
LiveBefore1 = ordsets:union(LiveBefore, Defines),
do_find_roots_bb(Is, L, LiveOut, LiveBefore1, [Roots|RootAcc]);
false ->
%% The result of the instruction
Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
LiveBefore1 = ordsets:union(LiveBefore, Defines),
do_find_roots_bb(Is, L, LiveOut, LiveBefore1, RootAcc)
end.
%% @doc This function is responsible for marking when GC Roots, which can be
%% only RTL variables go out of scope (dead). This pass is needed for the LLVM
%% back end because the LLVM framework forces us to explicit mark when gc roots
%% are no longer live.
mark_dead_roots(CFG, Liveness, Roots) ->
Labels = hipe_rtl_cfg:postorder(CFG),
mark_dead_bb(Labels, CFG, Liveness, Roots).
mark_dead_bb([], Cfg, _Liveness, _Roots) ->
Cfg;
mark_dead_bb([L|Ls], Cfg, Liveness, Roots) ->
Block = hipe_rtl_cfg:bb(Cfg, L),
BlockCode = hipe_bb:code(Block),
LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
NewBlockCode = do_mark_dead_bb(BlockCode, LiveOut, Roots, []),
%% Update the CFG
NewBB = hipe_bb:code_update(Block, NewBlockCode),
NewCFG = hipe_rtl_cfg:bb_add(Cfg, L, NewBB),
mark_dead_bb(Ls, NewCFG, Liveness, Roots).
do_mark_dead_bb([], _LiveOut, _Roots, NewBlockCode) ->
lists:reverse(NewBlockCode);
do_mark_dead_bb([I|Is], LiveOut ,Roots, NewBlockCode) ->
Uses = ordsets:from_list(strip(hipe_rtl:uses(I))),
%% GC roots that are used in this instruction
RootsUsed = ordsets:intersection(Roots, Uses),
UsedAfter_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
UsedAfter = ordsets:from_list(UsedAfter_),
%% GC roots that are live after this instruction
LiveAfter = ordsets:union(LiveOut, UsedAfter),
%% GC roots that their last use is in this instruction
DeadRoots = ordsets:subtract(RootsUsed, LiveAfter),
%% Recreate the RTL variable from the corresponding Index
OldVars = [hipe_rtl:mk_var(V1) || V1 <- DeadRoots],
%% Mark the RTL variable as DEAD (last use)
NewVars = [kill_var(V2) || V2 <- OldVars],
%% Create a list with the substitution of the old vars with the new
%% ones which are marked with the dead keyword
Subtitution = lists:zip(OldVars, NewVars),
NewI = case Subtitution of
[] -> I;
_ -> hipe_rtl:subst_uses_llvm(Subtitution, I)
end,
do_mark_dead_bb(Is, LiveOut, Roots, [NewI|NewBlockCode]).
%% Update the liveness of a var,in order to mark that this is the last use.
kill_var(Var) -> hipe_rtl:var_liveness_update(Var, dead).
%% We are only interested for rtl_vars, since only rtl_vars are possible gc
%% roots.
strip(L) -> [Y || {rtl_var, Y, _} <- L].
|