aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/src/beam_a.erl4
-rw-r--r--lib/compiler/src/beam_receive.erl92
-rw-r--r--lib/compiler/src/beam_utils.erl98
-rw-r--r--lib/compiler/src/compile.erl23
-rw-r--r--lib/compiler/src/v3_core.erl51
-rw-r--r--lib/compiler/test/andor_SUITE.erl33
-rw-r--r--lib/compiler/test/bs_bincomp_SUITE.erl3
-rw-r--r--lib/compiler/test/compile_SUITE.erl10
-rw-r--r--lib/compiler/test/compile_SUITE_data/key_compatibility.beambin0 -> 844 bytes
-rw-r--r--lib/compiler/test/compile_SUITE_data/key_compatibility.erl8
-rw-r--r--lib/compiler/test/receive_SUITE.erl34
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S71
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl6
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
new file mode 100644
index 0000000000..28329d2423
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/key_compatibility.beam
Binary files differ
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