diff options
Diffstat (limited to 'lib/compiler/src/beam_clean.erl')
-rw-r--r-- | lib/compiler/src/beam_clean.erl | 143 |
1 files changed, 142 insertions, 1 deletions
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index e771818d10..30d6670bf6 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -34,7 +34,8 @@ module({Mod,Exp,Attr,Fs0,_}, Opts) -> Fs1 = remove_unused(Order, Used, All), {Fs2,Lc} = clean_labels(Fs1), Fs3 = fix_swap(Fs2, Opts), - Fs = maybe_remove_lines(Fs3, Opts), + Fs4 = fix_bs_create_bin(Fs3, Opts), + Fs = maybe_remove_lines(Fs4, Opts), {ok,{Mod,Exp,Attr,Fs,Lc}}. %% Determine the rootset, i.e. exported functions and @@ -178,6 +179,146 @@ remove_lines_block([I|Is]) -> [I|remove_lines_block(Is)]; remove_lines_block([]) -> []. +%%% +%%% If compatibility with a previous release (OTP 24 or earlier) has +%%% been requested, eliminate bs_create_bin instructions by translating +%%% them to the old binary syntax instructions. +%%% + +fix_bs_create_bin(Fs, Opts) -> + %% Force translation to old instructions before we have actually + %% implemented the `bs_create_bin` instruction. + case true orelse proplists:get_bool(no_bs_create_bin, Opts) of + false -> Fs; + true -> fold_functions(fun fix_bs_create_bin/1, Fs) + end. + +fix_bs_create_bin([{bs_create_bin,Fail,Alloc,Live,Unit,Dst,{list,List}}|Is]) -> + Tail = fix_bs_create_bin(Is), + Flags = {field_flags,[]}, + try bs_pre_size_calc(List) of + SizeCalc0 -> + SizeCalc = fold_size_calc(SizeCalc0, 0, []), + TmpDst = SizeReg = {x,Live}, + SizeIs0 = bs_size_calc(SizeCalc, Fail, SizeReg, {x,Live+1}), + SizeIs = [{move,{integer,0},SizeReg}|SizeIs0], + RestIs = bs_puts(List, Fail) ++ [{move,TmpDst,Dst}|Tail], + case List of + [{atom,append},_,_,_,Src|_] -> + SizeIs ++ [{bs_append,Fail,SizeReg,Alloc,Live+1,Unit,Src,Flags,TmpDst}|RestIs]; + [{atom,private_append},_,_,_,Src|_] -> + TestHeap = {test_heap,Alloc,Live+1}, + SizeIs ++ [TestHeap,{bs_private_append,Fail,SizeReg,Unit,Src,Flags,TmpDst}|RestIs]; + _ -> + SizeIs ++ [{bs_init_bits,Fail,SizeReg,Alloc,Live+1,Flags,TmpDst}|RestIs] + end + catch + throw:invalid_size -> + [{move,{atom,badarg},{x,0}}, + {call_ext_only,1,{extfunc,erlang,error,1}}|Tail] + end; +fix_bs_create_bin([I|Is]) -> + [I|fix_bs_create_bin(Is)]; +fix_bs_create_bin([]) -> []. + +bs_pre_size_calc([Type,_Seg,Unit,_Flags,Src,Size|Segs]) -> + case Type of + {atom,T} when T =:= append; T =:= private_append -> + bs_pre_size_calc(Segs); + _ -> + [bs_pre_size_calc_1(Type, Unit, Src, Size)|bs_pre_size_calc(Segs)] + end; +bs_pre_size_calc([]) -> []. + +bs_pre_size_calc_1({atom,Type}, Unit, Src, Size) -> + case {Unit,Size} of + {0,{atom,undefined}} -> + %% No size/unit given. + {8,case Type of + utf8 -> {{instr,bs_utf8_size},Src}; + utf16 -> {{instr,bs_utf16_size},Src}; + utf32 -> {term,{integer,4}} + end}; + {Unit,_} -> + case {Type,Size} of + {binary,{atom,all}} -> + case Unit rem 8 of + 0 -> {8,{{bif,byte_size},Src}}; + _ -> {1,{{bif,bit_size},Src}} + end; + {_,_} -> + ensure_valid_size(Size), + {Unit,{term,Size}} + end + end. + +ensure_valid_size({x,_}) -> ok; +ensure_valid_size({y,_}) -> ok; +ensure_valid_size({integer,Size}) when Size >= 0 -> ok; +ensure_valid_size(_) -> throw(invalid_size). + +fold_size_calc([{Unit,{term,{integer,Size}}}|T], Bits, Acc) -> + fold_size_calc(T, Bits + Unit*Size, Acc); +fold_size_calc([{Unit,{{bif,Bif},{literal,Lit}}}=H|T], Bits, Acc) -> + try erlang:Bif(Lit) of + Result -> + fold_size_calc([{Unit,{term,{integer,Result}}}|T], Bits, Acc) + catch + _:_ -> + fold_size_calc(T, Bits, [H|Acc]) + end; +fold_size_calc([{U,_}=H|T], Bits, Acc) when U =:= 1; U =:= 8 -> + fold_size_calc(T, Bits, [H|Acc]); +fold_size_calc([{U,Var}|T], Bits, Acc) -> + fold_size_calc(T, Bits, [{1,{'*',{term,{integer,U}},Var}}|Acc]); +fold_size_calc([], Bits, Acc) -> + Bytes = Bits div 8, + RemBits = Bits rem 8, + Sizes = [{1,{term,{integer,RemBits}}},{8,{term,{integer,Bytes}}}|Acc], + [Pair || {_,Sz}=Pair <- Sizes, Sz =/= {term,{integer,0}}]. + +bs_size_calc([{Unit,{{bif,Bif},Reg}}|T], Fail, SizeReg, TmpReg) -> + Live = element(2, SizeReg) + 1, + [{gc_bif,Bif,Fail,Live,[Reg],TmpReg}, + {bs_add,Fail,[SizeReg,TmpReg,Unit],SizeReg}|bs_size_calc(T, Fail, SizeReg, TmpReg)]; +bs_size_calc([{Unit,{'*',{term,Term1},{term,Term2}}}|T], Fail, SizeReg, TmpReg) -> + Live = element(2, SizeReg) + 1, + [{gc_bif,'*',Fail,Live,[Term1,Term2],TmpReg}, + {bs_add,Fail,[SizeReg,TmpReg,Unit],SizeReg}|bs_size_calc(T, Fail, SizeReg, TmpReg)]; +bs_size_calc([{Unit,{{instr,Instr},Reg}}|T], Fail, SizeReg, TmpReg) -> + [{Instr,Fail,Reg,TmpReg}, + {bs_add,Fail,[SizeReg,TmpReg,Unit],SizeReg}|bs_size_calc(T, Fail, SizeReg, TmpReg)]; +bs_size_calc([{Unit,{term,Term}}|T], Fail, SizeReg, TmpReg) -> + [{bs_add,Fail,[SizeReg,Term,Unit],SizeReg}|bs_size_calc(T, Fail, SizeReg, TmpReg)]; +bs_size_calc([], _Fail, _SizeReg, _TmpReg) -> []. + +bs_puts([{atom,string},_Seg,_Unit,_Flags,{string,_}=Str,{integer,Size}|Is], Fail) -> + [{bs_put_string,Size,Str}|bs_puts(Is, Fail)]; +bs_puts([{atom,append},_,_,_,_,_|Is], Fail) -> + bs_puts(Is, Fail); +bs_puts([{atom,private_append},_,_,_,_,_|Is], Fail) -> + bs_puts(Is, Fail); +bs_puts([{atom,Type},_Seg,Unit,Flags0,Src,Size|Is], Fail) -> + Op = case Type of + integer -> bs_put_integer; + float -> bs_put_float; + binary -> bs_put_binary; + utf8 -> bs_put_utf8; + utf16 -> bs_put_utf16; + utf32 -> bs_put_utf32 + end, + Flags = case Flags0 of + nil -> []; + {literal,Fs} -> Fs + end, + I = if + Unit =:= 0 -> + {bs_put,Fail,{Op,{field_flags,Flags}},[Src]}; + true -> + {bs_put,Fail,{Op,Unit,{field_flags,Flags}},[Size,Src]} + end, + [I|bs_puts(Is, Fail)]; +bs_puts([], _Fail) -> []. %%% %%% Helpers. |