diff options
Diffstat (limited to 'lib/compiler')
| -rw-r--r-- | lib/compiler/src/beam_bool.erl | 50 | ||||
| -rw-r--r-- | lib/compiler/src/v3_codegen.erl | 58 | ||||
| -rw-r--r-- | lib/compiler/src/v3_core.erl | 2 | ||||
| -rw-r--r-- | lib/compiler/test/guard_SUITE.erl | 79 | ||||
| -rw-r--r-- | lib/compiler/test/map_SUITE.erl | 3 | 
5 files changed, 111 insertions, 81 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/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. | 
