%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2004-2009. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% -module(cerl_hybrid_transform). %% Use compile option `{core_transform, cerl_hybrid_transform}' to %% insert this as a compilation pass. -export([transform/2, core_transform/2]). -spec core_transform(cerl:cerl(), [term()]) -> cerl:cerl(). core_transform(Code, Opts) -> cerl:to_records(transform(cerl:from_records(Code), Opts)). -spec transform(cerl:cerl(), [term()]) -> cerl:cerl(). transform(Code, _Opts) -> Code0 = cerl_trees:map(fun unfold_literal/1, Code), {Code1, _} = cerl_trees:label(Code0), io:fwrite("Running hybrid heap analysis..."), {T1,_} = statistics(runtime), {Code2, _, Vars} = cerl_messagean:annotate(Code1), {T2,_} = statistics(runtime), io:fwrite("(~w ms), transform...", [T2 - T1]), Code3 = rewrite(Code2, Vars), io:fwrite("done.\n"), cerl_trees:map(fun fold_literal/1, Code3). unfold_literal(T) -> cerl:unfold_literal(T). fold_literal(T) -> cerl:fold_literal(T). %% If escape-annotated: %% {...} => hybrid:tuple([...]) %% [H | T] => hybrid:cons(H, T) %% %% Wrapper for args to hybrid:cons/hybrid:tuple that may need copying: %% hybrid:copy(A) rewrite(Node, Vars) -> case cerl:type(Node) of tuple -> Es = rewrite_list(cerl:tuple_es(Node), Vars), case is_escaping(Node) of false -> cerl:update_c_tuple(Node, Es); true -> Es1 = wrap(Es, Node, Vars), cerl:update_c_call(Node, cerl:abstract(hybrid), cerl:abstract(tuple), [cerl:make_list(Es1)]) %%% cerl:update_c_call(Node, cerl:abstract(hybrid), %%% cerl:abstract(tuple), Es1) end; cons -> H = rewrite(cerl:cons_hd(Node), Vars), T = rewrite(cerl:cons_tl(Node), Vars), case is_escaping(Node) of false -> cerl:update_c_cons(Node, H, T); true -> Es = wrap([H, T], Node, Vars), cerl:update_c_call(Node, cerl:abstract(hybrid), cerl:abstract(cons), Es) end; %%% call -> %%% M = rewrite(cerl:call_module(Node)), %%% F = rewrite(cerl:call_name(Node)), %%% As = rewrite_list(cerl:call_args(Node)), %%% case cerl:is_c_atom(M) andalso cerl:is_c_atom(F) of %%% true -> %%% case {cerl:atom_val(M), cerl:atom_val(F), length(As)} of %%% {erlang, '!', 2} -> %%% cerl:update_c_call(Node, %%% cerl:abstract(hipe_bifs), %%% cerl:abstract(send), %%% [cerl:make_list(As)]); %%% _ -> %%% cerl:update_c_call(Node, M, F, As) %%% end; %%% false -> %%% cerl:update_c_call(Node, M, F, As) %%% end; clause -> B = rewrite(cerl:clause_body(Node), Vars), cerl:update_c_clause(Node, cerl:clause_pats(Node), cerl:clause_guard(Node), B); primop -> case cerl:atom_val(cerl:primop_name(Node)) of match_fail -> Node; _ -> As = rewrite_list(cerl:primop_args(Node), Vars), cerl:update_c_primop(Node, cerl:primop_name(Node), As) end; _T -> case cerl:subtrees(Node) of [] -> Node; Gs -> cerl:update_tree(Node, [rewrite_list(Ns, Vars) || Ns <- Gs]) end end. rewrite_list([N | Ns], Vars) -> [rewrite(N, Vars) | rewrite_list(Ns, Vars)]; rewrite_list([], _) -> []. is_escaping(T) -> lists:member(escapes, cerl:get_ann(T)). wrap(Es, Node, Vars) -> L = cerl_trees:get_label(Node), Xs = dict:fetch(L, Vars), wrap(Es, Xs). wrap([E | Es], [{S, _} | Xs]) -> case ordsets:is_element(unsafe, S) of %% case cerl:type(E) =/= literal of true -> [cerl:c_call(cerl:abstract(hybrid), cerl:abstract(copy), [E]) | wrap(Es, Xs)]; false -> [E | wrap(Es, Xs)] end; wrap([], _) -> [].