diff options
-rw-r--r-- | lib/compiler/src/beam_clean.erl | 143 | ||||
-rw-r--r-- | lib/compiler/src/beam_jump.erl | 2 | ||||
-rw-r--r-- | lib/compiler/src/beam_kernel_to_ssa.erl | 197 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa.erl | 13 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa_bc_size.erl | 47 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa_codegen.erl | 113 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa_opt.erl | 179 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa_pre_codegen.erl | 53 | ||||
-rw-r--r-- | lib/compiler/src/beam_ssa_type.erl | 40 | ||||
-rw-r--r-- | lib/compiler/src/beam_trim.erl | 16 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/beam_validator.erl | 60 | ||||
-rw-r--r-- | lib/compiler/src/beam_z.erl | 26 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 8 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 44 | ||||
-rw-r--r-- | lib/compiler/test/Makefile | 18 | ||||
-rw-r--r-- | lib/compiler/test/bs_bincomp_SUITE.erl | 12 | ||||
-rw-r--r-- | lib/compiler/test/bs_construct_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/compiler/test/test_lib.erl | 8 |
19 files changed, 467 insertions, 520 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. diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index d060978a52..0c4ad8fae5 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -896,6 +896,8 @@ instr_labels({bif,_Name,Lbl,_As,_R}) -> do_instr_labels(Lbl); instr_labels({gc_bif,_Name,Lbl,_Live,_As,_R}) -> do_instr_labels(Lbl); +instr_labels({bs_create_bin,Lbl,_,_,_,_,_}) -> + do_instr_labels(Lbl); instr_labels({bs_init,Lbl,_,_,_,_}) -> do_instr_labels(Lbl); instr_labels({bs_put,Lbl,_,_}) -> diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl index aa66d4c418..7270be7066 100644 --- a/lib/compiler/src/beam_kernel_to_ssa.erl +++ b/lib/compiler/src/beam_kernel_to_ssa.erl @@ -25,8 +25,8 @@ -export([module/2]). -import(lists, [all/2,append/1,flatmap/2,foldl/3, - keysort/2,mapfoldl/3,map/2,member/2, - reverse/1,reverse/2,sort/1]). + keysort/2,mapfoldl/3,member/2, + reverse/1,sort/1]). -include("v3_kernel.hrl"). -include("beam_ssa.hrl"). @@ -1048,167 +1048,54 @@ put_cg_map(LineAnno, Op, SrcMap, Dst, List, St0) -> %%% cg_binary(Dst, Segs0, FailCtx, Le, St0) -> - {PutCode0,SzCalc0,St1} = cg_bin_put(Segs0, FailCtx, St0), + Segs1 = cg_bin_segments(Segs0, St0), + Segs = case Segs1 of + [#b_literal{val=binary},UnitFlags,Val,#b_literal{val=all}|Segs2] -> + Op = case member(single_use, Le) of + true -> private_append; + false -> append + end, + [#b_literal{val=Op},UnitFlags,Val,#b_literal{val=all}|Segs2]; + _ -> + Segs1 + end, LineAnno = line_anno(Le), - Anno = Le, - case PutCode0 of - [#b_set{op=bs_put,dst=Bin,args=[_,_,Src,#b_literal{val=all}|_]}, - #b_set{op={succeeded,_},dst=Bool,args=[Bin]}, - #b_br{bool=Bool}, - {label,_}|_] -> - #k_bin_seg{unit=Unit0,next=Segs} = Segs0, - Unit = #b_literal{val=Unit0}, - {PutCode,SzCalc1,St2} = cg_bin_put(Segs, FailCtx, St1), - {_,SzVar,SzCode0,St3} = cg_size_calc(1, SzCalc1, FailCtx, St2), - SzCode = cg_bin_anno(SzCode0, LineAnno), - Args = case member(single_use, Anno) of - true -> - [#b_literal{val=private_append},Src,SzVar,Unit]; - false -> - [#b_literal{val=append},Src,SzVar,Unit] - end, - BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args}, - {TestIs,St} = make_succeeded(Dst, FailCtx, St3), - {SzCode ++ [BsInit] ++ TestIs ++ PutCode,St}; - [#b_set{op=bs_put}|_] -> - {Unit,SzVar,SzCode0,St2} = cg_size_calc(8, SzCalc0, FailCtx, St1), - SzCode = cg_bin_anno(SzCode0, LineAnno), - Args = [#b_literal{val=new},SzVar,Unit], - BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args}, - {TestIs,St} = make_succeeded(Dst, FailCtx, St2), - {SzCode ++ [BsInit] ++ TestIs ++ PutCode0,St} - end. - -cg_bin_anno([Set|Sets], Anno) -> - [Set#b_set{anno=Anno}|Sets]; -cg_bin_anno([], _) -> []. - -%% cg_size_calc(PreferredUnit, SzCalc, FailCtx, St0) -> -%% {ActualUnit,SizeVariable,SizeCode,St}. -%% Generate size calculation code. - -cg_size_calc(Unit, error, _FailCtx, St) -> - {#b_literal{val=Unit},#b_literal{val=badarg},[],St}; -cg_size_calc(8, [{1,_}|_]=SzCalc, FailCtx, St) -> - cg_size_calc(1, SzCalc, FailCtx, St); -cg_size_calc(8, SzCalc, FailCtx, St0) -> - {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0), - {#b_literal{val=8},Var,Pre,St}; -cg_size_calc(1, SzCalc0, FailCtx, St0) -> - SzCalc = map(fun({8,#b_literal{val=Size}}) -> - {1,#b_literal{val=8*Size}}; - ({8,{{bif,byte_size},Src}}) -> - {1,{{bif,bit_size},Src}}; - ({8,{_,_}=UtfCalc}) -> - {1,{'*',#b_literal{val=8},UtfCalc}}; - ({_,_}=Pair) -> - Pair - end, SzCalc0), - {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0), - {#b_literal{val=1},Var,Pre,St}. - -cg_size_calc_1(SzCalc, FailCtx, St0) -> - cg_size_calc_2(SzCalc, #b_literal{val=0}, FailCtx, St0). - -cg_size_calc_2([{_,{'*',Unit,{_,_}=Bif}}|T], Sum0, FailCtx, St0) -> - {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), - {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1), - {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, Unit, FailCtx, St2), - {Sum,Pre0++Pre1++Pre2,St}; -cg_size_calc_2([{_,#b_literal{}=Sz}|T], Sum0, FailCtx, St0) -> - {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), - {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1), - {Sum,Pre0++Pre,St}; -cg_size_calc_2([{_,#b_var{}=Sz}|T], Sum0, FailCtx, St0) -> - {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), - {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1), - {Sum,Pre0++Pre,St}; -cg_size_calc_2([{_,{_,_}=Bif}|T], Sum0, FailCtx, St0) -> - {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), - {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1), - {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, #b_literal{val=1}, FailCtx, St2), - {Sum,Pre0++Pre1++Pre2,St}; -cg_size_calc_2([], Sum, _FailCtx, St) -> - {Sum,[],St}. - -cg_size_bif(#b_var{}=Var, _FailCtx, St) -> - {Var,[],St}; -cg_size_bif({Name,Src}, FailCtx, St0) -> - {Dst,St1} = new_ssa_var('@ssa_bif', St0), - Bif = #b_set{op=Name,dst=Dst,args=[Src]}, - {TestIs,St} = make_succeeded(Dst, FailCtx, St1), - {Dst,[Bif|TestIs],St}. - -cg_size_add(#b_literal{val=0}, Val, #b_literal{val=1}, _FailCtx, St) -> - {Val,[],St}; -cg_size_add(#b_literal{val=A}, #b_literal{val=B}, #b_literal{val=U}, _FailCtx, St) -> - {#b_literal{val=A+B*U},[],St}; -cg_size_add(A, B, Unit, FailCtx, St0) -> - {Dst,St1} = new_ssa_var('@ssa_sum', St0), - {TestIs,St} = make_succeeded(Dst, FailCtx, St1), - BsAdd = #b_set{op=bs_add,dst=Dst,args=[A,B,Unit]}, - {Dst,[BsAdd|TestIs],St}. - -cg_bin_put(Seg, FailCtx, St) -> - cg_bin_put_1(Seg, FailCtx, [], [], St). - -cg_bin_put_1(#k_bin_seg{size=Size0,unit=U,type=T,flags=Fs,seg=Src0,next=Next}, - FailCtx, Acc, SzCalcAcc, St0) -> - [Src,Size] = ssa_args([Src0,Size0], St0), - NeedSize = bs_need_size(T), - TypeArg = #b_literal{val=T}, - Flags = #b_literal{val=Fs}, - Unit = #b_literal{val=U}, - Args = case NeedSize of - true -> [TypeArg,Flags,Src,Size,Unit]; - false -> [TypeArg,Flags,Src] + Build = #b_set{anno=LineAnno,op=bs_create_bin,args=Segs,dst=Dst}, + {TestIs,St} = make_succeeded(Dst, FailCtx, St0), + {[Build|TestIs],St}. + +cg_bin_segments(#k_bin_seg{anno=Anno,type=Type,flags=Flags0,seg=Src0,size=Size0,unit=U,next=Next}, St) -> + Seg = case lists:keyfind(segment, 1,Anno) of + false -> []; + {segment,_}=Seg0 -> [Seg0] + end, + [Src,Size] = ssa_args([Src0,Size0], St), + TypeArg = #b_literal{val=Type}, + Unit = case U of + undefined -> 0; + _ -> U end, - {Dst,St1} = new_ssa_var('@ssa_bs_put', St0), - {TestIs,St} = make_succeeded(Dst, FailCtx, St1), - Is = [#b_set{op=bs_put,dst=Dst,args=Args}|TestIs], - SzCalc = bin_size_calc(T, Src, Size, U), - cg_bin_put_1(Next, FailCtx, reverse(Is, Acc), [SzCalc|SzCalcAcc], St); -cg_bin_put_1(#k_bin_end{}, _, Acc, SzCalcAcc, St) -> - SzCalc = fold_size_calc(SzCalcAcc, 0, []), - {reverse(Acc),SzCalc,St}. + Flags = strip_bs_construct_flags(Flags0), + UnitFlags = #b_literal{val=[Unit|Flags++Seg]}, + [TypeArg,UnitFlags,Src,Size|cg_bin_segments(Next, St)]; +cg_bin_segments(#k_bin_end{}, _St) -> []. bs_need_size(utf8) -> false; bs_need_size(utf16) -> false; bs_need_size(utf32) -> false; bs_need_size(_) -> true. -bin_size_calc(utf8, Src, _Size, _Unit) -> - {8,{bs_utf8_size,Src}}; -bin_size_calc(utf16, Src, _Size, _Unit) -> - {8,{bs_utf16_size,Src}}; -bin_size_calc(utf32, _Src, _Size, _Unit) -> - {8,#b_literal{val=4}}; -bin_size_calc(binary, Src, #b_literal{val=all}, Unit) -> - case Unit rem 8 of - 0 -> {8,{{bif,byte_size},Src}}; - _ -> {1,{{bif,bit_size},Src}} - end; -bin_size_calc(_Type, _Src, Size, Unit) -> - {Unit,Size}. - -fold_size_calc([{Unit,#b_literal{val=Size}}|T], Bits, Acc) -> - if - is_integer(Size) -> - fold_size_calc(T, Bits + Unit*Size, Acc); - true -> - error - end; -fold_size_calc([{U,#b_var{}}=H|T], Bits, Acc) when U =:= 1; U =:= 8 -> - fold_size_calc(T, Bits, [H|Acc]); -fold_size_calc([{U,#b_var{}=Var}|T], Bits, Acc) -> - fold_size_calc(T, Bits, [{1,{'*',#b_literal{val=U},Var}}|Acc]); -fold_size_calc([{_,_}=H|T], Bits, Acc) -> - fold_size_calc(T, Bits, [H|Acc]); -fold_size_calc([], Bits, Acc) -> - Bytes = Bits div 8, - RemBits = Bits rem 8, - Sizes = sort([{1,#b_literal{val=RemBits}},{8,#b_literal{val=Bytes}}|Acc]), - [Pair || {_,Sz}=Pair <- Sizes, Sz =/= #b_literal{val=0}]. +%% Only keep the flags that have a meaning for binary construction and +%% are distinct from the default value. +strip_bs_construct_flags(Flags) -> + [Flag || Flag <- Flags, + case Flag of + little -> true; + native -> true; + big -> false; + signed -> false; + unsigned -> false + end]. %%% %%% Utilities for creating the SSA types. diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl index 0163f59ae7..37d0b4b364 100644 --- a/lib/compiler/src/beam_ssa.erl +++ b/lib/compiler/src/beam_ssa.erl @@ -105,10 +105,9 @@ %% To avoid the collapsing, change the value of SET_LIMIT to 50 in the %% file erl_types.erl in the dialyzer application. --type prim_op() :: 'bs_add' | 'bs_extract' | 'bs_get_tail' | - 'bs_init' | 'bs_init_writable' | - 'bs_match' | 'bs_put' | 'bs_start_match' | 'bs_test_tail' | - 'bs_utf16_size' | 'bs_utf8_size' | 'build_stacktrace' | +-type prim_op() :: 'bs_extract' | 'bs_get_tail' | 'bs_init_writable' | + 'bs_match' | 'bs_start_match' | 'bs_test_tail' | + 'build_stacktrace' | 'call' | 'catch_end' | 'extract' | 'get_hd' | 'get_map_element' | 'get_tl' | 'get_tuple_element' | @@ -197,17 +196,13 @@ no_side_effect(#b_set{op=Op}) -> case Op of {bif,_} -> true; {float,get} -> true; - bs_add -> true; - bs_init -> true; + bs_create_bin -> true; bs_init_writable -> true; bs_extract -> true; bs_match -> true; bs_start_match -> true; bs_test_tail -> true; bs_get_tail -> true; - bs_put -> true; - bs_utf16_size -> true; - bs_utf8_size -> true; build_stacktrace -> true; extract -> true; get_hd -> true; diff --git a/lib/compiler/src/beam_ssa_bc_size.erl b/lib/compiler/src/beam_ssa_bc_size.erl index f795641592..9646408cc9 100644 --- a/lib/compiler/src/beam_ssa_bc_size.erl +++ b/lib/compiler/src/beam_ssa_bc_size.erl @@ -276,32 +276,13 @@ calc_size_is([I|Is], Bs0) -> calc_size_is(Is, Bs); calc_size_is([], Bs) -> Bs. -calc_size_instr(#b_set{op=bs_add,args=[A,B,U],dst=Dst}, Bs) -> - %% We must make sure that the value of bs_add only depends on literals - %% and arguments passed from the function that created the writable - %% binary. - case {get_value(A, Bs),get_arg_value(B, Bs)} of - {#b_literal{}=Lit,Val} -> - Bs#{Dst => {expr,{{bif,'+'},[Lit,{{bif,'*'},[Val,U]}]}}}; - {{expr,Expr},Val} -> - Bs#{Dst => {expr,{{bif,'+'},[Expr,{{bif,'*'},[Val,U]}]}}}; - {_,_} -> - %% The value depends on a variable of which we know nothing. - Bs#{Dst => any} - end; -calc_size_instr(#b_set{op=bs_init,args=[#b_literal{val=private_append}, - Writable,Size,Unit], +calc_size_instr(#b_set{op=bs_create_bin, + args=[#b_literal{val=private_append},_,Writable,_|Args], dst=Dst}, Bs) -> - case get_value(Size, Bs) of - {arg,SizeOrigin} -> - Expr = {{bif,'*'},[SizeOrigin,Unit]}, - update_writable(Dst, Writable, Expr, Bs); - #b_literal{} -> - Expr = {{bif,'*'},[Size,Unit]}, - update_writable(Dst, Writable, Expr, Bs); + case calc_create_bin_size(Args, Bs) of {expr,Expr} -> update_writable(Dst, Writable, Expr, Bs); - _ -> + any -> Bs#{Dst => any} end; calc_size_instr(#b_set{op=bs_match,args=[_Type,Ctx,_Flags, @@ -347,6 +328,26 @@ calc_size_instr(#b_set{op={succeeded,_},args=[Arg],dst=Dst}, Bs) -> calc_size_instr(#b_set{dst=Dst}, Bs) -> Bs#{Dst => any}. +calc_create_bin_size(Args, Bs) -> + calc_create_bin_size(Args, Bs, #b_literal{val=0}). + +calc_create_bin_size([_,#b_literal{val=[0|_]},_,_|_], _Bs, _Acc) -> + %% Construction without size (utf8/utf16/utf32). + any; +calc_create_bin_size([_,#b_literal{val=[U|_]},_,Size|T], Bs, Acc0) when is_integer(U) -> + case get_value(Size, Bs) of + #b_literal{val=Val} when is_integer(Val) -> + Acc = {{bif,'+'},[Acc0,#b_literal{val=U*Val}]}, + calc_create_bin_size(T, Bs, Acc); + {arg,Var} -> + Acc = {{bif,'+'},[Acc0,{{bif,'*'},[Var,#b_literal{val=U}]}]}, + calc_create_bin_size(T, Bs, Acc); + _ -> + any + end; +calc_create_bin_size([], _Bs, Acc) -> + {expr,Acc}. + update_writable(Dst, Writable, Expr, Bs) -> case get_value(Writable, Bs) of {writable,#b_literal{val=0}} -> diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index c929b65aa3..8454d02944 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -252,7 +252,7 @@ need_heap_blks([], H, Acc) -> need_heap_is([#cg_alloc{words=Words}=Alloc0|Is], N, Acc) -> Alloc = Alloc0#cg_alloc{words=add_heap_words(N, Words)}, need_heap_is(Is, #need{}, [Alloc|Acc]); -need_heap_is([#cg_set{anno=Anno,op=bs_init}=I0|Is], N, Acc) -> +need_heap_is([#cg_set{anno=Anno,op=bs_create_bin}=I0|Is], N, Acc) -> Alloc = case need_heap_need(N) of [#cg_alloc{words=Need}] -> alloc(Need); [] -> 0 @@ -284,13 +284,11 @@ need_heap_terminator([{_,#cg_blk{is=Is,last=#cg_br{succ=L}}}|_], L, N) -> [] -> {[],#need{}}; [_|_]=Alloc -> - %% If the preceding instructions are a binary construction, - %% hoist the allocation and incorporate into the bs_init + %% If the preceding instruction is a bs_create_bin instruction, + %% hoist the allocation and incorporate into the bs_create_bin %% instruction. case reverse(Is) of - [#cg_set{op=succeeded},#cg_set{op=bs_init}|_] -> - {[],N}; - [#cg_set{op=succeeded},#cg_set{op=bs_put}|_] -> + [#cg_set{op=succeeded},#cg_set{op=bs_create_bin}|_] -> {[],N}; _ -> %% Not binary construction. Must emit an allocation @@ -371,20 +369,16 @@ classify_heap_need(Name, _Args) -> %% Note: Only handle operations in this function that are not handled %% by classify_heap_need/2. -classify_heap_need(bs_add) -> gc; classify_heap_need(bs_get) -> gc; classify_heap_need(bs_get_tail) -> gc; -classify_heap_need(bs_init) -> gc; classify_heap_need(bs_init_writable) -> gc; classify_heap_need(bs_match_string) -> gc; -classify_heap_need(bs_put) -> neutral; +classify_heap_need(bs_create_bin) -> gc; classify_heap_need(bs_get_position) -> gc; classify_heap_need(bs_set_position) -> neutral; classify_heap_need(bs_skip) -> gc; classify_heap_need(bs_start_match) -> gc; classify_heap_need(bs_test_tail) -> neutral; -classify_heap_need(bs_utf16_size) -> neutral; -classify_heap_need(bs_utf8_size) -> neutral; classify_heap_need(build_stacktrace) -> gc; classify_heap_need(call) -> gc; classify_heap_need(catch_end) -> gc; @@ -677,8 +671,8 @@ get_live(#cg_set{anno=#{live:=Live}}) -> need_live_anno(Op) -> case Op of {bif,_} -> true; + bs_create_bin -> true; bs_get -> true; - bs_init -> true; bs_get_position -> true; bs_get_tail -> true; bs_start_match -> true; @@ -801,7 +795,7 @@ need_y_init(#cg_set{anno=#{clobbers:=Clobbers}}) -> Clobbers; need_y_init(#cg_set{op=bs_get}) -> true; need_y_init(#cg_set{op=bs_get_position}) -> true; need_y_init(#cg_set{op=bs_get_tail}) -> true; -need_y_init(#cg_set{op=bs_init}) -> true; +need_y_init(#cg_set{op=bs_create_bin}) -> true; need_y_init(#cg_set{op=bs_skip,args=[#b_literal{val=Type}|_]}) -> case Type of utf8 -> true; @@ -1108,30 +1102,21 @@ cg_block([#cg_set{anno=Anno,op={bif,Name},dst=Dst0,args=Args0}=I|T], Is = [{bif,Name,{f,0},Args,Dst}|Is0], {Is,St} end; -cg_block([#cg_set{op=bs_init,dst=Dst0,args=Args0,anno=Anno}=I, +cg_block([#cg_set{op=bs_create_bin,dst=Dst0,args=Args0,anno=Anno}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> Fail = bif_fail(Fail0), Line = line(Anno), Alloc = map_get(alloc, Anno), - [#b_literal{val=Kind}|Args1] = Args0, Live = get_live(I), - case Kind of - new -> - [Dst,Size,{integer,Unit}] = beam_args([Dst0|Args1], St), - {[Line|cg_bs_init(Dst, Size, Alloc, Unit, Live, Fail)],St}; - private_append -> - [Dst,Src,Bits,{integer,Unit}] = beam_args([Dst0|Args1], St), - Flags = {field_flags,[]}, - TestHeap = {test_heap,Alloc,Live}, - BsPrivateAppend = {bs_private_append,Fail,Bits,Unit,Src,Flags,Dst}, - Is = [TestHeap,Line,BsPrivateAppend], - {Is,St}; - append -> - [Dst,Src,Bits,{integer,Unit}] = beam_args([Dst0|Args1], St), - Flags = {field_flags,[]}, - Is = [Line,{bs_append,Fail,Bits,Alloc,Live,Unit,Src,Flags,Dst}], - {Is,St} - end; + [Dst|Args1] = beam_args([Dst0|Args0], St), + Args = bs_args(Args1), + Unit = case Args of + [{atom,append},_Seg,U|_] -> U; + [{atom,private_append},_Seg,U|_] -> U; + _ -> 1 + end, + Is = [Line,{bs_create_bin,Fail,Alloc,Live,Unit,Dst,{list,Args}}], + {Is,St}; cg_block([#cg_set{op=bs_start_match, dst=Ctx0, args=[#b_literal{val=new},Bin0]}=I, @@ -1156,10 +1141,6 @@ cg_block([#cg_set{op=bs_match_string,args=[CtxVar,#b_literal{val=String0}]}, Is = [{test,bs_match_string,Fail,[CtxReg,Bits,{string,String}]}], {Is,St}; -cg_block([#cg_set{op=bs_put,args=Args0}, - #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> - Args = beam_args(Args0, St), - {cg_bs_put(bif_fail(Fail), Args),St}; cg_block([#cg_set{dst=Dst0,op=landingpad,args=Args0}|T], Context, St0) -> [Dst,{atom,Kind},Tag] = beam_args([Dst0|Args0], St0), case Kind of @@ -1329,6 +1310,32 @@ cg_block([], {Bool0,Fail}, St) -> [Bool] = beam_args([Bool0], St), {[{test,is_eq_exact,Fail,[Bool,{atom,true}]}],St}. +bs_args([{atom,binary},{literal,[1|_]},{literal,Bs},{atom,all}|Args]) + when bit_size(Bs) =:= 0 -> + bs_args(Args); +bs_args([{atom,binary},{literal,[1|_]}=UFs,{literal,Bs},{atom,all}|Args0]) + when is_bitstring(Bs) -> + Bits = bit_size(Bs), + Bytes = Bits div 8, + case Bits rem 8 of + 0 -> + [{atom,string},0,8,nil,{string,Bs},{integer,byte_size(Bs)}|bs_args(Args0)]; + Rem -> + <<Binary:Bytes/bytes,Int:Rem>> = Bs, + Args = [{atom,binary},UFs,{literal,Binary},{atom,all}, + {atom,integer},{literal,[1]},{integer,Int},{integer,Rem}|Args0], + bs_args(Args) + end; +bs_args([Type,{literal,[Unit|Fs0]},Val,Size|Args]) -> + Segment = proplists:get_value(segment, Fs0, 0), + Fs1 = proplists:delete(segment, Fs0), + Fs = case Fs1 of + [] -> nil; + [_|_] -> {literal,Fs1} + end, + [Type,Segment,Unit,Fs,Val,Size|bs_args(Args)]; +bs_args([]) -> []. + cg_copy(T0, St) -> {Copies,T} = splitwith(fun(#cg_set{op=copy}) -> true; (_) -> false @@ -1716,14 +1723,8 @@ cg_instr(remove_message, [], _Dst) -> cg_instr(resume, [A,B], _Dst) -> [{bif,raise,{f,0},[A,B],{x,0}}]. -cg_test(bs_add=Op, Fail, [Src1,Src2,{integer,Unit}], Dst, _I) -> - [{Op,Fail,[Src1,Src2,Unit],Dst}]; cg_test(bs_skip, Fail, Args, _Dst, I) -> cg_bs_skip(Fail, Args, I); -cg_test(bs_utf8_size=Op, Fail, [Src], Dst, _I) -> - [{Op,Fail,Src,Dst}]; -cg_test(bs_utf16_size=Op, Fail, [Src], Dst, _I) -> - [{Op,Fail,Src,Dst}]; cg_test({float,convert}, Fail, [Src], Dst, #cg_set{anno=Anno}) -> {f,0} = Fail, %Assertion. [line(Anno),{fconv,Src,Dst}]; @@ -1793,34 +1794,6 @@ field_flags(Flags, #cg_set{anno=#{location:={File,Line}}}) -> field_flags(Flags, _) -> {field_flags,Flags}. -cg_bs_put(Fail, [{atom,Type},{literal,Flags}|Args]) -> - 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, - case Args of - [Src,Size,{integer,Unit}] -> - [{Op,Fail,Size,Unit,{field_flags,Flags},Src}]; - [Src] -> - [{Op,Fail,{field_flags,Flags},Src}] - end. - -cg_bs_init(Dst, Size0, Alloc, Unit, Live, Fail) -> - Op = case Unit of - 1 -> bs_init_bits; - 8 -> bs_init2 - end, - Size = cg_bs_init_size(Size0), - [{Op,Fail,Size,Alloc,Live,{field_flags,[]},Dst}]. - -cg_bs_init_size({x,_}=R) -> R; -cg_bs_init_size({y,_}=R) -> R; -cg_bs_init_size({integer,Int}) -> Int. - cg_catch(Agg, T0, Context, St0) -> {Moves,T1} = cg_extract(T0, Agg, St0), {T,St} = cg_block(T1, Context, St0), diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index 72e320749e..123edb868c 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -277,7 +277,7 @@ module_passes(Opts) -> repeated_passes(Opts) -> Ps = [?PASS(ssa_opt_live), ?PASS(ssa_opt_ne), - ?PASS(ssa_opt_bs_puts), + ?PASS(ssa_opt_bs_create_bin), ?PASS(ssa_opt_dead), ?PASS(ssa_opt_cse), ?PASS(ssa_opt_tail_phis), @@ -1788,101 +1788,63 @@ bsm_shortcut([], _PosMap) -> []. %%% If an integer segment or a float segment has a literal size and %%% a literal value, convert to a binary segment. Coalesce adjacent %%% literal binary segments. Literal binary segments will be converted -%%% to bs_put_string instructions in later pass. +%%% to bs_put_string instructions in a later pass. %%% -ssa_opt_bs_puts({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - {Linear,Count} = opt_bs_puts(Linear0, Count0, []), - {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}. - -opt_bs_puts([{L,#b_blk{is=Is}=Blk0}|Bs], Count0, Acc0) -> - case Is of - [#b_set{op=bs_put},#b_set{op={succeeded,_}}]=Is -> - case opt_bs_put(L, Is, Blk0, Count0, Acc0) of - not_possible -> - opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]); - {Count,Acc1} -> - Acc = opt_bs_puts_merge(Acc1), - opt_bs_puts(Bs, Count, Acc) - end; - _ -> - opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]) - end; -opt_bs_puts([], Count, Acc) -> - {reverse(Acc),Count}. - -opt_bs_puts_merge([{L1,#b_blk{is=Is}=Blk0},{L2,#b_blk{is=AccIs}}=BAcc|Acc]) -> - case {AccIs,Is} of - {[#b_set{op=bs_put, - args=[#b_literal{val=binary}, - #b_literal{}, - #b_literal{val=Bin0}, - #b_literal{val=all}, - #b_literal{val=1}]}, - #b_set{op={succeeded,_}}], - [#b_set{op=bs_put, - args=[#b_literal{val=binary}, - #b_literal{}, - #b_literal{val=Bin1}, - #b_literal{val=all}, - #b_literal{val=1}]}=I0, - #b_set{op={succeeded,_}}=Succeeded]} -> - %% Coalesce the two segments to one. - Bin = <<Bin0/bitstring,Bin1/bitstring>>, - I = I0#b_set{args=bs_put_args(binary, Bin, all)}, - Blk = Blk0#b_blk{is=[I,Succeeded]}, - [{L2,Blk}|Acc]; - {_,_} -> - [{L1,Blk0},BAcc|Acc] - end. +ssa_opt_bs_create_bin({#opt_st{ssa=Linear0}=St, FuncDb}) -> + Linear = opt_create_bin_fs(Linear0), + {St#opt_st{ssa=Linear}, FuncDb}. -opt_bs_put(L, [I0,Succeeded], #b_blk{last=Br0}=Blk0, Count0, Acc) -> - case opt_bs_put(I0) of - [Bin] when is_bitstring(Bin) -> - Args = bs_put_args(binary, Bin, all), - I = I0#b_set{args=Args}, - Blk = Blk0#b_blk{is=[I,Succeeded]}, - {Count0,[{L,Blk}|Acc]}; - [{int,Int,Size},Bin] when is_bitstring(Bin) -> - %% Construct a bs_put_integer instruction following - %% by a bs_put_binary instruction. - IntArgs = bs_put_args(integer, Int, Size), - BinArgs = bs_put_args(binary, Bin, all), - - {BinL,BinVarNum,BinBoolNum} = {Count0,Count0+1,Count0+2}, - Count = Count0 + 3, - BinVar = #b_var{name={'@ssa_bs_put',BinVarNum}}, - BinBool = #b_var{name={'@ssa_bool',BinBoolNum}}, - - BinI = I0#b_set{dst=BinVar,args=BinArgs}, - BinSucceeded = Succeeded#b_set{dst=BinBool,args=[BinVar]}, - BinBlk = Blk0#b_blk{is=[BinI,BinSucceeded], - last=Br0#b_br{bool=BinBool}}, - - IntI = I0#b_set{args=IntArgs}, - IntBlk = Blk0#b_blk{is=[IntI,Succeeded],last=Br0#b_br{succ=BinL}}, - - {Count,[{BinL,BinBlk},{L,IntBlk}|Acc]}; +opt_create_bin_fs([{L,#b_blk{is=Is0}=Blk0}|Bs]) -> + Is = opt_create_bin_is(Is0), + Blk = Blk0#b_blk{is=Is}, + [{L,Blk}|opt_create_bin_fs(Bs)]; +opt_create_bin_fs([]) -> []. + +opt_create_bin_is([#b_set{op=bs_create_bin,args=Args0}=I0|Is]) -> + Args = opt_create_bin_args(Args0), + I = I0#b_set{args=Args}, + [I|opt_create_bin_is(Is)]; +opt_create_bin_is([I|Is]) -> + [I|opt_create_bin_is(Is)]; +opt_create_bin_is([]) -> []. + +opt_create_bin_args([#b_literal{val=binary},#b_literal{val=[1|_]}, + #b_literal{val=Bin0},#b_literal{val=all}, + #b_literal{val=binary},#b_literal{val=[1|_]}, + #b_literal{val=Bin1},#b_literal{val=all}|Args0]) -> + %% Coalesce two litary binary segments to one. + Bin = <<Bin0/bitstring,Bin1/bitstring>>, + Args = [#b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args); +opt_create_bin_args([#b_literal{val=Type}=Type0,#b_literal{val=UFs}=UFs0,Val,Size|Args0]) -> + [Unit|Flags] = UFs, + case opt_create_bin_arg(Type, Unit, UFs, Val, Size) of not_possible -> - not_possible - end. - -opt_bs_put(#b_set{args=[#b_literal{val=binary},_,#b_literal{val=Val}, - #b_literal{val=all},#b_literal{val=Unit}]}) - when is_bitstring(Val) -> - if - bit_size(Val) rem Unit =:= 0 -> - [Val]; - true -> - not_possible - end; -opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, - #b_literal{val=Val},#b_literal{val=Size}, - #b_literal{val=Unit}]}=I0) when is_integer(Size) -> + [Type0,UFs0,Val,Size|opt_create_bin_args(Args0)]; + [Bin] when is_bitstring(Bin) -> + Args = [#b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args); + [{int,Int,IntSize},Bin] when is_bitstring(Bin) -> + Args = [#b_literal{val=integer},#b_literal{val=[1|Flags]}, + #b_literal{val=Int},#b_literal{val=IntSize}, + #b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args) + end; +opt_create_bin_args([]) -> []. + +opt_create_bin_arg(binary, Unit, _Flags, #b_literal{val=Val}, #b_literal{val=all}) + when Unit =/= 1, bit_size(Val) rem Unit =:= 0 -> + [Val]; +opt_create_bin_arg(Type, Unit, Flags, #b_literal{val=Val}, #b_literal{val=Size}) + when is_integer(Size), is_integer(Unit) -> EffectiveSize = Size * Unit, if EffectiveSize > 0 -> - case {Type,opt_bs_put_endian(Flags)} of + case {Type,opt_create_bin_endian(Flags)} of {integer,big} when is_integer(Val) -> if EffectiveSize < 64 -> @@ -1894,9 +1856,8 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, %% To avoid an explosion in code size, we only try %% to optimize relatively small fields. <<Int:EffectiveSize>> = <<Val:EffectiveSize/little>>, - Args = bs_put_args(Type, Int, EffectiveSize), - I = I0#b_set{args=Args}, - opt_bs_put(I); + opt_create_bin_arg(Type, 1, [], #b_literal{val=Int}, + #b_literal{val=EffectiveSize}); {binary,_} when is_bitstring(Val) -> case Val of <<Bitstring:EffectiveSize/bits,_/bits>> -> @@ -1907,8 +1868,14 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, end; {float,Endian} -> try - [opt_bs_put_float(Val, EffectiveSize, Endian)] - catch error:_ -> + case Endian of + big -> + [<<Val:EffectiveSize/big-float-unit:1>>]; + little -> + [<<Val:EffectiveSize/little-float-unit:1>>] + end + catch + error:_ -> not_possible end; {_,_} -> @@ -1917,25 +1884,12 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, true -> not_possible end; -opt_bs_put(#b_set{}) -> not_possible. - -opt_bs_put_float(N, Sz, Endian) -> - case Endian of - big -> <<N:Sz/big-float-unit:1>>; - little -> <<N:Sz/little-float-unit:1>> - end. - -bs_put_args(Type, Val, Size) -> - [#b_literal{val=Type}, - #b_literal{val=[unsigned,big]}, - #b_literal{val=Val}, - #b_literal{val=Size}, - #b_literal{val=1}]. +opt_create_bin_arg(_, _, _, _, _) -> not_possible. -opt_bs_put_endian([big=E|_]) -> E; -opt_bs_put_endian([little=E|_]) -> E; -opt_bs_put_endian([native=E|_]) -> E; -opt_bs_put_endian([_|Fs]) -> opt_bs_put_endian(Fs). +opt_create_bin_endian([little=E|_]) -> E; +opt_create_bin_endian([native=E|_]) -> E; +opt_create_bin_endian([_|Fs]) -> opt_create_bin_endian(Fs); +opt_create_bin_endian([]) -> big. opt_bs_put_split_int(Int, Size) -> Pos = opt_bs_put_split_int_1(Int, 0, Size - 1), @@ -2512,7 +2466,6 @@ unsuitable(Linear, Blocks) -> unsuitable_1([{L,#b_blk{is=[#b_set{op=Op}=I|_]}}|Bs]) -> Unsuitable = case Op of bs_extract -> true; - bs_put -> true; {float,_} -> true; landingpad -> true; _ -> beam_ssa:is_loop_header(I) diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl index 23890447c5..2e804462ea 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -634,36 +634,7 @@ sanitize_is([#b_set{op=get_map_element,args=Args0}=I0|Is], I = I0#b_set{args=Args}, sanitize_is(Is, Last, Count0, Values, true, [I|Acc]) end; -sanitize_is([#b_set{op={succeeded,guard},dst=Dst,args=[Arg0]}=I0], - #b_br{bool=Dst}=Last, Count, Values, _Changed, Acc0) -> - %% We no longer need to distinguish between guard and body checks, so we'll - %% rewrite this as a plain 'succeeded'. - case sanitize_arg(Arg0, Values) of - #b_var{}=Arg -> - case Acc0 of - [#b_set{op=call, - args=[#b_remote{mod=#b_literal{val=erlang}, - name=#b_literal{val=error}, - arity=1},_], - dst=Arg0}|Acc] -> - %% This erlang:error/1 is the result from a - %% sanitized bs_add or bs_init instruction. Calls - %% to erlang:error/1 in receive is not allowed, so - %% we will have to rewrite this instruction - %% sequence to an unconditional branch to the - %% failure label. - Fail = Last#b_br.fail, - Br = #b_br{bool=#b_literal{val=true},succ=Fail,fail=Fail}, - {reverse(Acc), Br, Count, Values}; - _ -> - I = I0#b_set{op=succeeded,args=[Arg]}, - {reverse(Acc0, [I]), Last, Count, Values} - end; - #b_literal{} -> - Value = #b_literal{val=true}, - {reverse(Acc0), Last, Count, Values#{ Dst => Value }} - end; -sanitize_is([#b_set{op={succeeded,body},dst=Dst,args=[Arg0]}=I0], +sanitize_is([#b_set{op={succeeded,_Kind},dst=Dst,args=[Arg0]}=I0], #b_br{bool=Dst}=Last, Count, Values, _Changed, Acc) -> %% We no longer need to distinguish between guard and body checks, so we'll %% rewrite this as a plain 'succeeded'. @@ -803,31 +774,9 @@ sanitize_instr(is_tagged_tuple, [#b_literal{val=Tuple}, true -> {value,false} end; -sanitize_instr(bs_add, [Arg1,Arg2,_|_], I0) -> - case all(fun(#b_literal{val=Size}) -> is_integer(Size) andalso Size >= 0; - (#b_var{}) -> true - end, [Arg1,Arg2]) of - true -> ok; - false -> {ok,sanitize_badarg(I0)} - end; -sanitize_instr(bs_init, [#b_literal{val=new},#b_literal{val=Sz}|_], I0) -> - if - is_integer(Sz), Sz >= 0 -> ok; - true -> {ok,sanitize_badarg(I0)} - end; -sanitize_instr(bs_init, [#b_literal{},_,#b_literal{val=Sz}|_], I0) -> - if - is_integer(Sz), Sz >= 0 -> ok; - true -> {ok,sanitize_badarg(I0)} - end; sanitize_instr(_, _, _) -> ok. -sanitize_badarg(I) -> - Func = #b_remote{mod=#b_literal{val=erlang}, - name=#b_literal{val=error},arity=1}, - I#b_set{op=call,args=[Func,#b_literal{val=badarg}]}. - remove_unreachable([L|Ls], Blocks, Reachable, Acc) -> #b_blk{is=Is0} = Blk0 = map_get(L, Blocks), case split_phis(Is0) of diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 63dfa64199..b1ffd46255 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -32,7 +32,7 @@ -include("beam_ssa_opt.hrl"). -include("beam_types.hrl"). --import(lists, [all/2,any/2,duplicate/2,foldl/3,member/2, +-import(lists, [any/2,duplicate/2,foldl/3,member/2, keyfind/3,reverse/1,split/2,zip/2]). %% The maximum number of #b_ret{} terminators a function can have before @@ -1122,36 +1122,12 @@ will_succeed_1(#b_set{op=get_tuple_element}, _Src, _Ts, _Sub) -> yes; will_succeed_1(#b_set{op=put_tuple}, _Src, _Ts, _Sub) -> yes; - -%% Remove the success branch from binary operations with invalid -%% sizes. That will remove subsequent bs_put and bs_match instructions, -%% which are probably not loadable. -will_succeed_1(#b_set{op=bs_add,args=[Arg1,Arg2,_]}, - _Src, _Ts, _Sub) -> - case all(fun(#b_literal{val=Size}) -> is_integer(Size) andalso Size >= 0; - (#b_var{}) -> true - end, [Arg1,Arg2]) of - true -> maybe; - false -> no - end; -will_succeed_1(#b_set{op=bs_init, - args=[#b_literal{val=new},#b_literal{val=Size},_Unit]}, - _Src, _Ts, _Sub) -> - if - is_integer(Size), Size >= 0 -> - maybe; - true -> - no - end; -will_succeed_1(#b_set{op=bs_init, - args=[#b_literal{},_,#b_literal{val=Size},_Unit]}, - _Src, _Ts, _Sub) -> - if - is_integer(Size), Size >= 0 -> - maybe; - true -> - no - end; +will_succeed_1(#b_set{op=bs_create_bin}, _Src, _Ts, _Sub) -> + %% Intentionally don't try to determine whether construction will + %% fail. Construction is unlikely to fail, and if it fails, the + %% instruction in the runtime system will generate an exception with + %% better information of what went wrong. + maybe; will_succeed_1(#b_set{op=bs_match, args=[#b_literal{val=Type},_,_,#b_literal{val=Size},_]}, _Src, _Ts, _Sub) -> @@ -1741,7 +1717,7 @@ type({bif,Bif}, Args, _Anno, Ts, _Ds) -> ArgTypes = normalized_types(Args, Ts), {RetType, _, _} = beam_call_types:types(erlang, Bif, ArgTypes), RetType; -type(bs_init, _Args, _Anno, _Ts, _Ds) -> +type(bs_create_bin, _Args, _Anno, _Ts, _Ds) -> #t_bitstring{}; type(bs_extract, [Ctx], _Anno, _Ts, Ds) -> #b_set{op=bs_match,args=Args} = map_get(Ctx, Ds), diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl index 2be10832b9..a22753ddc4 100644 --- a/lib/compiler/src/beam_trim.erl +++ b/lib/compiler/src/beam_trim.erl @@ -270,14 +270,6 @@ remap([{get_map_elements,Fail,M,{list,L0}}|Is], Map, Acc) -> L = [Map(E) || E <- L0], I = {get_map_elements,Fail,Map(M),{list,L}}, remap(Is, Map, [I|Acc]); -remap([{bs_init,Fail,Info,Live,Ss0,Dst0}|Is], Map, Acc) -> - Ss = [Map(Src) || Src <- Ss0], - Dst = Map(Dst0), - I = {bs_init,Fail,Info,Live,Ss,Dst}, - remap(Is, Map, [I|Acc]); -remap([{bs_put=Op,Fail,Info,Ss}|Is], Map, Acc) -> - I = {Op,Fail,Info,[Map(S) || S <- Ss]}, - remap(Is, Map, [I|Acc]); remap([{init_yregs,{list,Yregs0}}|Is], Map, Acc) -> Yregs = sort([Map(Y) || Y <- Yregs0]), I = {init_yregs,{list,Yregs}}, @@ -418,10 +410,6 @@ frame_size([{test,_,{f,L},_}|Is], Safe) -> frame_size_branch(L, Is, Safe); frame_size([{test,_,{f,L},_,_,_}|Is], Safe) -> frame_size_branch(L, Is, Safe); -frame_size([{bs_init,{f,L},_,_,_,_}|Is], Safe) -> - frame_size_branch(L, Is, Safe); -frame_size([{bs_put,{f,L},_,_}|Is], Safe) -> - frame_size_branch(L, Is, Safe); frame_size([{init_yregs,_}|Is], Safe) -> frame_size(Is, Safe); frame_size([{make_fun2,_,_,_,_}|Is], Safe) -> @@ -480,10 +468,6 @@ is_not_used(Y, [{block,Bl}|Is]) -> end; is_not_used(Y, [{bs_get_tail,Src,Dst,_}|Is]) -> is_not_used_ss_dst(Y, [Src], Dst, Is); -is_not_used(Y, [{bs_init,_,_,_,Ss,Dst}|Is]) -> - is_not_used_ss_dst(Y, Ss, Dst, Is); -is_not_used(Y, [{bs_put,{f,_},_,Ss}|Is]) -> - not member(Y, Ss) andalso is_not_used(Y, Is); is_not_used(Y, [{bs_start_match4,_Fail,_Live,Src,Dst}|Is]) -> Y =/= Src andalso Y =/= Dst andalso is_not_used(Y, Is); diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 816c29bc55..e81868beae 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -97,6 +97,10 @@ replace_labels_1([{make_fun2,{f,Lbl},U1,U2,U3}|Is], Acc, D, Fb) -> replace_labels_1(Is, [{make_fun2,{f,label(Lbl, D, Fb)},U1,U2,U3}|Acc], D, Fb); replace_labels_1([{make_fun3,{f,Lbl},U1,U2,U3,U4}|Is], Acc, D, Fb) -> replace_labels_1(Is, [{make_fun3,{f,label(Lbl, D, Fb)},U1,U2,U3,U4}|Acc], D, Fb); +replace_labels_1([{bs_create_bin,{f,Lbl},Alloc,Live,Unit,Dst,{list,List}}|Is], Acc, D, Fb) + when Lbl =/= 0 -> + replace_labels_1(Is, [{bs_create_bin,{f,label(Lbl, D, Fb)}, + Alloc,Live,Unit,Dst,{list,List}}|Acc], D, Fb); replace_labels_1([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D, Fb) when Lbl =/= 0 -> replace_labels_1(Is, [{bs_init,{f,label(Lbl, D, Fb)},Info,Live,Ss,Dst}|Acc], D, Fb); replace_labels_1([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D, Fb) when Lbl =/= 0 -> diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 4c9122d2c6..050dc563eb 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -995,6 +995,18 @@ vi(raw_raise=I, Vst0) -> vi(bs_init_writable=I, Vst) -> validate_body_call(I, 1, Vst); +vi({bs_create_bin,{f,Fail},Heap,Live,Unit,Dst,{list,List}}, Vst0) -> + verify_live(Live, Vst0), + verify_y_init(Vst0), + verify_create_bin_list(List, Vst0), + Vst = heap_alloc(Heap, Vst0), + branch(Fail, Vst, + fun(SuccVst0) -> + SuccVst1 = update_create_bin_list(List, SuccVst0), + SuccVst = prune_x_regs(Live, SuccVst1), + create_term(#t_bitstring{size_unit=Unit}, bs_create_bin, [], Dst, + SuccVst, SuccVst1) + end); vi({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), verify_y_init(Vst0), @@ -1305,6 +1317,54 @@ pmt_1([Key0, Value0 | List], Vst, Acc0) -> pmt_1([], _Vst, Acc) -> Acc. +verify_create_bin_list([{atom,string},_Seg,Unit,Flags,Val,Size|Args], Vst) -> + assert_bs_unit({atom,string}, Unit), + assert_term(Flags, Vst), + case Val of + {string,Bs} when is_binary(Bs) -> ok; + _ -> error({not_string,Val}) + end, + assert_term(Flags, Vst), + assert_term(Size, Vst), + verify_create_bin_list(Args, Vst); +verify_create_bin_list([Type,_Seg,Unit,Flags,Val,Size|Args], Vst) -> + assert_term(Type, Vst), + assert_bs_unit(Type, Unit), + assert_term(Flags, Vst), + assert_term(Val, Vst), + assert_term(Size, Vst), + verify_create_bin_list(Args, Vst); +verify_create_bin_list([], _Vst) -> ok. + +update_create_bin_list([{atom,string},_Seg,_Unit,_Flags,_Val,_Size|T], Vst) -> + update_create_bin_list(T, Vst); +update_create_bin_list([{atom,Op},_Seg,_Unit,_Flags,Val,_Size|T], Vst0) -> + Type = update_create_bin_type(Op), + Vst = update_type(fun meet/2, Type, Val, Vst0), + update_create_bin_list(T, Vst); +update_create_bin_list([], Vst) -> Vst. + +update_create_bin_type(append) -> #t_bitstring{}; +update_create_bin_type(private_append) -> #t_bitstring{}; +update_create_bin_type(binary) -> #t_bitstring{}; +update_create_bin_type(float) -> #t_float{}; +update_create_bin_type(integer) -> #t_integer{}; +update_create_bin_type(utf8) -> #t_integer{}; +update_create_bin_type(utf16) -> #t_integer{}; +update_create_bin_type(utf32) -> #t_integer{}. + +assert_bs_unit({atom,Type}, 0) -> + case Type of + utf8 -> ok; + utf16 -> ok; + utf32 -> ok; + _ -> error({zero_unit_invalid_for_type,Type}) + end; +assert_bs_unit({atom,_Type}, Unit) when is_integer(Unit), 0 < Unit, Unit =< 256 -> + ok; +assert_bs_unit(_, Unit) -> + error({invalid,Unit}). + %% %% Common code for validating returns, whether naked or as part of a tail call. %% diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index 84f18a2d41..0ca67f0a72 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -87,32 +87,6 @@ undo_renames([{get_hd,Src,Hd},{get_tl,Src,Tl}|Is]) -> get_list(Src, Hd, Tl, Is); undo_renames([{get_tl,Src,Tl},{get_hd,Src,Hd}|Is]) -> get_list(Src, Hd, Tl, Is); -undo_renames([{bs_put,_,{bs_put_binary,1,_}, - [{atom,all},{literal,<<>>}]}|Is]) -> - undo_renames(Is); -undo_renames([{bs_put,Fail,{bs_put_binary,1,_Flags}, - [{atom,all},{literal,BinString}]}|Is0]) - when is_bitstring(BinString)-> - Bits = bit_size(BinString), - Bytes = Bits div 8, - case Bits rem 8 of - 0 -> - I = {bs_put_string,byte_size(BinString), - {string,BinString}}, - [undo_rename(I)|undo_renames(Is0)]; - Rem -> - <<Binary:Bytes/bytes,Int:Rem>> = BinString, - PutInt = {bs_put_integer,Fail,{integer,Rem},1, - {field_flags,[unsigned,big]},{integer,Int}}, - Is = [PutInt|undo_renames(Is0)], - case Binary of - <<>> -> - Is; - _ -> - [{bs_put_string,byte_size(Binary), - {string,Binary}}|Is] - end - end; undo_renames([I|Is]) -> [undo_rename(I)|undo_renames(Is)]; undo_renames([]) -> []. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index faed60e938..2eeff21714 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -272,9 +272,13 @@ expand_opt(no_bsm4, Os) -> %% that a match instruction won't fail. expand_opt(no_type_opt, Os); expand_opt(r22, Os) -> - expand_opt(r23, [no_shared_fun_wrappers, no_swap | expand_opt(no_bsm4, Os)]); + expand_opt(r23, [no_bs_create_bin, no_shared_fun_wrappers, + no_swap | expand_opt(no_bsm4, Os)]); expand_opt(r23, Os) -> - expand_opt(no_make_fun3, [no_ssa_opt_float, no_recv_opt, no_init_yregs | Os]); + expand_opt(no_make_fun3, [no_bs_create_bin, no_ssa_opt_float, + no_recv_opt, no_init_yregs | Os]); +expand_opt(r24, Os) -> + [no_bs_create_bin | Os]; expand_opt(no_make_fun3, Os) -> [no_make_fun3, no_fun_opt | Os]; expand_opt({debug_info_key,_}=O, Os) -> diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index ffdcea18a2..08f26ef5bd 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1162,7 +1162,7 @@ is_iexprs_small_2(_, Threshold) -> %% record whereas c_literal should not have a wrapped annotation expr_bin(Es0, Anno, St0) -> - Es1 = [bin_element(E) || E <- Es0], + Es1 = bin_elements(Es0, 1), case constant_bin(Es1) of error -> case expr_bin_1(Es1, St0) of @@ -1202,12 +1202,12 @@ bitstrs([E0|Es0], St0) -> bitstrs([], St) -> {[],[],St}. -bitstr({bin_element,Line,{string,_,S},{integer,_,8},_}, St) -> - bitstrs(bin_expand_string(S, Line, 0, 0, []), St); -bitstr({bin_element,Line,{string,_,[]},Sz0,Ts}, St0) -> +bitstr({bin_element,{sl,_,Line},{string,_,S},{integer,_,8},_}, St) -> + bitstrs(bin_expand_string(S, {sl,0,Line}, 0, 0, []), St); +bitstr({bin_element,{sl,_,Line},{string,_,[]},Sz0,Ts}, St0) -> %% Empty string. We must make sure that the type is correct. {[#c_bitstr{size=Sz}],Eps0,St1} = - bitstr({bin_element,Line,{char,Line,0},Sz0,Ts}, St0), + bitstr({bin_element,{sl,0,Line},{char,Line,0},Sz0,Ts}, St0), %% At this point, the type is either a correct literal or %% an expression. @@ -1234,12 +1234,12 @@ bitstr({bin_element,Line,{string,_,[]},Sz0,Ts}, St0) -> Eps = Eps0 ++ Eps1, {[],Eps,St2} end; -bitstr({bin_element,Line,{string,_,S},Sz0,Ts}, St0) -> - {[Bitstr],Eps,St1} = bitstr({bin_element,Line,{char,Line,0},Sz0,Ts}, St0), +bitstr({bin_element,{sl,_,Line},{string,_,S},Sz0,Ts}, St0) -> + {[Bitstr],Eps,St1} = bitstr({bin_element,{sl,0,Line},{char,Line,0},Sz0,Ts}, St0), Es = [Bitstr#c_bitstr{val=#c_literal{anno=full_anno(Line, St1),val=C}} || C <- S], {Es,Eps,St1}; -bitstr({bin_element,Line,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> +bitstr({bin_element,{sl,Seg,Line},E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> {E1,Eps0,St1} = safe(E0, St0), {Size1,Eps1,St2} = safe(Size0, St1), Eps = Eps0 ++ Eps1, @@ -1263,16 +1263,30 @@ bitstr({bin_element,Line,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> #c_literal{val=all} -> ok; _ -> throw({bad_binary,Eps,St2}) end, - {[#c_bitstr{anno=lineno_anno(Line, St2), + Anno0 = lineno_anno(Line, St2), + + %% We will add a 'segment' annotation to segments that could + %% fail. There is no need to add it to literal segments of fixed + %% sized. The annotation will be used by the runtime system to + %% provide extended error information if construction of the + %% binary fails. + Anno = if Seg =:= 0 -> + Anno0; + true -> + [{segment,Seg}|Anno0] + end, + + {[#c_bitstr{anno=Anno, val=E1,size=Size1, unit=#c_literal{val=Unit}, type=#c_literal{val=Type}, flags=#c_literal{val=Flags}}], Eps,St2}. -bin_element({bin_element,Line,Expr,Size0,Type0}) -> +bin_elements([{bin_element,Line,Expr,Size0,Type0}|Es], Seg) -> {Size,Type} = make_bit_type(Line, Size0, Type0), - {bin_element,Line,Expr,Size,Type}. + [{bin_element,{sl,Seg,Line},Expr,Size,Type}|bin_elements(Es, Seg+1)]; +bin_elements([], _) -> []. make_bit_type(Line, default, Type0) -> case erl_bits:set_bit_type(default, Type0) of @@ -1392,8 +1406,12 @@ bin_expand_string([H|T], Line, Val, Size, Last) -> bin_expand_string([], Line, Val, Size, Last) -> [make_combined(Line, Val, Size) | Last]. -make_combined(Line, Val, Size) -> - {bin_element,Line,{integer,Line,Val}, +make_combined(SegLine, Val, Size) -> + Line = case SegLine of + {sl,_,Line0} -> Line0; + _ -> SegLine + end, + {bin_element,SegLine,{integer,Line,Val}, {integer,Line,Size}, [integer,{unit,1},unsigned,big]}. diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 467a2be6e3..5ee52c7cb3 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -106,6 +106,11 @@ INLINE= \ R23= \ fun +R24= \ + bs_construct \ + bs_utf \ + bs_bincomp + DIALYZER = bs_match CORE_MODULES = \ @@ -128,6 +133,8 @@ INLINE_MODULES= $(INLINE:%=%_inline_SUITE) INLINE_ERL_FILES= $(INLINE_MODULES:%=%.erl) R23_MODULES= $(R23:%=%_r23_SUITE) R23_ERL_FILES= $(R23_MODULES:%=%.erl) +R24_MODULES= $(R24:%=%_r24_SUITE) +R24_ERL_FILES= $(R24_MODULES:%=%.erl) NO_MOD_OPT_MODULES= $(NO_MOD_OPT:%=%_no_module_opt_SUITE) NO_MOD_OPT_ERL_FILES= $(NO_MOD_OPT_MODULES:%=%.erl) NO_SSA_OPT_MODULES= $(NO_SSA_OPT:%=%_no_ssa_opt_SUITE) @@ -169,7 +176,7 @@ DISABLE_SSA_OPT = +no_bool_opt +no_share_opt +no_bsm_opt +no_fun_opt +no_ssa_opt make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES) \ $(NO_CORE_OPT_ERL_FILES) $(INLINE_ERL_FILES) $(R23_ERL_FILES) \ $(NO_MOD_OPT_ERL_FILES) $(NO_TYPE_OPT_ERL_FILES) \ - $(DIALYZER_ERL_FILES) + $(DIALYZER_ERL_FILES) $(R24_ERL_FILES) $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \ > $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +no_copt $(DISABLE_SSA_OPT) +no_postopt \ @@ -184,6 +191,8 @@ make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES -o$(EBIN) $(INLINE_MODULES) >> $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +r23 $(ERL_COMPILE_FLAGS) \ -o$(EBIN) $(R23_MODULES) >> $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile +r24 $(ERL_COMPILE_FLAGS) \ + -o$(EBIN) $(R24_MODULES) >> $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +no_module_opt $(ERL_COMPILE_FLAGS) \ -o$(EBIN) $(NO_MOD_OPT_MODULES) >> $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +from_core $(ERL_COMPILE_FLAGS) \ @@ -225,6 +234,9 @@ docs: %_r23_SUITE.erl: %_SUITE.erl sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@ +%_r24_SUITE.erl: %_SUITE.erl + sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@ + %_no_module_opt_SUITE.erl: %_SUITE.erl sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@ @@ -246,7 +258,9 @@ release_tests_spec: make_emakefile $(INSTALL_DATA) compiler.spec compiler.cover \ $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \ - $(INLINE_ERL_FILES) $(R23_ERL_FILES) \ + $(INLINE_ERL_FILES) \ + $(R23_ERL_FILES) \ + $(R24_ERL_FILES) \ $(NO_CORE_OPT_ERL_FILES) \ $(NO_MOD_OPT_ERL_FILES) \ $(NO_SSA_OPT_ERL_FILES) \ diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl index e27c668e81..b855d91bc3 100644 --- a/lib/compiler/test/bs_bincomp_SUITE.erl +++ b/lib/compiler/test/bs_bincomp_SUITE.erl @@ -387,6 +387,12 @@ sizes(Config) when is_list(Config) -> <<>> = Fun6([], 42), <<42,43:20>> = Fun6([42], 20), + Fun7 = fun(B) -> + cs_default(<< <<C/utf8>> || C <- B >>) + end, + <<"Foundation"/utf8>> = Fun7("Foundation"), + <<"Основание"/utf8>> = Fun7("Основание"), + %% Binary generators. Fun10 = fun(Bin) -> @@ -437,6 +443,12 @@ sizes(Config) when is_list(Config) -> <<$a:32,$b:32,$c:32,($a bsl 8 bor $b):32>> = Fun14([8,16], <<"abc">>), <<$a:32,$b:32,$c:32>> = Fun14([8,bad], <<"abc">>), + Fun15 = fun(B) -> + cs_default(<< <<C/utf8>> || << C:32 >> <= id(B) >>) + end, + <<"Foundation"/utf8>> = Fun15(<<"Foundation"/utf32>>), + <<"Основание"/utf8>> = Fun15(<<"Основание"/utf32>>), + {'EXIT',_} = (catch << <<C:4>> || <<C:8>> <= {1,2,3} >>), cs_end(), diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index a5f59e8bc3..7bf8bac2d9 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -72,10 +72,10 @@ end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> verify_highest_opcode(_Config) -> case ?MODULE of - bs_construct_r21_SUITE -> + bs_construct_r24_SUITE -> {ok,Beam} = file:read_file(code:which(?MODULE)), case test_lib:highest_opcode(Beam) of - Highest when Highest =< 163 -> + Highest when Highest =< 176 -> ok; TooHigh -> ct:fail({too_high_opcode_for_21,TooHigh}) diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl index 8a6071e71f..edfc9b00ec 100644 --- a/lib/compiler/test/test_lib.erl +++ b/lib/compiler/test/test_lib.erl @@ -87,7 +87,7 @@ opt_opts(Mod) -> (debug_info) -> true; (dialyzer) -> true; (inline) -> true; - (no_bsm3) -> true; + (no_bs_create_bin) -> true; (no_bsm_opt) -> true; (no_copt) -> true; (no_fun_opt) -> true; @@ -95,11 +95,10 @@ opt_opts(Mod) -> (no_make_fun3) -> true; (no_module_opt) -> true; (no_postopt) -> true; - (no_put_tuple2) -> true; (no_recv_opt) -> true; (no_share_opt) -> true; (no_shared_fun_wrappers) -> true; - (no_ssa_float) -> true; + (no_ssa_opt_float) -> true; (no_ssa_opt) -> true; (no_stack_trimming) -> true; (no_swap) -> true; @@ -136,7 +135,8 @@ is_cloned_mod_1("_no_copt_SUITE") -> true; is_cloned_mod_1("_no_ssa_opt_SUITE") -> true; is_cloned_mod_1("_post_opt_SUITE") -> true; is_cloned_mod_1("_inline_SUITE") -> true; -is_cloned_mod_1("_21_SUITE") -> true; +is_cloned_mod_1("_23_SUITE") -> true; +is_cloned_mod_1("_24_SUITE") -> true; is_cloned_mod_1("_no_module_opt_SUITE") -> true; is_cloned_mod_1([_|T]) -> is_cloned_mod_1(T); is_cloned_mod_1([]) -> false. |