Tag instructions in annotated scode
This commit is contained in:
parent
758f836bf6
commit
46c538b7bf
@ -18,8 +18,18 @@
|
|||||||
| switch_body
|
| switch_body
|
||||||
| tuple(). %% FATE instruction
|
| tuple(). %% FATE instruction
|
||||||
|
|
||||||
-type stype() :: tuple | boolean.
|
%% Annotated scode
|
||||||
-type maybe_scode() :: missing | scode().
|
-type scode_a() :: {switch, stype(), [maybe_scode_a()], maybe_scode_a()} %% last arg is catch-all
|
||||||
|
| switch_body
|
||||||
|
| {i, ann(), tuple()}. %% FATE instruction
|
||||||
|
|
||||||
|
-type ann() :: #{ live_in := vars(), live_out := vars() }.
|
||||||
|
-type var() :: {var, integer()}.
|
||||||
|
-type vars() :: ordsets:set(var()).
|
||||||
|
|
||||||
|
-type stype() :: tuple | boolean.
|
||||||
|
-type maybe_scode() :: missing | scode().
|
||||||
|
-type maybe_scode_a() :: missing | scode_a().
|
||||||
|
|
||||||
-define(TODO(What), error({todo, ?FILE, ?LINE, ?FUNCTION_NAME, What})).
|
-define(TODO(What), error({todo, ?FILE, ?LINE, ?FUNCTION_NAME, What})).
|
||||||
|
|
||||||
@ -271,7 +281,7 @@ pp_ann(Ind, [{switch, Type, Alts, Def} | Code]) ->
|
|||||||
pp_ann(Ind, Code)];
|
pp_ann(Ind, Code)];
|
||||||
pp_ann(Ind, [switch_body | Code]) ->
|
pp_ann(Ind, [switch_body | Code]) ->
|
||||||
[Ind, "SWITCH-BODY\n", pp_ann(Ind, Code)];
|
[Ind, "SWITCH-BODY\n", pp_ann(Ind, Code)];
|
||||||
pp_ann(Ind, [{#{ live_in := In, live_out := Out }, I} | Code]) ->
|
pp_ann(Ind, [{i, #{ live_in := In, live_out := Out }, I} | Code]) ->
|
||||||
Fmt = fun([]) -> "()";
|
Fmt = fun([]) -> "()";
|
||||||
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
|
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
|
||||||
end,
|
end,
|
||||||
@ -298,10 +308,10 @@ ann_writes([{switch, Type, Alts, Def} | Code], Writes, Acc) ->
|
|||||||
Writes1 = ordsets:union(Writes, ordsets:intersection([WritesDef | WritesAlts])),
|
Writes1 = ordsets:union(Writes, ordsets:intersection([WritesDef | WritesAlts])),
|
||||||
ann_writes(Code, Writes1, [{switch, Type, Alts1, Def1} | Acc]);
|
ann_writes(Code, Writes1, [{switch, Type, Alts1, Def1} | Acc]);
|
||||||
ann_writes([I | Code], Writes, Acc) ->
|
ann_writes([I | Code], Writes, Acc) ->
|
||||||
Ws = var_writes({#{}, I}),
|
Ws = var_writes(I),
|
||||||
Writes1 = ordsets:union(Writes, Ws),
|
Writes1 = ordsets:union(Writes, Ws),
|
||||||
Ann = #{ writes_in => Writes, writes_out => Writes1 },
|
Ann = #{ writes_in => Writes, writes_out => Writes1 },
|
||||||
ann_writes(Code, Writes1, [{Ann, I} | Acc]);
|
ann_writes(Code, Writes1, [{i, Ann, I} | Acc]);
|
||||||
ann_writes([], Writes, Acc) ->
|
ann_writes([], Writes, Acc) ->
|
||||||
{Acc, Writes}.
|
{Acc, Writes}.
|
||||||
|
|
||||||
@ -314,7 +324,7 @@ ann_reads([{switch, Type, Alts, Def} | Code], Reads, Acc) ->
|
|||||||
{Def1, ReadsDef} = ann_reads(Def, Reads, []),
|
{Def1, ReadsDef} = ann_reads(Def, Reads, []),
|
||||||
Reads1 = ordsets:union([Reads, ReadsDef | ReadsAlts]),
|
Reads1 = ordsets:union([Reads, ReadsDef | ReadsAlts]),
|
||||||
ann_reads(Code, Reads1, [{switch, Type, Alts1, Def1} | Acc]);
|
ann_reads(Code, Reads1, [{switch, Type, Alts1, Def1} | Acc]);
|
||||||
ann_reads([{Ann, I} | Code], Reads, Acc) ->
|
ann_reads([{i, Ann, I} | Code], Reads, Acc) ->
|
||||||
#{ writes_in := WritesIn, writes_out := WritesOut } = Ann,
|
#{ writes_in := WritesIn, writes_out := WritesOut } = Ann,
|
||||||
#{ read := Rs, write := W, pure := Pure } = attributes(I),
|
#{ read := Rs, write := W, pure := Pure } = attributes(I),
|
||||||
Reads1 =
|
Reads1 =
|
||||||
@ -328,7 +338,7 @@ ann_reads([{Ann, I} | Code], Reads, Acc) ->
|
|||||||
LiveIn = ordsets:intersection(Reads1, WritesIn),
|
LiveIn = ordsets:intersection(Reads1, WritesIn),
|
||||||
LiveOut = ordsets:intersection(Reads, WritesOut),
|
LiveOut = ordsets:intersection(Reads, WritesOut),
|
||||||
Ann1 = #{ live_in => LiveIn, live_out => LiveOut },
|
Ann1 = #{ live_in => LiveIn, live_out => LiveOut },
|
||||||
ann_reads(Code, Reads1, [{Ann1, I} | Acc]);
|
ann_reads(Code, Reads1, [{i, Ann1, I} | Acc]);
|
||||||
ann_reads([], Reads, Acc) -> {Acc, Reads}.
|
ann_reads([], Reads, Acc) -> {Acc, Reads}.
|
||||||
|
|
||||||
%% Instruction attributes: reads, writes and purity (pure means no side-effects
|
%% Instruction attributes: reads, writes and purity (pure means no side-effects
|
||||||
@ -453,7 +463,8 @@ attributes(I) ->
|
|||||||
'NOP' -> Pure(none, [])
|
'NOP' -> Pure(none, [])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
var_writes({_, I}) ->
|
var_writes({i, _, I}) -> var_writes(I);
|
||||||
|
var_writes(I) ->
|
||||||
#{ write := W } = attributes(I),
|
#{ write := W } = attributes(I),
|
||||||
case W of
|
case W of
|
||||||
{var, _} -> [W];
|
{var, _} -> [W];
|
||||||
@ -464,7 +475,7 @@ independent({switch, _, _, _}, _) -> false;
|
|||||||
independent(_, {switch, _, _, _}) -> false;
|
independent(_, {switch, _, _, _}) -> false;
|
||||||
independent(switch_body, _) -> true;
|
independent(switch_body, _) -> true;
|
||||||
independent(_, switch_body) -> true;
|
independent(_, switch_body) -> true;
|
||||||
independent({_, I}, {_, J}) ->
|
independent({i, _, I}, {i, _, J}) ->
|
||||||
#{ write := WI, read := RI, pure := PureI } = attributes(I),
|
#{ write := WI, read := RI, pure := PureI } = attributes(I),
|
||||||
#{ write := WJ, read := RJ, pure := PureJ } = attributes(J),
|
#{ write := WJ, read := RJ, pure := PureJ } = attributes(J),
|
||||||
|
|
||||||
@ -486,7 +497,7 @@ merge_ann(#{ live_in := LiveIn }, #{ live_out := LiveOut }) ->
|
|||||||
%% Swap two instructions. Precondition: the instructions are independent/2.
|
%% Swap two instructions. Precondition: the instructions are independent/2.
|
||||||
swap_instrs(I, switch_body) -> {switch_body, I};
|
swap_instrs(I, switch_body) -> {switch_body, I};
|
||||||
swap_instrs(switch_body, I) -> {I, switch_body};
|
swap_instrs(switch_body, I) -> {I, switch_body};
|
||||||
swap_instrs({#{ live_in := Live1 }, I}, {#{ live_in := Live2, live_out := Live3 }, J}) ->
|
swap_instrs({i, #{ live_in := Live1 }, I}, {i, #{ live_in := Live2, live_out := Live3 }, J}) ->
|
||||||
%% Since I and J are independent the J can't read or write anything in
|
%% Since I and J are independent the J can't read or write anything in
|
||||||
%% that I writes.
|
%% that I writes.
|
||||||
WritesI = ordsets:subtract(Live2, Live1),
|
WritesI = ordsets:subtract(Live2, Live1),
|
||||||
@ -494,8 +505,8 @@ swap_instrs({#{ live_in := Live1 }, I}, {#{ live_in := Live2, live_out := Live3
|
|||||||
#{ read := ReadsI } = attributes(I),
|
#{ read := ReadsI } = attributes(I),
|
||||||
ReadsJ = ordsets:subtract(Live2, ordsets:union(Live3, ReadsI)),
|
ReadsJ = ordsets:subtract(Live2, ordsets:union(Live3, ReadsI)),
|
||||||
Live2_ = ordsets:subtract(ordsets:union([Live1, Live2, Live3]), ordsets:union(WritesI, ReadsJ)),
|
Live2_ = ordsets:subtract(ordsets:union([Live1, Live2, Live3]), ordsets:union(WritesI, ReadsJ)),
|
||||||
{{#{ live_in => Live1, live_out => Live2_ }, J},
|
{{i, #{ live_in => Live1, live_out => Live2_ }, J},
|
||||||
{#{ live_in => Live2_, live_out => Live3 }, I}}.
|
{i, #{ live_in => Live2_, live_out => Live3 }, I}}.
|
||||||
|
|
||||||
live_in(R, #{ live_in := LiveIn }) -> ordsets:is_element(R, LiveIn).
|
live_in(R, #{ live_in := LiveIn }) -> ordsets:is_element(R, LiveIn).
|
||||||
live_out(R, #{ live_out := LiveOut }) -> ordsets:is_element(R, LiveOut).
|
live_out(R, #{ live_out := LiveOut }) -> ordsets:is_element(R, LiveOut).
|
||||||
@ -548,33 +559,33 @@ rules() ->
|
|||||||
].
|
].
|
||||||
|
|
||||||
%% Removing pushes that are immediately consumed.
|
%% Removing pushes that are immediately consumed.
|
||||||
r_push_consume({Ann1, {'PUSH', A}}, [{Ann2, {Op, R, ?a, B}} | Code]) when ?IsBinOp(Op) ->
|
r_push_consume({i, Ann1, {'PUSH', A}}, [{i, Ann2, {Op, R, ?a, B}} | Code]) when ?IsBinOp(Op) ->
|
||||||
{[{merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
{[{i, merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
||||||
r_push_consume({Ann1, {'PUSH', B}}, [{Ann2, {Op, R, A, ?a}} | Code]) when A /= ?a, ?IsBinOp(Op) ->
|
r_push_consume({i, Ann1, {'PUSH', B}}, [{i, Ann2, {Op, R, A, ?a}} | Code]) when A /= ?a, ?IsBinOp(Op) ->
|
||||||
{[{merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
{[{i, merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
||||||
r_push_consume({Ann1, {'PUSH', A}}, [{Ann2, {'STORE', R, ?a}} | Code]) ->
|
r_push_consume({i, Ann1, {'PUSH', A}}, [{Ann2, {'STORE', R, ?a}} | Code]) ->
|
||||||
{[{merge_ann(Ann1, Ann2), {'STORE', R, A}}], Code};
|
{[{i, merge_ann(Ann1, Ann2), {'STORE', R, A}}], Code};
|
||||||
r_push_consume({Ann1, {'PUSH', A}}, [{Ann2, {'POP', B}} | Code]) ->
|
r_push_consume({i, Ann1, {'PUSH', A}}, [{i, Ann2, {'POP', B}} | Code]) ->
|
||||||
case live_out(B, Ann2) of
|
case live_out(B, Ann2) of
|
||||||
true -> {[{merge_ann(Ann1, Ann2), {'STORE', B, A}}], Code};
|
true -> {[{i, merge_ann(Ann1, Ann2), {'STORE', B, A}}], Code};
|
||||||
false -> {[], Code}
|
false -> {[], Code}
|
||||||
end;
|
end;
|
||||||
%% Writing directly to memory instead of going through the accumulator.
|
%% Writing directly to memory instead of going through the accumulator.
|
||||||
r_push_consume({Ann1, {Op, ?a, A, B}}, [{Ann2, {'STORE', R, ?a}} | Code]) when ?IsBinOp(Op) ->
|
r_push_consume({i, Ann1, {Op, ?a, A, B}}, [{i, Ann2, {'STORE', R, ?a}} | Code]) when ?IsBinOp(Op) ->
|
||||||
{[{merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
{[{i, merge_ann(Ann1, Ann2), {Op, R, A, B}}], Code};
|
||||||
|
|
||||||
r_push_consume(_, _) -> false.
|
r_push_consume(_, _) -> false.
|
||||||
|
|
||||||
%% Changing PUSH A, DUPA to PUSH A, PUSH A enables further optimisations
|
%% Changing PUSH A, DUPA to PUSH A, PUSH A enables further optimisations
|
||||||
r_dup_to_push({Ann1, Push={'PUSH', _}}, [{Ann2, 'DUPA'} | Code]) ->
|
r_dup_to_push({i, Ann1, Push={'PUSH', _}}, [{i, Ann2, 'DUPA'} | Code]) ->
|
||||||
#{ live_in := LiveIn } = Ann1,
|
#{ live_in := LiveIn } = Ann1,
|
||||||
Ann1_ = Ann1#{ live_out => LiveIn },
|
Ann1_ = Ann1#{ live_out => LiveIn },
|
||||||
Ann2_ = Ann2#{ live_in => LiveIn },
|
Ann2_ = Ann2#{ live_in => LiveIn },
|
||||||
{[{Ann1_, Push}, {Ann2_, Push}], Code};
|
{[{i, Ann1_, Push}, {i, Ann2_, Push}], Code};
|
||||||
r_dup_to_push(_, _) -> false.
|
r_dup_to_push(_, _) -> false.
|
||||||
|
|
||||||
%% Move PUSH A past non-stack instructions.
|
%% Move PUSH A past non-stack instructions.
|
||||||
r_swap_push(Push = {_, {'PUSH', _}}, [I | Code]) ->
|
r_swap_push(Push = {i, _, {'PUSH', _}}, [I | Code]) ->
|
||||||
case independent(Push, I) of
|
case independent(Push, I) of
|
||||||
true ->
|
true ->
|
||||||
{I1, Push1} = swap_instrs(Push, I),
|
{I1, Push1} = swap_instrs(Push, I),
|
||||||
@ -584,7 +595,7 @@ r_swap_push(Push = {_, {'PUSH', _}}, [I | Code]) ->
|
|||||||
r_swap_push(_, _) -> false.
|
r_swap_push(_, _) -> false.
|
||||||
|
|
||||||
%% Match up writes to variables with instructions further down.
|
%% Match up writes to variables with instructions further down.
|
||||||
r_swap_write(I = {_, _}, [J | Code]) ->
|
r_swap_write(I = {i, _, _}, [J | Code]) ->
|
||||||
case {var_writes(I), independent(I, J)} of
|
case {var_writes(I), independent(I, J)} of
|
||||||
{[_], true} ->
|
{[_], true} ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
@ -611,14 +622,14 @@ r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
|||||||
r_swap_write(_, _, _) -> false.
|
r_swap_write(_, _, _) -> false.
|
||||||
|
|
||||||
%% Inline stores
|
%% Inline stores
|
||||||
r_inline_store(I = {_, {'STORE', R = {var, _}, A = {arg, _}}}, Code) ->
|
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A = {arg, _}}}, Code) ->
|
||||||
%% Not when A is var unless updating the annotations properly.
|
%% Not when A is var unless updating the annotations properly.
|
||||||
r_inline_store([I], R, A, Code);
|
r_inline_store([I], R, A, Code);
|
||||||
r_inline_store(_, _) -> false.
|
r_inline_store(_, _) -> false.
|
||||||
|
|
||||||
r_inline_store(Acc, R, A, [switch_body | Code]) ->
|
r_inline_store(Acc, R, A, [switch_body | Code]) ->
|
||||||
r_inline_store([switch_body | Acc], R, A, Code);
|
r_inline_store([switch_body | Acc], R, A, Code);
|
||||||
r_inline_store(Acc, R, A, [{Ann, I} | Code]) ->
|
r_inline_store(Acc, R, A, [{i, Ann, I} | Code]) ->
|
||||||
#{ write := W, pure := Pure } = attributes(I),
|
#{ write := W, pure := Pure } = attributes(I),
|
||||||
Inl = fun(X) when X == R -> A; (X) -> X end,
|
Inl = fun(X) when X == R -> A; (X) -> X end,
|
||||||
case not live_in(R, Ann) orelse not Pure orelse lists:member(W, [R, A]) of
|
case not live_in(R, Ann) orelse not Pure orelse lists:member(W, [R, A]) of
|
||||||
@ -626,18 +637,18 @@ r_inline_store(Acc, R, A, [{Ann, I} | Code]) ->
|
|||||||
false ->
|
false ->
|
||||||
case I of
|
case I of
|
||||||
{Op, S, B, C} when ?IsBinOp(Op), B == R orelse C == R ->
|
{Op, S, B, C} when ?IsBinOp(Op), B == R orelse C == R ->
|
||||||
Acc1 = [{Ann, {Op, S, Inl(B), Inl(C)}} | Acc],
|
Acc1 = [{i, Ann, {Op, S, Inl(B), Inl(C)}} | Acc],
|
||||||
case r_inline_store(Acc1, R, A, Code) of
|
case r_inline_store(Acc1, R, A, Code) of
|
||||||
false -> {lists:reverse(Acc1), Code};
|
false -> {lists:reverse(Acc1), Code};
|
||||||
{New, Rest} -> {New, Rest}
|
{New, Rest} -> {New, Rest}
|
||||||
end;
|
end;
|
||||||
_ -> r_inline_store([{Ann, I} | Acc], R, A, Code)
|
_ -> r_inline_store([{i, Ann, I} | Acc], R, A, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_inline_store(_Acc, _, _, _) -> false.
|
r_inline_store(_Acc, _, _, _) -> false.
|
||||||
|
|
||||||
%% Shortcut write followed by final read
|
%% Shortcut write followed by final read
|
||||||
r_one_shot_var({Ann1, {Op, R = {var, _}, A, B}}, [{Ann2, J} | Code]) when ?IsBinOp(Op) ->
|
r_one_shot_var({i, Ann1, {Op, R = {var, _}, A, B}}, [{i, Ann2, J} | Code]) when ?IsBinOp(Op) ->
|
||||||
Copy = case J of
|
Copy = case J of
|
||||||
{'PUSH', R} -> {write_to, ?a};
|
{'PUSH', R} -> {write_to, ?a};
|
||||||
{'STORE', S, R} -> {write_to, S};
|
{'STORE', S, R} -> {write_to, S};
|
||||||
@ -645,29 +656,29 @@ r_one_shot_var({Ann1, {Op, R = {var, _}, A, B}}, [{Ann2, J} | Code]) when ?IsBin
|
|||||||
end,
|
end,
|
||||||
case {live_out(R, Ann2), Copy} of
|
case {live_out(R, Ann2), Copy} of
|
||||||
{false, {write_to, X}} ->
|
{false, {write_to, X}} ->
|
||||||
{[{merge_ann(Ann1, Ann2), {Op, X, A, B}}], Code};
|
{[{i, merge_ann(Ann1, Ann2), {Op, X, A, B}}], Code};
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_one_shot_var(_, _) -> false.
|
r_one_shot_var(_, _) -> false.
|
||||||
|
|
||||||
%% Remove writes to dead variables
|
%% Remove writes to dead variables
|
||||||
r_write_to_dead_var({Ann, {Op, R = {var, _}, A, B}}, Code) when ?IsBinOp(Op) ->
|
r_write_to_dead_var({i, Ann, {Op, R = {var, _}, A, B}}, Code) when ?IsBinOp(Op) ->
|
||||||
case live_out(R, Ann) of
|
case live_out(R, Ann) of
|
||||||
false ->
|
false ->
|
||||||
%% Subtle: we still have to pop the stack if any of the arguments
|
%% Subtle: we still have to pop the stack if any of the arguments
|
||||||
%% came from there. In this case we pop to R, which we know is
|
%% came from there. In this case we pop to R, which we know is
|
||||||
%% unused.
|
%% unused.
|
||||||
{[{Ann, {'POP', R}} || X <- [A, B], X == ?a], Code};
|
{[{i, Ann, {'POP', R}} || X <- [A, B], X == ?a], Code};
|
||||||
true -> false
|
true -> false
|
||||||
end;
|
end;
|
||||||
r_write_to_dead_var({Ann, {'STORE', R = {var, _}, A}}, Code) when A /= ?a ->
|
r_write_to_dead_var({i, Ann, {'STORE', R = {var, _}, A}}, Code) when A /= ?a ->
|
||||||
case live_out(R, Ann) of
|
case live_out(R, Ann) of
|
||||||
false ->
|
false ->
|
||||||
case Code of
|
case Code of
|
||||||
[] -> {[], []};
|
[] -> {[], []};
|
||||||
[switch_body, {Ann1, I} | Code1] ->
|
[switch_body, {Ann1, I} | Code1] ->
|
||||||
{[], [switch_body, {merge_ann(Ann, Ann1), I} | Code1]};
|
{[], [switch_body, {i, merge_ann(Ann, Ann1), I} | Code1]};
|
||||||
[{Ann1, I} | Code1] ->
|
[{i, Ann1, I} | Code1] ->
|
||||||
{[], [{merge_ann(Ann, Ann1), I} | Code1]}
|
{[], [{merge_ann(Ann, Ann1), I} | Code1]}
|
||||||
end;
|
end;
|
||||||
true -> false
|
true -> false
|
||||||
@ -676,13 +687,14 @@ r_write_to_dead_var(_, _) -> false.
|
|||||||
|
|
||||||
|
|
||||||
%% Desugar and specialize and remove annotations
|
%% Desugar and specialize and remove annotations
|
||||||
|
-spec unannotate(scode_a()) -> scode().
|
||||||
unannotate(switch_body) -> [switch_body];
|
unannotate(switch_body) -> [switch_body];
|
||||||
unannotate({switch, Type, Alts, Def}) ->
|
unannotate({switch, Type, Alts, Def}) ->
|
||||||
[{switch, Type, [unannotate(A) || A <- Alts], unannotate(Def)}];
|
[{switch, Type, [unannotate(A) || A <- Alts], unannotate(Def)}];
|
||||||
unannotate(missing) -> missing;
|
unannotate(missing) -> missing;
|
||||||
unannotate(Code) when is_list(Code) ->
|
unannotate(Code) when is_list(Code) ->
|
||||||
lists:flatmap(fun unannotate/1, Code);
|
lists:flatmap(fun unannotate/1, Code);
|
||||||
unannotate({_Ann, I}) -> [I].
|
unannotate({i, _Ann, I}) -> [I].
|
||||||
|
|
||||||
%% Desugar and specialize
|
%% Desugar and specialize
|
||||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_code:inc()];
|
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_code:inc()];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user