aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/src/beam_bool.erl50
-rw-r--r--lib/compiler/src/cerl.erl11
-rw-r--r--lib/compiler/src/v3_codegen.erl58
-rw-r--r--lib/compiler/src/v3_core.erl2
-rw-r--r--lib/compiler/test/guard_SUITE.erl79
-rw-r--r--lib/compiler/test/map_SUITE.erl3
6 files changed, 120 insertions, 83 deletions
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 14b6381230..d14be83496 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -142,11 +142,6 @@ bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
throw:not_boolean_expr ->
failed;
- %% The block contains a 'move' instruction that could
- %% not be handled.
- throw:move ->
- failed;
-
%% The optimization is not safe. (A register
%% used by the instructions following the
%% optimized code is either not assigned a
@@ -215,37 +210,14 @@ ensure_opt_safe(Bl, NewCode, OldIs, Fail, PrecedingCode, St) ->
false -> throw(all_registers_not_killed);
true -> ok
end,
- Same = assigned_same_value(Bl, NewCode),
MustBeUnused = ordsets:subtract(ordsets:union(NotSet, NewDst),
- ordsets:union(MustBeKilled, Same)),
+ MustBeKilled),
case none_used(MustBeUnused, OldIs, Fail, St) of
false -> throw(registers_used);
true -> ok
end,
ok.
-%% assigned_same_value(OldCode, NewCodeReversed) -> [DestinationRegs]
-%% Return an ordset with a list of all y registers that are always
-%% assigned the same value in the old and new code. Currently, we
-%% are very conservative in that we only consider identical move
-%% instructions in the same order.
-%%
-assigned_same_value(Old, New) ->
- case reverse(New) of
- [{block,Bl}|_] ->
- assigned_same_value(Old, Bl, []);
- _ ->
- ordsets:new()
- end.
-
-assigned_same_value([{set,[{y,_}=D],[S],move}|T1],
- [{set,[{y,_}=D],[S],move}|T2], Acc) ->
- assigned_same_value(T1, T2, [D|Acc]);
-assigned_same_value(_, _, Acc) ->
- ordsets:from_list(Acc).
-
-update_fail_label([{set,_,_,move}=I|Is], Fail, Acc) ->
- update_fail_label(Is, Fail, [I|Acc]);
update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
update_fail_label([{set,Ds,As,{alloc,Regs,{gc_bif,N,{f,_}}}}|Is], Fail, Acc) ->
@@ -314,8 +286,6 @@ split_block_1(Is, Fail, ProhibitFailLabel) ->
end
end.
-split_block_2([{set,_,_,move}=I|Is], Fail, Acc) ->
- split_block_2(Is, Fail, [I|Acc]);
split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
split_block_2(Is, Fail, [I|Acc]);
split_block_2([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}=I|Is], Fail, Acc) ->
@@ -343,8 +313,6 @@ dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
dst_regs(Is, [D|Acc]);
dst_regs([{set,[D],_,{alloc,_,{gc_bif,_,{f,_}}}}|Is], Acc) ->
dst_regs(Is, [D|Acc]);
-dst_regs([{set,[D],_,move}|Is], Acc) ->
- dst_regs(Is, [D|Acc]);
dst_regs([_|Is], Acc) ->
dst_regs(Is, Acc);
dst_regs([], Acc) -> ordsets:from_list(Acc).
@@ -411,13 +379,6 @@ bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) ->
_Res ->
throw(not_boolean_expr)
end;
-bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) ->
- case {Src,Dst} of
- {{tmp,_},_} -> throw(move);
- {_,{tmp,_}} -> throw(move);
- _ -> ok
- end,
- bopt_tree(Is, Forest, [Move|Pre]);
bopt_tree([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
Ar = length(As),
case safe_bool_op(N, Ar) of
@@ -589,10 +550,6 @@ free_variables(Is) ->
E = gb_sets:empty(),
free_vars_1(Is, E, E, E).
-free_vars_1([{set,Ds,As,move}|Is], F0, N0, A) ->
- F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
- N = gb_sets:union(N0, var_list(Ds)),
- free_vars_1(Is, F, N, A);
free_vars_1([{set,Ds,As,{bif,_,_}}|Is], F0, N0, A) ->
F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
N = gb_sets:union(N0, var_list(Ds)),
@@ -632,8 +589,6 @@ free_vars_regs(X) -> [{x,X-1}|free_vars_regs(X-1)].
rename_regs(Is, Regs) ->
rename_regs(Is, Regs, []).
-rename_regs([{set,_,_,move}=I|Is], Regs, Acc) ->
- rename_regs(Is, Regs, [I|Acc]);
rename_regs([{set,[Dst0],Ss0,{alloc,_,Info}}|Is], Regs0, Acc) ->
Live = live_regs(Regs0),
Ss = rename_sources(Ss0, Regs0),
@@ -737,8 +692,7 @@ ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
Sub1 = gb_trees:update(R, NewReg, Sub0),
Sub = gb_trees:insert(NewReg, NewReg, Sub1),
Ssa#ssa{sub=Sub}
- end;
-ssa_assign(_, Ssa) -> Ssa.
+ end.
ssa_sub_list(List, Sub) ->
[ssa_sub(E, Sub) || E <- List].
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 010327b5e3..e7a2b8177a 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -1598,13 +1598,20 @@ is_c_map(#c_literal{val = V}) when is_map(V) ->
is_c_map(_) ->
false.
--spec map_es(c_map()) -> [c_map_pair()].
+-spec map_es(c_map() | c_literal()) -> [c_map_pair()].
+map_es(#c_literal{anno=As,val=M}) when is_map(M) ->
+ [ann_c_map_pair(As,
+ #c_literal{anno=As,val='assoc'},
+ #c_literal{anno=As,val=K},
+ #c_literal{anno=As,val=V}) || {K,V} <- maps:to_list(M)];
map_es(#c_map{es = Es}) ->
Es.
--spec map_arg(c_map()) -> c_map() | c_literal().
+-spec map_arg(c_map() | c_literal()) -> c_map() | c_literal().
+map_arg(#c_literal{anno=As,val=M}) when is_map(M) ->
+ #c_literal{anno=As,val=#{}};
map_arg(#c_map{arg=M}) ->
M.
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 34c67b16ca..2a89305f4d 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1327,12 +1327,13 @@ bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
%% that we save any variable that will be live after this BIF call.
MayFail = not erl_bifs:is_safe(erlang, Bif, length(As)),
- {Sis,Int0} = case St0#cg.in_catch andalso
- St0#cg.bfail =:= 0 andalso
- MayFail of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} =
+ case MayFail of
+ true ->
+ maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0);
+ false ->
+ {[],Bef}
+ end,
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Reg = put_reg(V, Int1#sr.reg),
Int = Int1#sr{reg=Reg},
@@ -1363,11 +1364,7 @@ gc_bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
%% Currently, we are somewhat pessimistic in
%% that we save any variable that will be live after this BIF call.
- {Sis,Int0} =
- case St0#cg.in_catch andalso St0#cg.bfail =:= 0 of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0),
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Reg = put_reg(V, Int1#sr.reg),
@@ -1512,8 +1509,7 @@ set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
Ret = fetch_reg(R, Int1#sr.reg),
{[{put_list,S1,S2,Ret}], Int1, St};
-set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
- #cg{in_catch=InCatch, bfail=Bfail}=St) ->
+set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{bfail=Bfail}=St) ->
%% At run-time, binaries are constructed in three stages:
%% 1) First the size of the binary is calculated.
%% 2) Then the binary is allocated.
@@ -1532,11 +1528,7 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% First generate the code that constructs each field.
Fail = {f,Bfail},
PutCode = cg_bin_put(Segs, Fail, Bef),
- {Sis,Int1} =
- case InCatch of
- true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Int0}
- end,
+ {Sis,Int1} = maybe_adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb, St),
MaxRegs = max_reg(Bef#sr.reg),
Aft = clear_dead(Int1, Le#l.i, Vdb),
@@ -1545,14 +1537,11 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
{Sis++Code,Aft,St};
% Map single variable key
set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef,
- #cg{in_catch=InCatch,bfail=Bfail}=St) ->
+ #cg{bfail=Bfail}=St) ->
Fail = {f,Bfail},
- {Sis,Int0} =
- case InCatch of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St),
+
SrcReg = cg_reg_arg(Map,Int0),
Line = line(Le#l.a),
@@ -1573,17 +1562,13 @@ set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef,
% Map (possibly) multiple literal keys
set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,
- #cg{in_catch=InCatch,bfail=Bfail}=St) ->
+ #cg{bfail=Bfail}=St) ->
%% assert key literals
[] = [Var||{map_pair,{var,_}=Var,_} <- Es],
Fail = {f,Bfail},
- {Sis,Int0} =
- case InCatch of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St),
SrcReg = cg_reg_arg(Map,Int0),
Line = line(Le#l.a),
@@ -2038,6 +2023,19 @@ trim_free([R|Rs0]) ->
end;
trim_free([]) -> [].
+%% maybe_adjust_stack(Bef, FirstBefore, LastFrom, Vdb, St) -> {[Ainstr],Aft}.
+%% Adjust the stack, but only if the code is inside a catch and not
+%% inside a guard. Use this funtion before instructions that may
+%% cause an exception.
+
+maybe_adjust_stack(Bef, Fb, Lf, Vdb, St) ->
+ case St of
+ #cg{in_catch=true,bfail=0} ->
+ adjust_stack(Bef, Fb, Lf, Vdb);
+ #cg{} ->
+ {[],Bef}
+ end.
+
%% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}.
%% Do complete stack adjustment by compressing stack and adding
%% variables to be saved. Try to optimise ordering on stack by
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 0941ad5dd5..7d93e2ae16 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -804,7 +804,7 @@ map_op(map_field_assoc) -> #c_literal{val=assoc};
map_op(map_field_exact) -> #c_literal{val=exact}.
is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true;
-is_valid_map_src(#c_var{}) -> true;
+is_valid_map_src(#c_var{}=Var) -> not cerl:is_c_fname(Var);
is_valid_map_src(_) -> false.
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index b3b67155b3..47eb1ba78b 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -34,7 +34,8 @@
tricky/1,rel_ops/1,rel_op_combinations/1,literal_type_tests/1,
basic_andalso_orelse/1,traverse_dcd/1,
check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1,
- bad_constants/1,bad_guards/1]).
+ bad_constants/1,bad_guards/1,scotland/1,
+ guard_in_catch/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -52,7 +53,7 @@ groups() ->
rel_ops,rel_op_combinations,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
- bad_constants,bad_guards]}].
+ bad_constants,bad_guards,scotland,guard_in_catch]}].
init_per_suite(Config) ->
Config.
@@ -1831,6 +1832,80 @@ bad_guards_2(M, [_]) when M#{a := 0, b => 0}, map_size(M) ->
bad_guards_3(M, [_]) when is_map(M) andalso M#{a := 0, b => 0}, length(M) ->
ok.
+%% beam_bool would remove the initialization of {y,0}.
+%% (Thanks to Thomas Arts and QuickCheck.)
+
+scotland(_Config) ->
+ million = do_scotland(placed),
+ {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(false)),
+ {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(true)),
+ {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(echo)),
+ ok.
+
+do_scotland(Echo) ->
+ found(case Echo of
+ Echo when true; Echo, Echo, Echo ->
+ Echo;
+ echo ->
+ []
+ end,
+ Echo = placed).
+
+found(_, _) -> million.
+
+%% Building maps in a guard in a 'catch' would crash v3_codegen.
+
+guard_in_catch(_Config) ->
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{}),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{a=>b}),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(atom),
+
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{}),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{a=>b}),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(atom),
+
+ {'EXIT',{if_clause,_}} = (catch do_guard_in_catch_map_3()),
+
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(42),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(<<1,2,3>>),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(atom),
+ {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(#{}),
+
+ ok.
+
+do_guard_in_catch_map_1(From) ->
+ catch
+ if
+ From#{[] => sufficient} ->
+ saint
+ end.
+
+do_guard_in_catch_map_2(From) ->
+ catch
+ if
+ From#{From => sufficient} ->
+ saint
+ end.
+
+do_guard_in_catch_map_3() ->
+ try
+ if [] -> solo end
+ catch
+ Friendly when Friendly#{0 => []} -> minutes
+ after
+ membership
+ end.
+
+do_guard_in_catch_bin(From) ->
+ %% Would not crash v3_codegen, but there would be an unnecessary
+ %% 'move' to a Y register.
+ catch
+ if
+ <<From:32>> ->
+ saint
+ end.
+
+
%% Call this function to turn off constant propagation.
id(I) -> I.
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index 411b15eebe..cff3b5deb4 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -883,6 +883,9 @@ t_update_map_expressions(Config) when is_list(Config) ->
%% Error cases.
{'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
{'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ {'EXIT',{{badmap,_},_}} =
+ (catch (fun t_update_map_expressions/1)#{u => 42}),
+
ok.