diff options
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/src/beam_a.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/beam_receive.erl | 92 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 98 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 23 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 51 | ||||
-rw-r--r-- | lib/compiler/test/andor_SUITE.erl | 33 | ||||
-rw-r--r-- | lib/compiler/test/bs_bincomp_SUITE.erl | 3 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 10 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE_data/key_compatibility.beam | bin | 0 -> 844 bytes | |||
-rw-r--r-- | lib/compiler/test/compile_SUITE_data/key_compatibility.erl | 8 | ||||
-rw-r--r-- | lib/compiler/test/receive_SUITE.erl | 34 | ||||
-rw-r--r-- | lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S | 71 | ||||
-rw-r--r-- | lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl | 6 |
13 files changed, 288 insertions, 145 deletions
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index 1c51226314..b348e854a0 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -70,8 +70,8 @@ rename_instr({bs_put_utf16=I,F,Fl,Src}) -> {bs_put,F,{I,Fl},[Src]}; rename_instr({bs_put_utf32=I,F,Fl,Src}) -> {bs_put,F,{I,Fl},[Src]}; -%% rename_instr({bs_put_string,_,_}=I) -> -%% {bs_put,{f,0},I,[]}; +rename_instr({bs_put_string,_,_}=I) -> + {bs_put,{f,0},I,[]}; rename_instr({bs_add=I,F,[Src1,Src2,U],Dst}) when is_integer(U) -> {bif,I,F,[Src1,Src2,{integer,U}],Dst}; rename_instr({bs_utf8_size=I,F,Src,Dst}) -> diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl index 3dd5ed182e..97a9188ee7 100644 --- a/lib/compiler/src/beam_receive.erl +++ b/lib/compiler/src/beam_receive.erl @@ -151,20 +151,20 @@ opt_recv(Is, Regs, D) -> opt_recv([{label,L}=Lbl,{loop_rec,{f,Fail},_}=Loop|Is], D, R0, _, Acc) -> R = regs_kill_not_live(0, R0), - case regs_to_list(R) of - [{y,_}=RefReg] -> - %% We now have the new reference in the Y register RefReg + case regs_empty(R) of + false -> + %% We now have the new reference in Y registers %% and the current instruction is the beginning of a %% receive statement. We must now verify that only messages %% that contain the reference will be matched. - case opt_ref_used(Is, RefReg, Fail, D) of + case opt_ref_used(Is, R, Fail, D) of false -> no; true -> RecvSet = {recv_set,{f,L}}, {yes,reverse(Acc, [RecvSet,Lbl,Loop|Is]),L} end; - [] -> + true -> no end; opt_recv([I|Is], D, R0, L0, Acc) -> @@ -226,9 +226,9 @@ opt_update_regs_bl([{set,Ds,_,_}|Is], Regs0) -> opt_update_regs_bl(Is, Regs); opt_update_regs_bl([], Regs) -> Regs. -%% opt_ref_used([Instruction], RefRegister, FailLabel, LabelIndex) -> true|false +%% opt_ref_used([Instruction], RefRegs, FailLabel, LabelIndex) -> true|false %% Return 'true' if it is certain that only messages that contain the same -%% reference as in RefRegister can be matched out. Otherwise return 'false'. +%% reference as in RefRegs can be matched out. Otherwise return 'false'. %% %% Basically, we follow all possible paths through the receive statement. %% If all paths are safe, we return 'true'. @@ -236,7 +236,7 @@ opt_update_regs_bl([], Regs) -> Regs. %% A branch to FailLabel is safe, because it exits the receive statement %% and no further message may be matched out. %% -%% If a path hits an comparision between RefRegister and part of the message, +%% If a path hits an comparision between RefRegs and part of the message, %% that path is safe (any messages that may be matched further down the %% path is guaranteed to contain the reference). %% @@ -245,11 +245,11 @@ opt_update_regs_bl([], Regs) -> Regs. %% we hit an unrecognized instruction, we also give up and return %% 'false' (the optimization may be unsafe). -opt_ref_used(Is, RefReg, Fail, D) -> +opt_ref_used(Is, RefRegs, Fail, D) -> Done = gb_sets:singleton(Fail), Regs = regs_init_x0(), try - _ = opt_ref_used_1(Is, RefReg, D, Done, Regs), + _ = opt_ref_used_1(Is, RefRegs, D, Done, Regs), true catch throw:not_used -> @@ -258,37 +258,39 @@ opt_ref_used(Is, RefReg, Fail, D) -> %% This functions only returns if all paths through the receive %% statement are safe, and throws an 'not_used' term otherwise. -opt_ref_used_1([{block,Bl}|Is], RefReg, D, Done, Regs0) -> +opt_ref_used_1([{block,Bl}|Is], RefRegs, D, Done, Regs0) -> Regs = opt_ref_used_bl(Bl, Regs0), - opt_ref_used_1(Is, RefReg, D, Done, Regs); -opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefReg, Regs) of + opt_ref_used_1(Is, RefRegs, D, Done, Regs); +opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], + RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), + case is_ref_msg_comparison(Args, RefRegs, Regs) of false -> - opt_ref_used_1(Is, RefReg, D, Done, Regs); + opt_ref_used_1(Is, RefRegs, D, Done, Regs); true -> %% The instructions that follow (Is) can only be executed - %% if the message contains the same reference as in RefReg. + %% if the message contains the same reference as in RefRegs. Done end; -opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefReg, Regs) of +opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], + RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), + case is_ref_msg_comparison(Args, RefRegs, Regs) of false -> - opt_ref_used_at(Fail, RefReg, D, Done, Regs); + opt_ref_used_at(Fail, RefRegs, D, Done, Regs); true -> Done end; -opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs), - opt_ref_used_1(Is, RefReg, D, Done, Regs); -opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefReg, D, Done, Regs) -> +opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), + opt_ref_used_1(Is, RefRegs, D, Done, Regs); +opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefRegs, D, Done, Regs) -> Lbls = [F || {f,F} <- List] ++ [Fail], - opt_ref_used_in_all(Lbls, RefReg, D, Done, Regs); -opt_ref_used_1([{label,Lbl}|Is], RefReg, D, Done, Regs) -> + opt_ref_used_in_all(Lbls, RefRegs, D, Done, Regs); +opt_ref_used_1([{label,Lbl}|Is], RefRegs, D, Done, Regs) -> case gb_sets:is_member(Lbl, Done) of true -> Done; - false -> opt_ref_used_1(Is, RefReg, D, Done, Regs) + false -> opt_ref_used_1(Is, RefRegs, D, Done, Regs) end; opt_ref_used_1([{loop_rec_end,_}|_], _, _, Done, _) -> Done; @@ -296,27 +298,25 @@ opt_ref_used_1([_I|_], _RefReg, _D, _Done, _Regs) -> %% The optimization may be unsafe. throw(not_used). -%% is_ref_msg_comparison(Args, RefReg, RegisterSet) -> true|false. +%% is_ref_msg_comparison(Args, RefRegs, RegisterSet) -> true|false. %% Return 'true' if Args denotes a comparison between the %% reference and message or part of the message. -is_ref_msg_comparison([R,RefReg], RefReg, Regs) -> - regs_is_member(R, Regs); -is_ref_msg_comparison([RefReg,R], RefReg, Regs) -> - regs_is_member(R, Regs); -is_ref_msg_comparison([_,_], _, _) -> false. - -opt_ref_used_in_all([L|Ls], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(L, RefReg, D, Done0, Regs), - opt_ref_used_in_all(Ls, RefReg, D, Done, Regs); +is_ref_msg_comparison([R1,R2], RefRegs, Regs) -> + (regs_is_member(R2, RefRegs) andalso regs_is_member(R1, Regs)) orelse + (regs_is_member(R1, RefRegs) andalso regs_is_member(R2, Regs)). + +opt_ref_used_in_all([L|Ls], RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(L, RefRegs, D, Done0, Regs), + opt_ref_used_in_all(Ls, RefRegs, D, Done, Regs); opt_ref_used_in_all([], _, _, Done, _) -> Done. -opt_ref_used_at(Fail, RefReg, D, Done0, Regs) -> +opt_ref_used_at(Fail, RefRegs, D, Done0, Regs) -> case gb_sets:is_member(Fail, Done0) of true -> Done0; false -> Is = beam_utils:code_at(Fail, D), - Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs), + Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), gb_sets:add(Fail, Done) end. @@ -408,15 +408,3 @@ regs_all_members([], _) -> true. regs_is_member({x,N}, {Regs,_}) -> Regs band (1 bsl N) =/= 0; regs_is_member({y,N}, {_,Regs}) -> Regs band (1 bsl N) =/= 0; regs_is_member(_, _) -> false. - -%% regs_to_list(RegisterSet) -> [Register] -%% Convert the register set to an explicit list of registers. -regs_to_list({Xregs,Yregs}) -> - regs_to_list_1(Xregs, 0, x, regs_to_list_1(Yregs, 0, y, [])). - -regs_to_list_1(0, _, _, Acc) -> - Acc; -regs_to_list_1(Regs, N, Tag, Acc) when (Regs band 1) =:= 1 -> - regs_to_list_1(Regs bsr 1, N+1, Tag, [{Tag,N}|Acc]); -regs_to_list_1(Regs, N, Tag, Acc) -> - regs_to_list_1(Regs bsr 1, N+1, Tag, Acc). diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 8af0447f63..e623bcc6a5 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -61,7 +61,7 @@ is_killed_block(R, Is) -> %% to determine the kill state across branches. is_killed(R, Is, D) -> - St = #live{bl=fun check_killed_block/2,lbl=D,res=gb_trees:empty()}, + St = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()}, case check_liveness(R, Is, St) of {killed,_} -> true; {used,_} -> false; @@ -72,7 +72,7 @@ is_killed(R, Is, D) -> %% Determine whether Reg is killed at label Lbl. is_killed_at(R, Lbl, D) when is_integer(Lbl) -> - St0 = #live{bl=fun check_killed_block/2,lbl=D,res=gb_trees:empty()}, + St0 = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()}, case check_liveness_at(R, Lbl, St0) of {killed,_} -> true; {used,_} -> false; @@ -87,7 +87,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) -> %% across branches. is_not_used(R, Is, D) -> - St = #live{bl=check_used_block_fun(D),lbl=D,res=gb_trees:empty()}, + St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()}, case check_liveness(R, Is, St) of {killed,_} -> true; {used,_} -> false; @@ -102,7 +102,7 @@ is_not_used(R, Is, D) -> %% across branches. is_not_used_at(R, Lbl, D) -> - St = #live{bl=check_used_block_fun(D),lbl=D,res=gb_trees:empty()}, + St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()}, case check_liveness_at(R, Lbl, St) of {killed,_} -> true; {used,_} -> false; @@ -246,10 +246,10 @@ combine_heap_needs(H1, H2) when is_integer(H1), is_integer(H2) -> check_liveness(R, [{set,_,_,_}=I|_], St) -> erlang:error(only_allowed_in_blocks, [R,I,St]); -check_liveness(R, [{block,Blk}|Is], #live{bl=BlockCheck}=St) -> - case BlockCheck(R, Blk) of - transparent -> check_liveness(R, Is, St); - Other when is_atom(Other) -> {Other,St} +check_liveness(R, [{block,Blk}|Is], #live{bl=BlockCheck}=St0) -> + case BlockCheck(R, Blk, St0) of + {transparent,St} -> check_liveness(R, Is, St); + {Other,_}=Res when is_atom(Other) -> Res end; check_liveness(R, [{label,_}|Is], St) -> check_liveness(R, Is, St); @@ -533,6 +533,9 @@ check_liveness_fail(R, Op, Args, Fail, St) -> %% %% (Unknown instructions will cause an exception.) +check_killed_block_fun() -> + fun(R, Is, St) -> {check_killed_block(R, Is),St} end. + check_killed_block({x,X}, [{set,_,_,{alloc,Live,_}}|_]) -> if X >= Live -> killed; @@ -563,50 +566,51 @@ check_killed_block(_, []) -> transparent. %% %% (Unknown instructions will cause an exception.) -check_used_block_fun(D) -> - fun(R, Is) -> check_used_block(R, Is, D) end. - -check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], D) -> +check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St) -> if - X >= Live -> killed; + X >= Live -> {killed,St}; + true -> check_used_block_1(R, Ss, Ds, Op, Is, St) + end; +check_used_block(R, [{set,Ds,Ss,Op}|Is], St) -> + check_used_block_1(R, Ss, Ds, Op, Is, St); +check_used_block(R, [{'%live',Live}|Is], St) -> + case R of + {x,X} when X >= Live -> {killed,St}; + _ -> check_used_block(R, Is, St) + end; +check_used_block(_, [], St) -> {transparent,St}. + +check_used_block_1(R, Ss, Ds, Op, Is, St0) -> + case member(R, Ss) of true -> - case member(R, Ss) orelse - is_reg_used_at(R, Op, D) of - true -> used; - false -> + {used,St0}; + false -> + case is_reg_used_at(R, Op, St0) of + {true,St} -> + {used,St}; + {false,St} -> case member(R, Ds) of - true -> killed; - false -> check_used_block(R, Is, D) + true -> {killed,St}; + false -> check_used_block(R, Is, St) end end - end; -check_used_block(R, [{set,Ds,Ss,Op}|Is], D) -> - case member(R, Ss) orelse - is_reg_used_at(R, Op, D) of - true -> used; - false -> - case member(R, Ds) of - true -> killed; - false -> check_used_block(R, Is, D) - end - end; -check_used_block(R, [{'%live',Live}|Is], D) -> - case R of - {x,X} when X >= Live -> killed; - _ -> check_used_block(R, Is, D) - end; -check_used_block(_, [], _) -> transparent. + end. -is_reg_used_at(R, {gc_bif,_,{f,Lbl}}, D) -> - is_reg_used_at_1(R, Lbl, D); -is_reg_used_at(R, {bif,_,{f,Lbl}}, D) -> - is_reg_used_at_1(R, Lbl, D); -is_reg_used_at(_, _, _) -> false. +is_reg_used_at(R, {gc_bif,_,{f,Lbl}}, St) -> + is_reg_used_at_1(R, Lbl, St); +is_reg_used_at(R, {bif,_,{f,Lbl}}, St) -> + is_reg_used_at_1(R, Lbl, St); +is_reg_used_at(_, _, St) -> + {false,St}. -is_reg_used_at_1(_, 0, _) -> - false; -is_reg_used_at_1(R, Lbl, D) -> - not is_not_used_at(R, Lbl, D). +is_reg_used_at_1(_, 0, St) -> + {false,St}; +is_reg_used_at_1(R, Lbl, St0) -> + case check_liveness_at(R, Lbl, St0) of + {killed,St} -> {false,St}; + {used,St} -> {true,St}; + {unknown,St} -> {true,St} + end. index_labels_1([{label,Lbl}|Is0], Acc) -> Is = lists:dropwhile(fun({label,_}) -> true; @@ -730,6 +734,8 @@ live_opt([{loop_rec,_Fail,_Dst}=I|Is], _, D, Acc) -> live_opt(Is, 0, D, [I|Acc]); live_opt([timeout=I|Is], _, D, Acc) -> live_opt(Is, 0, D, [I|Acc]); +live_opt([{wait,_}=I|Is], _, D, Acc) -> + live_opt(Is, 0, D, [I|Acc]); %% Transparent instructions - they neither use nor modify x registers. live_opt([{deallocate,_}=I|Is], Regs, D, Acc) -> @@ -740,8 +746,6 @@ live_opt([{try_end,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); -live_opt([{wait,_}=I|Is], Regs, D, Acc) -> - live_opt(Is, Regs, D, [I|Acc]); live_opt([{wait_timeout,_,{Tag,_}}=I|Is], Regs, D, Acc) when Tag =/= x -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{line,_}=I|Is], Regs, D, Acc) -> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 497af2b52c..2ca403de54 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -599,7 +599,8 @@ standard_passes() -> core_passes() -> %% Optimization and transforms of Core Erlang code. - [{delay, + [{iff,clint0,?pass(core_lint_module)}, + {delay, [{unless,no_copt, [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1}, {iff,doldinline,{listing,"oldinline"}}, @@ -1196,9 +1197,9 @@ abstract_code(#compile{code=Code,options=Opts,ofile=OFile}) -> encrypt_abs_code(Abstr, Key0) -> try - {Mode,RealKey} = generate_key(Key0), + RealKey = generate_key(Key0), case start_crypto() of - ok -> {ok,encrypt(Mode, RealKey, Abstr)}; + ok -> {ok,encrypt(RealKey, Abstr)}; {error,_}=E -> E end catch @@ -1215,19 +1216,19 @@ start_crypto() -> {error,[{none,?MODULE,no_crypto}]} end. -generate_key({Mode,String}) when is_atom(Mode), is_list(String) -> - {Mode,beam_lib:make_crypto_key(Mode, String)}; +generate_key({Type,String}) when is_atom(Type), is_list(String) -> + beam_lib:make_crypto_key(Type, String); generate_key(String) when is_list(String) -> generate_key({des3_cbc,String}). -encrypt(des3_cbc=Mode, {K1,K2,K3, IVec}, Bin0) -> - Bin1 = case byte_size(Bin0) rem 8 of +encrypt({des3_cbc=Type,Key,IVec,BlockSize}, Bin0) -> + Bin1 = case byte_size(Bin0) rem BlockSize of 0 -> Bin0; - N -> list_to_binary([Bin0,random_bytes(8-N)]) + N -> list_to_binary([Bin0,random_bytes(BlockSize-N)]) end, - Bin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin1), - ModeString = atom_to_list(Mode), - list_to_binary([0,length(ModeString),ModeString,Bin]). + Bin = crypto:block_encrypt(Type, Key, IVec, Bin1), + TypeString = atom_to_list(Type), + list_to_binary([0,length(TypeString),TypeString,Bin]). random_bytes(N) -> {A,B,C} = now(), diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 01042cc56f..eea54b30a2 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1187,9 +1187,9 @@ list_gen_pattern(P0, Line, St) -> bc_initial_size(E, Q, St0) -> try - {ElemSzExpr,ElemSzPre,St1} = bc_elem_size(E, St0), + {ElemSzExpr,ElemSzPre,EVs,St1} = bc_elem_size(E, St0), {V,St2} = new_var(St1), - {GenSzExpr,GenSzPre,St3} = bc_gen_size(Q, St2), + {GenSzExpr,GenSzPre,St3} = bc_gen_size(Q, EVs, St2), case ElemSzExpr of #c_literal{val=ElemSz} when ElemSz rem 8 =:= 0 -> NumBytesExpr = #c_literal{val=ElemSz div 8}, @@ -1214,11 +1214,13 @@ bc_initial_size(E, Q, St0) -> bc_elem_size({bin,_,El}, St0) -> case bc_elem_size_1(El, 0, []) of {Bits,[]} -> - {#c_literal{val=Bits},[],St0}; + {#c_literal{val=Bits},[],[],St0}; {Bits,Vars0} -> [{U,V0}|Pairs] = sort(Vars0), F = bc_elem_size_combine(Pairs, U, [V0], []), - bc_mul_pairs(F, #c_literal{val=Bits}, [], St0) + Vs = [V || {_,#c_var{name=V}} <- Vars0], + {E,Pre,St} = bc_mul_pairs(F, #c_literal{val=Bits}, [], St0), + {E,Pre,Vs,St} end. bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) -> @@ -1260,11 +1262,11 @@ bc_add_list_1([H|T], Pre, E, St0) -> bc_add_list_1([], Pre, E, St) -> {E,reverse(Pre),St}. -bc_gen_size(Q, St) -> - bc_gen_size_1(Q, #c_literal{val=1}, [], St). +bc_gen_size(Q, EVs, St) -> + bc_gen_size_1(Q, EVs, #c_literal{val=1}, [], St). -bc_gen_size_1([{generate,L,El,Gen}|Qs], E0, Pre0, St0) -> - bc_verify_non_filtering(El), +bc_gen_size_1([{generate,L,El,Gen}|Qs], EVs, E0, Pre0, St0) -> + bc_verify_non_filtering(El, EVs), case Gen of {var,_,ListVar} -> Lanno = lineno_anno(L, St0), @@ -1275,16 +1277,16 @@ bc_gen_size_1([{generate,L,El,Gen}|Qs], E0, Pre0, St0) -> name=#c_literal{val=length}, args=[#c_var{name=ListVar}]}}, {E,Pre,St} = bc_gen_size_mul(E0, LenVar, [Set|Pre0], St1), - bc_gen_size_1(Qs, E, Pre, St); + bc_gen_size_1(Qs, EVs, E, Pre, St); _ -> %% The only expressions we handle is literal lists. Len = bc_list_length(Gen, 0), {E,Pre,St} = bc_gen_size_mul(E0, #c_literal{val=Len}, Pre0, St0), - bc_gen_size_1(Qs, E, Pre, St) + bc_gen_size_1(Qs, EVs, E, Pre, St) end; -bc_gen_size_1([{b_generate,_,El,Gen}|Qs], E0, Pre0, St0) -> - bc_verify_non_filtering(El), - {MatchSzExpr,Pre1,St1} = bc_elem_size(El, St0), +bc_gen_size_1([{b_generate,_,El,Gen}|Qs], EVs, E0, Pre0, St0) -> + bc_verify_non_filtering(El, EVs), + {MatchSzExpr,Pre1,_,St1} = bc_elem_size(El, St0), Pre2 = reverse(Pre1, Pre0), {ResVar,St2} = new_var(St1), {BitSizeExpr,Pre3,St3} = bc_gen_bit_size(Gen, Pre2, St2), @@ -1292,10 +1294,10 @@ bc_gen_size_1([{b_generate,_,El,Gen}|Qs], E0, Pre0, St0) -> MatchSzExpr)}, Pre4 = [Div|Pre3], {E,Pre,St} = bc_gen_size_mul(E0, ResVar, Pre4, St3), - bc_gen_size_1(Qs, E, Pre, St); -bc_gen_size_1([], E, Pre, St) -> + bc_gen_size_1(Qs, EVs, E, Pre, St); +bc_gen_size_1([], _, E, Pre, St) -> {E,reverse(Pre),St}; -bc_gen_size_1(_, _, _, _) -> +bc_gen_size_1(_, _, _, _, _) -> throw(impossible). bc_gen_bit_size({var,L,V}, Pre0, St0) -> @@ -1312,13 +1314,20 @@ bc_gen_bit_size({bin,_,_}=Bin, Pre, St) -> bc_gen_bit_size(_, _, _) -> throw(impossible). -bc_verify_non_filtering({bin,_,Els}) -> - foreach(fun({bin_element,_,{var,_,_},_,_}) -> ok; +bc_verify_non_filtering({bin,_,Els}, EVs) -> + foreach(fun({bin_element,_,{var,_,V},_,_}) -> + case member(V, EVs) of + true -> throw(impossible); + false -> ok + end; (_) -> throw(impossible) end, Els); -bc_verify_non_filtering({var,_,_}) -> - ok; -bc_verify_non_filtering(_) -> +bc_verify_non_filtering({var,_,V}, EVs) -> + case member(V, EVs) of + true -> throw(impossible); + false -> ok + end; +bc_verify_non_filtering(_, _) -> throw(impossible). bc_list_length({string,_,Str}, Len) -> diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl index 0d00769704..4ffbe07e32 100644 --- a/lib/compiler/test/andor_SUITE.erl +++ b/lib/compiler/test/andor_SUITE.erl @@ -21,7 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, t_case/1,t_and_or/1,t_andalso/1,t_orelse/1,inside/1,overlap/1, - combined/1,in_case/1,before_and_inside_if/1]). + combined/1,in_case/1,before_and_inside_if/1, + slow_compilation/1]). -include_lib("test_server/include/test_server.hrl"). @@ -472,6 +473,36 @@ before_and_inside_if_2(XDo1, XDo2, Do3) -> end, {CH1,CH2}. + +-record(state, {stack = []}). + +slow_compilation(_) -> + %% The function slow_compilation_1 used to compile very slowly. + ok = slow_compilation_1({a}, #state{}). + +slow_compilation_1(T1, #state{stack = [T2|_]}) + when element(1, T2) == a, element(1, T1) == b, element(1, T1) == c -> + ok; +slow_compilation_1(T, _) + when element(1, T) == a1; element(1, T) == b1; element(1, T) == c1 -> + ok; +slow_compilation_1(T, _) + when element(1, T) == a2; element(1, T) == b2; element(1, T) == c2 -> + ok; +slow_compilation_1(T, _) when element(1, T) == a -> + ok; +slow_compilation_1(T, _) + when + element(1, T) == a, + (element(1, T) == b) and (element(1, T) == c) -> + ok; +slow_compilation_1(_, T) when element(1, T) == a -> + ok; +slow_compilation_1(_, T) when element(1, T) == b -> + ok; +slow_compilation_1(T, _) when element(1, T) == a -> + ok. + %% Utilities. check(V1, V0) -> diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl index d39e340429..451a9b1e3b 100644 --- a/lib/compiler/test/bs_bincomp_SUITE.erl +++ b/lib/compiler/test/bs_bincomp_SUITE.erl @@ -282,6 +282,9 @@ sizes(Config) when is_list(Config) -> ?line <<1,2,3,0>> = Fun13(7), ?line <<1,2,3,0,0>> = Fun13(8), + <<0:3>> = cs_default(<< <<0:S>> || S <- [0,1,2] >>), + <<0:3>> = cs_default(<< <<0:S>> || <<S>> <= <<0,1,2>> >>), + ?line {'EXIT',_} = (catch << <<C:4>> || <<C:8>> <= {1,2,3} >>), ?line cs_end(), diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 229e5a98a1..c635d13c89 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -492,6 +492,16 @@ encrypted_abstr_1(Simple, Target) -> ?line {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = beam_lib:chunks("simple.beam", [abstract_code]), ?line ok = file:set_cwd(OldCwd), + + %% Test key compatibility by reading a BEAM file produced before + %% the update to the new crypto functions. + install_crypto_key("an old key"), + KeyCompat = filename:join(filename:dirname(Simple), + "key_compatibility"), + {ok,{key_compatibility,[Chunk]}} = beam_lib:chunks(KeyCompat, + [abstract_code]), + {abstract_code,{raw_abstract_v1,_}} = Chunk, + ok. diff --git a/lib/compiler/test/compile_SUITE_data/key_compatibility.beam b/lib/compiler/test/compile_SUITE_data/key_compatibility.beam Binary files differnew file mode 100644 index 0000000000..28329d2423 --- /dev/null +++ b/lib/compiler/test/compile_SUITE_data/key_compatibility.beam diff --git a/lib/compiler/test/compile_SUITE_data/key_compatibility.erl b/lib/compiler/test/compile_SUITE_data/key_compatibility.erl new file mode 100644 index 0000000000..e2931f1b12 --- /dev/null +++ b/lib/compiler/test/compile_SUITE_data/key_compatibility.erl @@ -0,0 +1,8 @@ +-module(key_compatibility). +-export([main/0]). + +%% Compile like this: +%% erlc +'{debug_info_key,"an old key"}' key_compatibility.erl + +main() -> + ok. diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index b91f2922fb..ec49267ded 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -23,7 +23,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1]). + export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1, + wait/1]). -include_lib("test_server/include/test_server.hrl"). @@ -44,7 +45,7 @@ all() -> groups() -> [{p,test_lib:parallel(), - [recv,coverage,otp_7980,ref_opt,export]}]. + [recv,coverage,otp_7980,ref_opt,export,wait]}]. init_per_suite(Config) -> @@ -188,7 +189,7 @@ ref_opt(Config) when is_list(Config) -> ref_opt_1(Config) -> ?line DataDir = ?config(data_dir, Config), ?line PrivDir = ?config(priv_dir, Config), - ?line Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.erl"])), + Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])), ?line test_lib:p_run(fun(Src) -> do_ref_opt(Src, PrivDir) end, Sources), @@ -196,10 +197,15 @@ ref_opt_1(Config) -> do_ref_opt(Source, PrivDir) -> try - {ok,Mod} = c:c(Source, [{outdir,PrivDir}]), + Ext = filename:extension(Source), + {ok,Mod} = compile:file(Source, [report_errors,report_warnings, + {outdir,PrivDir}] ++ + [from_asm || Ext =:= ".S" ]), + Base = filename:rootname(filename:basename(Source), Ext), + code:purge(list_to_atom(Base)), + BeamFile = filename:join(PrivDir, Base), + code:load_abs(BeamFile), ok = Mod:Mod(), - Base = filename:rootname(filename:basename(Source), ".erl"), - BeamFile = filename:join(PrivDir, Base), {beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile), case Base of "no_"++_ -> @@ -247,4 +253,20 @@ export_1(Reference) -> id({build,self()}), Result. +wait(Config) when is_list(Config) -> + self() ! <<42>>, + <<42>> = wait_1(r, 1, 2), + {1,2,3} = wait_1(1, 2, 3), + ok. + +wait_1(r, _, _) -> + receive + B when byte_size(B) > 0 -> + B + end; +%% beam_utils would wrongly assume that wait/1 could fall through +%% to the next clause. +wait_1(A, B, C) -> + {A,B,C}. + id(I) -> I. diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S new file mode 100644 index 0000000000..fd14228135 --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S @@ -0,0 +1,71 @@ +{module, yes_14}. %% version = 0 + +{exports, [{f,2},{module_info,0},{module_info,1},{yes_14,0}]}. + +{attributes, []}. + +{labels, 12}. + + +{function, yes_14, 0, 2}. + {label,1}. + {func_info,{atom,yes_14},{atom,yes_14},0}. + {label,2}. + {move,{atom,ok},{x,0}}. + return. + + +{function, f, 2, 4}. + {label,3}. + {func_info,{atom,yes_14},{atom,f},2}. + {label,4}. + {allocate_heap,2,3,2}. + {move,{x,0},{y,1}}. + {put_tuple,2,{y,0}}. + {put,{atom,data}}. + {put,{x,1}}. + {call_ext,0,{extfunc,erlang,make_ref,0}}. % Ref in [x0] + {test_heap,4,1}. + {put_tuple,3,{x,1}}. + {put,{atom,request}}. + {put,{x,0}}. + {put,{y,0}}. + {move,{x,0},{y,0}}. % Ref in [x0,y0] + {move,{y,1},{x,0}}. % Ref in [y0] + {kill,{y,1}}. + send. + {move,{y,0},{x,0}}. % Ref in [x0,y0] + {move,{x,0},{y,1}}. % Ref in [x0,y0,y1] + {label,5}. + {loop_rec,{f,7},{x,0}}. % Ref in [y0,y1] + {test,is_tuple,{f,6},[{x,0}]}. + {test,test_arity,{f,6},[{x,0},2]}. + {get_tuple_element,{x,0},0,{x,1}}. + {get_tuple_element,{x,0},1,{x,2}}. + {test,is_eq_exact,{f,6},[{x,1},{atom,reply}]}. + {test,is_eq_exact,{f,6},[{x,2},{y,1}]}. + remove_message. + {move,{atom,ok},{x,0}}. + {deallocate,2}. + return. + {label,6}. + {loop_rec_end,{f,5}}. + {label,7}. + {wait,{f,5}}. + + +{function, module_info, 0, 9}. + {label,8}. + {func_info,{atom,yes_14},{atom,module_info},0}. + {label,9}. + {move,{atom,yes_14},{x,0}}. + {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. + + +{function, module_info, 1, 11}. + {label,10}. + {func_info,{atom,yes_14},{atom,module_info},1}. + {label,11}. + {move,{x,0},{x,1}}. + {move,{atom,yes_14},{x,0}}. + {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl index 3f02fba6a6..5070e3e546 100644 --- a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl @@ -24,11 +24,7 @@ do_call(Process, Label, Request, Timeout) -> {'DOWN', Mref, _, _, Reason} -> exit(Reason) after Timeout -> - erlang:demonitor(Mref), - receive - {'DOWN', Mref, _, _, _} -> true - after 0 -> true - end, + erlang:demonitor(Mref, [flush]), exit(timeout) end catch |