1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%% Purpose : Converts intermediate assembly code to final format.
21
22-module(beam_flatten).
23
24-export([module/2]).
25
26-import(lists, [reverse/1,reverse/2]).
27
28-spec module(beam_utils:module_code(), [compile:option()]) ->
29                    {'ok',beam_utils:module_code()}.
30
31module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
32    {ok,{Mod,Exp,Attr,[function(F) || F <- Fs],Lc}}.
33
34function({function,Name,Arity,CLabel,Is0}) ->
35    Is1 = block(Is0),
36    Is = opt(Is1),
37    {function,Name,Arity,CLabel,Is}.
38
39block(Is) ->
40    block(Is, []).
41
42block([{block,Is0}|Is1], Acc) -> block(Is1, norm_block(Is0, Acc));
43block([I|Is], Acc) -> block(Is, [I|Acc]);
44block([], Acc) -> reverse(Acc).
45
46norm_block([{set,[],[],{alloc,R,Alloc}}|Is], Acc0) ->
47    case insert_alloc_in_bs_init(Acc0, Alloc) of
48	impossible ->
49	    norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
50	Acc ->
51	    norm_block(Is, Acc)
52    end;
53norm_block([{set,[D1],[S],get_hd},{set,[D2],[S],get_tl}|Is], Acc) ->
54    I = {get_list,S,D1,D2},
55    norm_block(Is, [I|Acc]);
56norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
57norm_block([], Acc) -> Acc.
58
59norm({set,[D],As,{bif,N,F}})      -> {bif,N,F,As,D};
60norm({set,[D],As,{alloc,R,{gc_bif,N,F}}}) -> {gc_bif,N,F,R,As,D};
61norm({set,[D],[],init})           -> {init,D};
62norm({set,[D],[S],move})          -> {move,S,D};
63norm({set,[D],[S],fmove})         -> {fmove,S,D};
64norm({set,[D],[S],fconv})         -> {fconv,S,D};
65norm({set,[D],[S1,S2],put_list})  -> {put_list,S1,S2,D};
66norm({set,[D],[],{put_tuple,A}})  -> {put_tuple,A,D};
67norm({set,[],[S],put})            -> {put,S};
68norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
69norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
70norm({set,[D],[S],get_hd})        -> {get_hd,S,D};
71norm({set,[D],[S],get_tl})        -> {get_tl,S,D};
72norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
73    {put_map,F,Op,S,D,R,{list,Puts}};
74norm({set,[],[],remove_message})   -> remove_message;
75norm({set,[],[],fclearerror}) -> fclearerror;
76norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
77norm({set,[],[],{line,_}=Line}) -> Line.
78
79norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
80    [{test_heap,Nh,Regs}];
81norm_allocate({zero,0,Nh,[]}, Regs) ->
82    norm_allocate({nozero,0,Nh,[]}, Regs);
83norm_allocate({zero,Ns,0,[]}, Regs) ->
84    [{allocate_zero,Ns,Regs}];
85norm_allocate({zero,Ns,Nh,[]}, Regs) ->
86    [{allocate_heap_zero,Ns,Nh,Regs}];
87norm_allocate({nozero,Ns,0,Inits}, Regs) ->
88    [{allocate,Ns,Regs}|Inits];
89norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
90    [{allocate_heap,Ns,Nh,Regs}|Inits].
91
92%% insert_alloc_in_bs_init(ReverseInstructionStream, AllocationInfo) ->
93%%                                  impossible | ReverseInstructionStream'
94%%   A bs_init/6 instruction should not be followed by a test heap instruction.
95%%   Given the AllocationInfo from a test heap instruction, merge the
96%%   allocation amounts into the previous bs_init/6 instruction (if any).
97%%
98insert_alloc_in_bs_init([{bs_put,_,_,_}=I|Is], Alloc) ->
99    %% The instruction sequence ends with an bs_put/4 instruction.
100    %% We'll need to search backwards for the bs_init/6 instruction.
101    insert_alloc_1(Is, Alloc, [I]);
102insert_alloc_in_bs_init(_, _) -> impossible.
103
104insert_alloc_1([{bs_init=Op,Fail,Info0,Live,Ss,Dst}|Is],
105	       {_,nostack,Ws2,[]}, Acc) when is_integer(Live) ->
106    %% The number of extra heap words is always in the second position
107    %% in the Info tuple.
108    Ws1 = element(2, Info0),
109    Al = beam_utils:combine_heap_needs(Ws1, Ws2),
110    Info = setelement(2, Info0, Al),
111    I = {Op,Fail,Info,Live,Ss,Dst},
112    reverse(Acc, [I|Is]);
113insert_alloc_1([{bs_put,_,_,_}=I|Is], Alloc, Acc) ->
114    insert_alloc_1(Is, Alloc, [I|Acc]).
115
116%% opt(Is0) -> Is
117%%  Simple peep-hole optimization to move a {move,Any,{x,0}} past
118%%  any kill up to the next call instruction. (To give the loader
119%%  an opportunity to combine the 'move' and the 'call' instructions.)
120%%
121opt(Is) ->
122    opt_1(Is, []).
123
124opt_1([{move,_,{x,0}}=I|Is0], Acc0) ->
125    case move_past_kill(Is0, I, Acc0) of
126	impossible -> opt_1(Is0, [I|Acc0]);
127	{Is,Acc} -> opt_1(Is, Acc)
128    end;
129opt_1([I|Is], Acc) ->
130    opt_1(Is, [I|Acc]);
131opt_1([], Acc) -> reverse(Acc).
132
133move_past_kill([{kill,Src}|_], {move,Src,_}, _) ->
134    impossible;
135move_past_kill([{kill,_}=I|Is], Move, Acc) ->
136    move_past_kill(Is, Move, [I|Acc]);
137move_past_kill([{trim,N,_}=I|Is], {move,Src,Dst}=Move, Acc) ->
138    case Src of
139	{y,Y} when Y < N-> impossible;
140	{y,Y} -> {Is,[{move,{y,Y-N},Dst},I|Acc]};
141	_ -> {Is,[Move,I|Acc]}
142    end;
143move_past_kill(Is, Move, Acc) ->
144    {Is,[Move|Acc]}.
145