Rewrite liveness analysis
This commit is contained in:
parent
4976e0402e
commit
2d7c860e3a
@ -653,73 +653,52 @@ pp_arg(?a) -> "a".
|
|||||||
%% -- Analysis --
|
%% -- Analysis --
|
||||||
|
|
||||||
annotate_code(Code) ->
|
annotate_code(Code) ->
|
||||||
{WCode, _} = ann_writes(Code, ordsets:new(), []),
|
annotate_code(5, [], Code).
|
||||||
{RCode, _} = ann_reads(WCode, ordsets:new(), []),
|
|
||||||
RCode.
|
|
||||||
|
|
||||||
%% Reverses the code
|
annotate_code(Fuel, LiveTop, Code) ->
|
||||||
ann_writes(missing, Writes, []) -> {missing, Writes};
|
{Code1, LiveIn} = ann_live(LiveTop, Code, []),
|
||||||
ann_writes([switch_body | Code], Writes, Acc) ->
|
case LiveIn == LiveTop of
|
||||||
ann_writes(Code, Writes, [switch_body | Acc]);
|
true -> Code1;
|
||||||
ann_writes([{switch, Arg, Type, Alts, Def} | Code], Writes, Acc) ->
|
|
||||||
{Alts1, WritesAlts} = lists:unzip([ ann_writes(Alt, Writes, []) || Alt <- Alts ]),
|
|
||||||
{Def1, WritesDef} = ann_writes(Def, Writes, []),
|
|
||||||
Writes1 = ordsets:union(Writes, ordsets:intersection([WritesDef | WritesAlts])),
|
|
||||||
ann_writes(Code, Writes1, [{switch, Arg, Type, Alts1, Def1} | Acc]);
|
|
||||||
ann_writes([I | Code], Writes, Acc) ->
|
|
||||||
Ws = [ W || W <- var_writes(I) ],
|
|
||||||
Writes1 = ordsets:union(Writes, Ws),
|
|
||||||
Ann = #{ writes_in => Writes, writes_out => Writes1 },
|
|
||||||
ann_writes(Code, Writes1, [{i, Ann, I} | Acc]);
|
|
||||||
ann_writes([], Writes, Acc) ->
|
|
||||||
{Acc, Writes}.
|
|
||||||
|
|
||||||
%% Takes reversed code and unreverses it.
|
|
||||||
ann_reads(missing, Reads, []) -> {missing, Reads};
|
|
||||||
ann_reads([switch_body | Code], Reads, Acc) ->
|
|
||||||
ann_reads(Code, Reads, [switch_body | Acc]);
|
|
||||||
ann_reads([{switch, Arg, Type, Alts, Def} | Code], Reads, Acc) ->
|
|
||||||
{Alts1, ReadsAlts} = lists:unzip([ ann_reads(Alt, Reads, []) || Alt <- Alts ]),
|
|
||||||
{Def1, ReadsDef} = ann_reads(Def, Reads, []),
|
|
||||||
Reads1 = ordsets:union([[Arg], Reads, ReadsDef | ReadsAlts]),
|
|
||||||
ann_reads(Code, Reads1, [{switch, Arg, Type, Alts1, Def1} | Acc]);
|
|
||||||
ann_reads([{i, _Ann, loop} | Code], _Reads, Acc) ->
|
|
||||||
ann_reads_loop(10, Code, [], Acc);
|
|
||||||
ann_reads([{i, _Ann, I} | Code], Reads, Acc) ->
|
|
||||||
#{ read := Rs0, write := W, pure := Pure } = attributes(I),
|
|
||||||
IsReg = fun({immediate, _}) -> false;
|
|
||||||
(?a) -> false;
|
|
||||||
(_) -> true end,
|
|
||||||
Rs = lists:filter(IsReg, Rs0),
|
|
||||||
%% If we write it here it's not live in (unless we also read it)
|
|
||||||
Reads1 = Reads -- [W],
|
|
||||||
Reads2 =
|
|
||||||
case {W, Pure andalso not ordsets:is_element(W, Reads)} of
|
|
||||||
%% This is a little bit dangerous: if writing to a dead variable, we ignore
|
|
||||||
%% the reads. Relies on dead writes to be removed by the
|
|
||||||
%% optimisations below (r_write_to_dead_var).
|
|
||||||
{{var, _}, true} -> Reads1;
|
|
||||||
_ -> ordsets:union(Reads1, Rs)
|
|
||||||
end,
|
|
||||||
LiveIn = Reads2, % For well-formed code this should be a subset of WritesIn
|
|
||||||
LiveOut = Reads, % and this of WritesOut,
|
|
||||||
Ann1 = #{ live_in => LiveIn, live_out => LiveOut },
|
|
||||||
ann_reads(Code, Reads2, [{i, Ann1, I} | Acc]);
|
|
||||||
ann_reads([], Reads, Acc) -> {Acc, Reads}.
|
|
||||||
|
|
||||||
ann_reads_loop(Fuel, Code, Reads, Acc) ->
|
|
||||||
Ann1 = #{ live_in => Reads, live_out => [] },
|
|
||||||
{Acc1, Reads1} = ann_reads(Code, Reads, [{i, Ann1, loop} | Acc]),
|
|
||||||
case Reads1 == Reads of
|
|
||||||
true -> {Acc1, Reads1};
|
|
||||||
false when Fuel =< 0 ->
|
false when Fuel =< 0 ->
|
||||||
io:format("WARNING: Loop analysis fuel exhausted!\n"),
|
aeso_errors:throw(aeso_code_errors:format(liveness_analysis_out_of_fuel));
|
||||||
{Acc1, Reads1};
|
false -> annotate_code(Fuel - 1, LiveIn, Code)
|
||||||
false -> ann_reads_loop(Fuel - 1, Code, Reads1, Acc)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Instruction attributes: reads, writes and purity (pure means no side-effects
|
ann_live(_LiveTop, missing, _LiveOut) -> {missing, []};
|
||||||
%% aside from the reads and writes).
|
ann_live(_LiveTop, [], LiveOut) -> {[], LiveOut};
|
||||||
|
ann_live(LiveTop, [I | Is], LiveOut) ->
|
||||||
|
{Is1, LiveMid} = ann_live(LiveTop, Is, LiveOut),
|
||||||
|
{I1, LiveIn} = ann_live1(LiveTop, I, LiveMid),
|
||||||
|
{[I1 | Is1], LiveIn}.
|
||||||
|
|
||||||
|
ann_live1(_LiveTop, switch_body, LiveOut) ->
|
||||||
|
{switch_body, LiveOut};
|
||||||
|
ann_live1(LiveTop, loop, _LiveOut) ->
|
||||||
|
Ann = #{ live_in => LiveTop, live_out => [] },
|
||||||
|
{{i, Ann, loop}, LiveTop};
|
||||||
|
ann_live1(LiveTop, {switch, Arg, Type, Alts, Def}, LiveOut) ->
|
||||||
|
Read = [Arg || is_reg(Arg)],
|
||||||
|
{Alts1, LiveAlts} = lists:unzip([ ann_live(LiveTop, Alt, LiveOut) || Alt <- Alts ]),
|
||||||
|
{Def1, LiveDef} = ann_live(LiveTop, Def, LiveOut),
|
||||||
|
LiveIn = ordsets:union([Read, LiveDef | LiveAlts]),
|
||||||
|
{{switch, Arg, Type, Alts1, Def1}, LiveIn};
|
||||||
|
ann_live1(_LiveTop, I, LiveOut) ->
|
||||||
|
#{ read := Reads0, write := W } = attributes(I),
|
||||||
|
Reads = lists:filter(fun is_reg/1, Reads0),
|
||||||
|
%% If we write it here it's not live in (unless we also read it)
|
||||||
|
LiveIn = ordsets:union(LiveOut -- [W], Reads),
|
||||||
|
Ann = #{ live_in => LiveIn, live_out => LiveOut },
|
||||||
|
{{i, Ann, I}, LiveIn}.
|
||||||
|
|
||||||
|
is_reg(?a) -> false;
|
||||||
|
is_reg(none) -> false;
|
||||||
|
is_reg(pc) -> false;
|
||||||
|
is_reg({immediate, _}) -> false;
|
||||||
|
is_reg({arg, _}) -> true;
|
||||||
|
is_reg({store, _}) -> true;
|
||||||
|
is_reg({var, _}) -> true.
|
||||||
|
|
||||||
|
%% Instruction attributes: reads, writes and purity (pure means no writing to the chain).
|
||||||
attributes(I) ->
|
attributes(I) ->
|
||||||
Set = fun(L) when is_list(L) -> ordsets:from_list(L);
|
Set = fun(L) when is_list(L) -> ordsets:from_list(L);
|
||||||
(X) -> ordsets:from_list([X]) end,
|
(X) -> ordsets:from_list([X]) end,
|
||||||
@ -1196,6 +1175,7 @@ pick_branch(_Type, _V, _Alts) ->
|
|||||||
r_inline_switch_target(Store = {i, _, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
r_inline_switch_target(Store = {i, _, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
||||||
Switch = {switch, A, Type, Alts, Def},
|
Switch = {switch, A, Type, Alts, Def},
|
||||||
case R of
|
case R of
|
||||||
|
A -> false;
|
||||||
?a -> {[Switch], Code};
|
?a -> {[Switch], Code};
|
||||||
{var, _} ->
|
{var, _} ->
|
||||||
case lists:any(fun(Alt) -> live_in(R, Alt) end, [Def | Alts]) of
|
case lists:any(fun(Alt) -> live_in(R, Alt) end, [Def | Alts]) of
|
||||||
|
Loading…
x
Reference in New Issue
Block a user