Merge pull request #132 from aeternity/fate-compiler-optimizations
Fate compiler optimizations
This commit is contained in:
commit
9eed18f812
@ -114,13 +114,16 @@
|
||||
Op =:= 'CREATOR' orelse
|
||||
false)).
|
||||
|
||||
-record(env, { contract, vars = [], locals = [], tailpos = true }).
|
||||
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true }).
|
||||
|
||||
%% -- Debugging --------------------------------------------------------------
|
||||
|
||||
debug(Tag, Options, Fmt, Args) ->
|
||||
is_debug(Tag, Options) ->
|
||||
Tags = proplists:get_value(debug, Options, []),
|
||||
case Tags == all orelse lists:member(Tag, Tags) of
|
||||
Tags == all orelse lists:member(Tag, Tags).
|
||||
|
||||
debug(Tag, Options, Fmt, Args) ->
|
||||
case is_debug(Tag, Options) of
|
||||
true -> io:format(Fmt, Args);
|
||||
false -> ok
|
||||
end.
|
||||
@ -154,10 +157,10 @@ functions_to_scode(ContractName, Functions, Options) ->
|
||||
attrs := Attrs,
|
||||
return := Type}} <- maps:to_list(Functions)]).
|
||||
|
||||
function_to_scode(ContractName, Functions, _Name, Attrs0, Args, Body, ResType, _Options) ->
|
||||
function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) ->
|
||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
||||
SCode = to_scode(init_env(ContractName, Functions, Args), Body),
|
||||
SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body),
|
||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
||||
|
||||
-define(tvars, '$tvars').
|
||||
@ -199,10 +202,11 @@ type_to_scode({tvar, X}) ->
|
||||
|
||||
%% -- Environment functions --
|
||||
|
||||
init_env(ContractName, FunNames, Args) ->
|
||||
init_env(ContractName, FunNames, Name, Args) ->
|
||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||
contract = ContractName,
|
||||
locals = FunNames,
|
||||
current_function = Name,
|
||||
tailpos = true }.
|
||||
|
||||
next_var(#env{ vars = Vars }) ->
|
||||
@ -305,6 +309,24 @@ to_scode(Env, {'let', X, Expr, Body}) ->
|
||||
aeb_fate_ops:store({var, I}, {stack, 0}),
|
||||
to_scode(Env1, Body) ];
|
||||
|
||||
to_scode(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
|
||||
%% Tail-call to current function, f(e0..en). Compile to
|
||||
%% [ let xi = ei ]
|
||||
%% [ STORE argi xi ]
|
||||
%% jump 0
|
||||
{Vars, Code, _Env} =
|
||||
lists:foldl(fun(Arg, {Is, Acc, Env1}) ->
|
||||
{I, Env2} = bind_local("_", Env1),
|
||||
ArgCode = to_scode(notail(Env2), Arg),
|
||||
Acc1 = [Acc, ArgCode,
|
||||
aeb_fate_ops:store({var, I}, ?a)],
|
||||
{[I | Is], Acc1, Env2}
|
||||
end, {[], [], Env}, Args),
|
||||
[ Code,
|
||||
[ aeb_fate_ops:store({arg, I}, {var, J})
|
||||
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
||||
lists:reverse(Vars)) ],
|
||||
loop ];
|
||||
to_scode(Env, {def, Fun, Args}) ->
|
||||
FName = make_function_id(Fun),
|
||||
Lbl = aeb_fate_data:make_string(FName),
|
||||
@ -662,12 +684,15 @@ pp_ann(Ind, [{i, #{ live_in := In, live_out := Out }, I} | Code]) ->
|
||||
Fmt = fun([]) -> "()";
|
||||
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
|
||||
end,
|
||||
Op = [Ind, aeb_fate_pp:format_op(I, #{})],
|
||||
Op = [Ind, pp_op(I)],
|
||||
Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []],
|
||||
[io_lib:format("~-40s~s\n", [Op, Ann]),
|
||||
pp_ann(Ind, Code)];
|
||||
pp_ann(_, []) -> [].
|
||||
|
||||
pp_op(loop) -> "LOOP";
|
||||
pp_op(I) ->
|
||||
aeb_fate_pp:format_op(I, #{}).
|
||||
|
||||
pp_arg(?i(I)) -> io_lib:format("~w", [I]);
|
||||
pp_arg({arg, N}) -> io_lib:format("arg~p", [N]);
|
||||
@ -734,6 +759,7 @@ attributes(I) ->
|
||||
Pure = fun(W, R) -> Attr(W, R, true) end,
|
||||
Impure = fun(W, R) -> Attr(W, R, false) end,
|
||||
case I of
|
||||
loop -> Impure(pc, []);
|
||||
'RETURN' -> Impure(pc, []);
|
||||
{'RETURNR', A} -> Impure(pc, A);
|
||||
{'CALL', _} -> Impure(?a, []);
|
||||
@ -936,15 +962,29 @@ simpl_s({switch, Arg, Type, Alts, Def}, Options) ->
|
||||
{switch, Arg, Type, [simplify(A, Options) || A <- Alts], simplify(Def, Options)};
|
||||
simpl_s(I, _) -> I.
|
||||
|
||||
simpl_top(I, Code, Options) ->
|
||||
apply_rules(rules(), I, Code, Options).
|
||||
%% Safe-guard against loops in the rewriting. Shouldn't happen so throw an
|
||||
%% error if we run out.
|
||||
-define(SIMPL_FUEL, 5000).
|
||||
|
||||
apply_rules(Rules, I, Code, Options) ->
|
||||
Cons = fun(X, Xs) -> simpl_top(X, Xs, Options) end,
|
||||
simpl_top(I, Code, Options) ->
|
||||
simpl_top(?SIMPL_FUEL, I, Code, Options).
|
||||
|
||||
simpl_top(0, I, Code, _Options) ->
|
||||
error({out_of_fuel, I, Code});
|
||||
simpl_top(Fuel, I, Code, Options) ->
|
||||
apply_rules(Fuel, rules(), I, Code, Options).
|
||||
|
||||
apply_rules(Fuel, Rules, I, Code, Options) ->
|
||||
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
||||
case apply_rules_once(Rules, I, Code) of
|
||||
false -> [I | Code];
|
||||
{RName, New, Rest} ->
|
||||
debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", [I | Code]), pp_ann(" ", New ++ Rest)]),
|
||||
case is_debug(opt_rules, Options) of
|
||||
true ->
|
||||
{OldCode, NewCode} = drop_common_suffix([I | Code], New ++ Rest),
|
||||
debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", OldCode), pp_ann(" ", NewCode)]);
|
||||
false -> ok
|
||||
end,
|
||||
lists:foldr(Cons, Rest, New)
|
||||
end.
|
||||
|
||||
@ -968,6 +1008,7 @@ merge_rules() ->
|
||||
rules() ->
|
||||
merge_rules() ++
|
||||
[?RULE(r_swap_push),
|
||||
?RULE(r_swap_pop),
|
||||
?RULE(r_swap_write),
|
||||
?RULE(r_constant_propagation),
|
||||
?RULE(r_prune_impossible_branches),
|
||||
@ -1005,11 +1046,12 @@ inline_push(Ann1, Arg, Stack, [{i, Ann2, I} = AI | Code], Acc) ->
|
||||
{As0, As1} = split_stack_arg(Stack, As),
|
||||
Acc1 = [{i, merge_ann(Ann1, Ann2), from_op_view(Op, R, As0 ++ [Arg] ++ As1)} | Acc],
|
||||
{lists:reverse(Acc1), Code};
|
||||
false ->
|
||||
false when Arg /= R ->
|
||||
{AI1, {i, Ann1b, _}} = swap_instrs({i, Ann1, {'STORE', ?a, Arg}}, AI),
|
||||
inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc])
|
||||
inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc]);
|
||||
false -> false
|
||||
end;
|
||||
false -> false
|
||||
_ -> false
|
||||
end;
|
||||
inline_push(_, _, _, _, _) -> false.
|
||||
|
||||
@ -1021,16 +1063,42 @@ split_stack_arg(N, [A | As], Acc) ->
|
||||
true -> N end,
|
||||
split_stack_arg(N1, As, [A | Acc]).
|
||||
|
||||
%% Move PUSH A past non-stack instructions.
|
||||
r_swap_push(Push = {i, _, {'STORE', ?a, _}}, [I | Code]) ->
|
||||
case independent(Push, I) of
|
||||
true ->
|
||||
{I1, Push1} = swap_instrs(Push, I),
|
||||
{[I1, Push1], Code};
|
||||
false -> false
|
||||
%% Move PUSHes past non-stack instructions.
|
||||
r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
||||
case op_view(PushI) of
|
||||
{_, ?a, _} ->
|
||||
case independent(Push, I) of
|
||||
true ->
|
||||
{I1, Push1} = swap_instrs(Push, I),
|
||||
{[I1, Push1], Code};
|
||||
false -> false
|
||||
end;
|
||||
_ -> false
|
||||
end;
|
||||
r_swap_push(_, _) -> false.
|
||||
|
||||
%% Move non-stack instruction past POPs.
|
||||
r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
||||
case independent(IA, JA) of
|
||||
true ->
|
||||
case {op_view(I), op_view(J)} of
|
||||
{false, _} -> false;
|
||||
{_, false} -> false;
|
||||
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
||||
NonStackI = not lists:member(?a, [IR | IAs]),
|
||||
%% RJ /= ?a to not conflict with r_swap_push
|
||||
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
||||
case NonStackI andalso PopJ of
|
||||
false -> false;
|
||||
true ->
|
||||
{JA1, IA1} = swap_instrs(IA, JA),
|
||||
{[JA1, IA1], Code}
|
||||
end
|
||||
end;
|
||||
false -> false
|
||||
end;
|
||||
r_swap_pop(_, _) -> false.
|
||||
|
||||
%% Match up writes to variables with instructions further down.
|
||||
r_swap_write(I = {i, _, _}, [J | Code]) ->
|
||||
case {var_writes(I), independent(I, J)} of
|
||||
@ -1166,6 +1234,8 @@ r_float_switch_body(I = {i, _, _}, [switch_body | Code]) ->
|
||||
r_float_switch_body(_, _) -> false.
|
||||
|
||||
%% Inline stores
|
||||
r_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
||||
{[], Code};
|
||||
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
||||
%% Not when A is var unless updating the annotations properly.
|
||||
Inline = case A of
|
||||
@ -1258,9 +1328,13 @@ unannotate(Code) when is_list(Code) ->
|
||||
unannotate({i, _Ann, I}) -> [I].
|
||||
|
||||
%% Desugar and specialize
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)];
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
|
||||
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(A)];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)];
|
||||
desugar({switch, Arg, Type, Alts, Def}) ->
|
||||
[{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}];
|
||||
desugar(missing) -> missing;
|
||||
@ -1460,7 +1534,8 @@ split_calls(Ref, [], Acc, Blocks) ->
|
||||
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
||||
element(1, I) == 'CALL_R';
|
||||
element(1, I) == 'CALL_GR';
|
||||
element(1, I) == 'jumpif' ->
|
||||
element(1, I) == 'jumpif';
|
||||
I == loop ->
|
||||
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
||||
lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||
@ -1473,7 +1548,8 @@ split_calls(Ref, [I | Code], Acc, Blocks) ->
|
||||
|
||||
set_labels(Labels, {Ref, Code}) when is_reference(Ref) ->
|
||||
{maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]};
|
||||
set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels));
|
||||
set_labels(_Labels, loop) -> aeb_fate_ops:jump(0);
|
||||
set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels));
|
||||
set_labels(Labels, {jumpif, Arg, Ref}) -> aeb_fate_ops:jumpif(Arg, maps:get(Ref, Labels));
|
||||
set_labels(Labels, {switch, Arg, Refs}) ->
|
||||
case [ maps:get(Ref, Labels) || Ref <- Refs ] of
|
||||
@ -1488,3 +1564,11 @@ set_labels(_, I) -> I.
|
||||
with_ixs(Xs) ->
|
||||
lists:zip(lists:seq(0, length(Xs) - 1), Xs).
|
||||
|
||||
drop_common_suffix(Xs, Ys) ->
|
||||
drop_common_suffix_r(lists:reverse(Xs), lists:reverse(Ys)).
|
||||
|
||||
drop_common_suffix_r([X | Xs], [X | Ys]) ->
|
||||
drop_common_suffix_r(Xs, Ys);
|
||||
drop_common_suffix_r(Xs, Ys) ->
|
||||
{lists:reverse(Xs), lists:reverse(Ys)}.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user