1
1
%% -*- erlang-indent-level: 2 -*-
2
2
%%-----------------------------------------------------------------------
3
%% ``The contents of this file are subject to the Erlang Public License,
5
%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
7
%% The contents of this file are subject to the Erlang Public License,
4
8
%% Version 1.1, (the "License"); you may not use this file except in
5
9
%% compliance with the License. You should have received a copy of the
6
10
%% Erlang Public License along with this software. If not, it can be
7
%% retrieved via the world wide web at http://www.erlang.org/.
11
%% retrieved online at http://www.erlang.org/.
9
13
%% Software distributed under the License is distributed on an "AS IS"
10
14
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11
15
%% the License for the specific language governing rights and limitations
12
16
%% under the License.
14
%% Copyright 2006, 2007 Tobias Lindahl and Kostis Sagonas
19
21
%%%-------------------------------------------------------------------
107
109
%%io:format("Entering fun: ~w\n", [cerl_trees:get_label(Tree)]),
108
110
Body = cerl:fun_body(Tree),
109
111
Label = cerl_trees:get_label(Tree),
110
if CurrentFun =:= top ->
111
State1 = state__add_deps(top, output(set__singleton(Label)), State);
113
O1 = output(set__singleton(CurrentFun)),
114
O2 = output(set__singleton(Label)),
115
TmpState = state__add_deps(Label, O1, State),
116
State1 = state__add_deps(CurrentFun, O2,TmpState)
113
if CurrentFun =:= top ->
114
state__add_deps(top, output(set__singleton(Label)), State);
116
O1 = output(set__singleton(CurrentFun)),
117
O2 = output(set__singleton(Label)),
118
TmpState = state__add_deps(Label, O1, State),
119
state__add_deps(CurrentFun, O2,TmpState)
118
121
{BodyFuns, State2} = traverse(Body, Out, State1,
119
122
cerl_trees:get_label(Tree)),
120
123
{output(set__singleton(Label)), state__add_esc(BodyFuns, State2)};
284
287
false -> {ArgFuns, State1}
287
%%____________________________________________________________
290
%%------------------------------------------------------------
292
294
-record(set, {set :: set()}).
294
296
set__singleton(Val) ->
295
#set{set=sets:add_element(Val, sets:new())}.
297
#set{set = sets:add_element(Val, sets:new())}.
297
299
set__from_list(List) ->
298
#set{set=sets:from_list(List)}.
300
#set{set = sets:from_list(List)}.
300
302
set__is_element(_El, none) ->
302
set__is_element(El, #set{set=Set}) ->
304
set__is_element(El, #set{set = Set}) ->
303
305
sets:is_element(El, Set).
305
set__union(none, X) -> X;
306
set__union(X, none) -> X;
307
set__union(#set{set=X}, #set{set=Y}) -> #set{set=sets:union(X, Y)}.
307
set__union(none, Set) -> Set;
308
set__union(Set, none) -> Set;
309
set__union(#set{set = S1}, #set{set = S2}) -> #set{set = sets:union(S1, S2)}.
309
311
set__to_ordsets(none) -> [];
310
set__to_ordsets(#set{set=Set}) -> ordsets:from_list(sets:to_list(Set)).
312
set__to_ordsets(#set{set = Set}) -> ordsets:from_list(sets:to_list(Set)).
312
314
set__size(none) -> 0;
313
set__size(#set{set=X}) -> sets:size(X).
315
set__size(#set{set = Set}) -> sets:size(Set).
315
set__filter(#set{set=X}, Fun) ->
316
NewSet = sets:filter(Fun, X),
317
set__filter(#set{set = Set}, Fun) ->
318
NewSet = sets:filter(Fun, Set),
317
319
case sets:size(NewSet) =:= 0 of
319
false -> #set{set=NewSet}
321
false -> #set{set = NewSet}
323
%%____________________________________________________________
324
%%------------------------------------------------------------
328
328
-record(output, {type :: 'single' | 'list',
329
329
content :: 'none' | #set{} | [{output,_,_}]}).
331
output(none) -> #output{type=single, content=none};
332
output(S = #set{}) -> #output{type=single, content=S};
333
output(List) when is_list(List) -> #output{type=list, content=List}.
331
output(none) -> #output{type = single, content = none};
332
output(S = #set{}) -> #output{type = single, content = S};
333
output(List) when is_list(List) -> #output{type = list, content = List}.
335
335
merge_outs([H|T]) ->
336
336
merge_outs(T, H);
337
merge_outs(#output{type=list, content=[H|T]}) ->
337
merge_outs(#output{type = list, content = [H|T]}) ->
338
338
merge_outs(T, H);
339
merge_outs(#output{type=list, content=[]}) ->
339
merge_outs(#output{type = list, content = []}) ->
342
merge_outs([#output{content=none}|Left], O) ->
344
merge_outs([O|Left], #output{content=none}) ->
346
merge_outs([#output{type=single, content=S1}|Left],
347
#output{type=single, content=S2}) ->
342
merge_outs([#output{content = none}|Left], O) ->
344
merge_outs([O|Left], #output{content = none}) ->
346
merge_outs([#output{type = single, content = S1}|Left],
347
#output{type = single, content = S2}) ->
348
348
merge_outs(Left, output(set__union(S1, S2)));
349
merge_outs([#output{type=list, content=L1}|Left],
350
#output{type=list, content=L2}) ->
349
merge_outs([#output{type = list, content = L1}|Left],
350
#output{type = list, content = L2}) ->
351
351
NewList = [merge_outs([X, Y]) || {X, Y} <- lists:zip(L1, L2)],
352
352
merge_outs(Left, output(NewList));
353
353
merge_outs([], Res) ->
356
filter_outs(#output{type=single, content=S}, Fun) ->
356
filter_outs(#output{type = single, content = S}, Fun) ->
357
357
output(set__filter(S, Fun)).
359
add_external(#output{type=single, content=Set}) ->
359
add_external(#output{type = single, content = Set}) ->
360
360
output(set__union(Set, set__singleton(external)));
361
add_external(#output{type=list, content=List}) ->
361
add_external(#output{type = list, content = List}) ->
362
362
output([add_external(O) || O <- List]).
364
is_only_external(#output{type=single, content=Set}) ->
364
is_only_external(#output{type = single, content = Set}) ->
365
365
set__is_element(external, Set) andalso (set__size(Set) =:= 1).
367
%%____________________________________________________________
367
%%------------------------------------------------------------
395
394
map__finalize(Map) ->
396
dict:map(fun(_Key, Set = #set{}) -> set__to_ordsets(Set);
397
(_Key, #output{type=single, content=Set}) -> set__to_ordsets(Set)
395
dict:map(fun (_Key, #set{} = Set) -> set__to_ordsets(Set);
396
(_Key, #output{type = single, content = Set}) ->
400
%%____________________________________________________________
400
%%------------------------------------------------------------
402
401
%% Binding outs in the map
405
bind_pats_list(_Pats, #output{content=none}, Map) ->
404
bind_pats_list(_Pats, #output{content = none}, Map) ->
407
bind_pats_list([Pat], O = #output{type=single}, Map) ->
406
bind_pats_list([Pat], #output{type = single} = O, Map) ->
408
407
bind_single(all_vars(Pat), O, Map);
409
bind_pats_list(Pats, #output{type=list, content=List}, Map) ->
408
bind_pats_list(Pats, #output{type = list, content = List}, Map) ->
410
409
bind_pats_list(Pats, List, Map);
411
410
bind_pats_list([Pat|PatLeft],
412
[O = #output{type=single}|SetLeft], Map)->
411
[#output{type = single} = O|SetLeft], Map)->
413
412
Map1 = bind_single(all_vars(Pat), O, Map),
414
413
bind_pats_list(PatLeft, SetLeft, Map1);
415
414
bind_pats_list([Pat|PatLeft],
416
[#output{type=list, content=List}|SetLeft], Map)->
417
case cerl:is_c_values(Pat) of
418
true -> Map1 = bind_pats_list(cerl:values_es(Pat), List, Map);
419
false -> Map1 = bind_single(all_vars(Pat), merge_outs(List), Map)
415
[#output{type = list, content = List}|SetLeft], Map) ->
416
Map1 = case cerl:is_c_values(Pat) of
417
true -> bind_pats_list(cerl:values_es(Pat), List, Map);
418
false -> bind_single(all_vars(Pat), merge_outs(List), Map)
421
420
bind_pats_list(PatLeft, SetLeft, Map1);
422
421
bind_pats_list([], [], Map) ->
487
state__add_deps(_From, #output{content=none}, State) ->
486
state__add_deps(_From, #output{content = none}, State) ->
489
state__add_deps(From, #output{type=single, content=To},
490
State = #state{deps=Map}) ->
491
%%io:format("Adding deps from ~w to ~w\n", [From, set__to_ordsets(To)]),
492
State#state{deps=map__add(From, To, Map)}.
488
state__add_deps(From, #output{type = single, content=To},
489
#state{deps = Map} = State) ->
490
%% io:format("Adding deps from ~w to ~w\n", [From, set__to_ordsets(To)]),
491
State#state{deps = map__add(From, To, Map)}.
494
state__deps(#state{deps=Deps}) ->
493
state__deps(#state{deps = Deps}) ->
497
state__add_esc(#output{content=none}, State) ->
496
state__add_esc(#output{content = none}, State) ->
499
state__add_esc(#output{type=single, content=Set}, State = #state{esc=Esc}) ->
500
State#state{esc=set__union(Set, Esc)}.
498
state__add_esc(#output{type = single, content = Set},
499
#state{esc = Esc} = State) ->
500
State#state{esc = set__union(Set, Esc)}.
502
state__esc(#state{esc=Esc}) ->
502
state__esc(#state{esc = Esc}) ->
505
state__store_callsite(_From, #output{content=none}, _CallArity, State) ->
505
state__store_callsite(_From, #output{content = none}, _CallArity, State) ->
507
507
state__store_callsite(From, To, CallArity,
508
State = #state{call=Calls, arities=Arities}) ->
508
#state{call = Calls, arities = Arities} = State) ->
509
509
Filter = fun(external) -> true;
510
510
(Fun) -> CallArity =:= dict:fetch(Fun, Arities)
512
512
case filter_outs(To, Filter) of
513
#output{content=none} -> State;
514
To1 -> State#state{call=map__store(From, To1, Calls)}
513
#output{content = none} -> State;
514
To1 -> State#state{call = map__store(From, To1, Calls)}
517
state__calls(#state{call=Calls}) ->
517
state__calls(#state{call = Calls}) ->
520
%%____________________________________________________________
520
%%------------------------------------------------------------
522
521
%% A test function. Not part of the intended interface.
525
524
-ifndef(NO_UNUSED).
528
{ok, _, Code} = compile:file(Mod, [to_core,binary]),
527
{ok, _, Code} = compile:file(Mod, [to_core, binary]),
529
528
Tree = cerl:from_records(Code),
530
529
{LabeledTree, _} = cerl_trees:label(Tree),
532
%%io:put_chars(cerl_prettypr:format(LabeledTree)),
535
530
{Deps, Esc, Calls} = analyze(LabeledTree),
536
531
Edges0 = dict:fold(fun(Caller, Set, Acc) ->
537
532
[[{Caller, Callee} || Callee <- Set]|Acc]
572
567
NamedEsc = [dict:fetch(X, NameMap) || X <- Esc],
573
568
%% Color the edges
574
569
ColorEsc = [{X, {color, red}} || X <- NamedEsc],
576
570
CallEdges0 = dict:fold(fun(Caller, Set, Acc) ->
577
571
[[{Caller, Callee} || Callee <- Set]|Acc]
579
573
CallEdges = lists:flatten(CallEdges0),
580
574
NamedCallEdges = [{X, dict:fetch(Y, NameMap)} || {X, Y} <- CallEdges],
582
hipe_dot:translate_list(NamedEdges ++ NamedCallEdges, "/tmp/cg.dot", "CG",
575
AllNamedEdges = NamedEdges ++ NamedCallEdges,
576
hipe_dot:translate_list(AllNamedEdges, "/tmp/cg.dot", "CG", ColorEsc),
584
577
os:cmd("dot -T ps -o /tmp/cg.ps /tmp/cg.dot"),