diff options
Diffstat (limited to 'lib/compiler/test')
32 files changed, 1701 insertions, 474 deletions
| diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index da5d207db9..f042a5cb51 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -14,6 +14,7 @@ MODULES= \  	beam_except_SUITE \  	beam_jump_SUITE \  	beam_reorder_SUITE \ +	beam_ssa_SUITE \  	beam_type_SUITE \  	beam_utils_SUITE \  	bif_SUITE \ @@ -52,6 +53,7 @@ NO_OPT= \  	beam_except \  	beam_jump \  	beam_reorder \ +	beam_ssa \  	beam_type \  	beam_utils \  	bif \ @@ -75,6 +77,7 @@ INLINE= \  	andor \  	apply \  	beam_block \ +	beam_ssa \  	beam_utils \  	bif \  	bs_bincomp \ @@ -94,16 +97,26 @@ INLINE= \  	receive \  	record +R21= \ +        bs_construct \ +        bs_match +  CORE_MODULES = \  	lfe_andor_SUITE \  	lfe_guard_SUITE +NO_MOD_OPT = $(NO_OPT) +  NO_OPT_MODULES= $(NO_OPT:%=%_no_opt_SUITE)  NO_OPT_ERL_FILES= $(NO_OPT_MODULES:%=%.erl)  POST_OPT_MODULES= $(NO_OPT:%=%_post_opt_SUITE)  POST_OPT_ERL_FILES= $(POST_OPT_MODULES:%=%.erl)  INLINE_MODULES= $(INLINE:%=%_inline_SUITE)  INLINE_ERL_FILES= $(INLINE_MODULES:%=%.erl) +R21_MODULES= $(R21:%=%_r21_SUITE) +R21_ERL_FILES= $(R21_MODULES:%=%.erl) +NO_MOD_OPT_MODULES= $(NO_MOD_OPT:%=%_no_module_opt_SUITE) +NO_MOD_OPT_ERL_FILES= $(NO_MOD_OPT_MODULES:%=%.erl)  ERL_FILES= $(MODULES:%=%.erl)  CORE_FILES= $(CORE_MODULES:%=%.core) @@ -124,7 +137,7 @@ RELSYSDIR = $(RELEASE_PATH)/compiler_test  # ----------------------------------------------------  ERL_MAKE_FLAGS += -ERL_COMPILE_FLAGS += +clint +clint0 +ERL_COMPILE_FLAGS += +clint +clint0 +ssalint  EBIN = . @@ -132,15 +145,21 @@ EBIN = .  # Targets  # ---------------------------------------------------- -make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(INLINE_ERL_FILES) +make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \ +  $(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES)  	$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \  	> $(EMAKEFILE) -	$(ERL_TOP)/make/make_emakefile +no_copt +no_postopt $(ERL_COMPILE_FLAGS) \ +	$(ERL_TOP)/make/make_emakefile +no_copt +no_postopt \ +	+no_ssa_opt +no_recv_opt $(ERL_COMPILE_FLAGS) \  	-o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE)  	$(ERL_TOP)/make/make_emakefile +no_copt $(ERL_COMPILE_FLAGS) \  	-o$(EBIN) $(POST_OPT_MODULES) >> $(EMAKEFILE)  	$(ERL_TOP)/make/make_emakefile +inline $(ERL_COMPILE_FLAGS) \  	-o$(EBIN) $(INLINE_MODULES) >> $(EMAKEFILE) +	$(ERL_TOP)/make/make_emakefile +r21 $(ERL_COMPILE_FLAGS) \ +	-o$(EBIN) $(R21_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) \  	-o$(EBIN) $(CORE_MODULES) >> $(EMAKEFILE) @@ -167,6 +186,12 @@ docs:  %_inline_SUITE.erl: %_SUITE.erl  	sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@ +%_r21_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 $@));' $< > $@ +  # ----------------------------------------------------  # Release Target  # ----------------------------------------------------  @@ -179,7 +204,8 @@ 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) "$(RELSYSDIR)" +		$(INLINE_ERL_FILES) $(R21_ERL_FILES) \ +		$(NO_MOD_OPT_ERL_FILES) "$(RELSYSDIR)"  	$(INSTALL_DATA) $(CORE_FILES) "$(RELSYSDIR)"  	for file in $(ERL_DUMMY_FILES); do \  	    module=`basename $$file .erl`; \ diff --git a/lib/compiler/test/apply_SUITE.erl b/lib/compiler/test/apply_SUITE.erl index 0f82a56fb7..2ee518b1a0 100644 --- a/lib/compiler/test/apply_SUITE.erl +++ b/lib/compiler/test/apply_SUITE.erl @@ -73,6 +73,7 @@ mfa(Config) when is_list(Config) ->      {'EXIT',_} = (catch ?APPLY2(Mod, (id(bazzzzzz)), a, b)),      {'EXIT',_} = (catch ?APPLY2({}, baz, a, b)),      {'EXIT',_} = (catch ?APPLY2(?MODULE, [], a, b)), +    {'EXIT',_} = (catch bad_literal_call(1)),      ok = apply(Mod, foo, id([])),      {[a,b|c]} = apply(Mod, bar, id([[a,b|c]])), @@ -92,6 +93,13 @@ mfa(Config) when is_list(Config) ->      apply(Mod, foo, []). +%% The single call to this function with a literal argument caused type +%% optimization to swap out the 'mod' field of a #b_remote{}, which was +%% mishandled during code generation as it assumed that the module would always +%% be an atom. +bad_literal_call(I) -> +    I:foo(). +  foo() ->      ok. diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index 2b4a780899..9380fe06c8 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -21,7 +21,7 @@  -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,  	 init_per_group/2,end_per_group/2, -	 multiple_allocs/1,coverage/1]). +	 multiple_allocs/1,bs_get_tail/1,coverage/1]).  suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -31,6 +31,7 @@ all() ->  groups() ->      [{p,[parallel],        [multiple_allocs, +       bs_get_tail,         coverage]}].  init_per_suite(Config) -> @@ -63,6 +64,17 @@ place(lee) ->  conditions() ->      (talking = going) = storage + [large = wanted]. +bs_get_tail(Config) -> +    {<<"abc">>,0,0,Config} = bs_get_tail_1(id(<<0:32, "abc">>), 0, 0, Config), +    {'EXIT', +     {function_clause, +      [{?MODULE,bs_get_tail_1,[<<>>,0,0,Config],_}|_]}} = +        (catch bs_get_tail_1(id(<<>>), 0, 0, Config)), +    ok. + +bs_get_tail_1(<<_:32, Rest/binary>>, Z1, Z2, F1) -> +    {Rest,Z1,Z2,F1}. +  coverage(_) ->      File = {file,"fake.erl"},      ok = fc(a), @@ -83,8 +95,24 @@ coverage(_) ->  	(catch bar(x)),      {'EXIT',{{case_clause,{1}},[{?MODULE,bar,1,[File,{line,9}]}|_]}} =  	(catch bar(0)), + +    Self = self(), +    {'EXIT',{{strange,Self},[{?MODULE,foo,[any],[File,{line,14}]}|_]}} = +        (catch foo(any)), + +    {ok,succeed,1,2} = foobar(succeed, 1, 2), +    {'EXIT',{function_clause,[{?MODULE,foobar,[[fail],1,2], +                               [{file,"fake.erl"},{line,16}]}|_]}} = +        (catch foobar([fail], 1, 2)), +    {'EXIT',{function_clause,[{?MODULE,fake_function_clause,[{a,b},42.0],_}|_]}} = +        (catch fake_function_clause({a,b})), +      ok. +fake_function_clause(A) -> error(function_clause, [A,42.0]). + +id(I) -> I. +  -file("fake.erl", 1).  fc(a) ->	                                %Line 2      ok;						%Line 3 @@ -96,3 +124,9 @@ bar(X) ->					%Line 8      case {X+1} of				%Line 9  	1 -> ok					%Line 10      end.					%Line 11 +%% Cover collection code for function_clause exceptions. +foo(A) ->                                       %Line 13 +    error({strange,self()}, [A]).               %Line 14 +%% Cover beam_except:tag_literal/1. +foobar(A, B, C) when is_atom(A) ->              %Line 16 +    {ok,A,B,C}.                                 %Line 17 diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl index faedc0c1f1..759d884dc4 100644 --- a/lib/compiler/test/beam_jump_SUITE.erl +++ b/lib/compiler/test/beam_jump_SUITE.erl @@ -22,7 +22,8 @@  -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,  	 init_per_group/2,end_per_group/2,  	 undefined_label/1,ambiguous_catch_try_state/1, -         build_tuple/1]). +         unsafe_move_elimination/1,build_tuple/1, +         coverage/1]).  suite() ->      [{ct_hooks,[ts_install_cth]}]. @@ -34,7 +35,9 @@ groups() ->      [{p,[parallel],        [undefined_label,         ambiguous_catch_try_state, -       build_tuple +       unsafe_move_elimination, +       build_tuple, +       coverage        ]}].  init_per_suite(Config) -> @@ -75,6 +78,43 @@ checks(Wanted) ->      %% Must be one line to cause the unsafe optimization.      {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}. +unsafe_move_elimination(_Config) -> +    {{left,right,false},false} = unsafe_move_elimination(left, right, false), +    {{false,right,false},false} = unsafe_move_elimination(false, right, true), +    {{true,right,right},right} = unsafe_move_elimination(true, right, true), +    ok. + +unsafe_move_elimination(Left, Right, Simple0) -> +    id(1), + +    %% The move at label 29 would be removed by beam_jump, which is unsafe because +    %% the two select_val instructions have different source registers. +    %% +    %%   {select_val,{y,0},{f,25},{list,[{atom,true},{f,27},{atom,false},{f,29}]}}. +    %%               ^^^^^                                  ^^^^^^^^^^^^^^^^^^^ +    %% {label,27}. +    %%   {kill,{y,0}}. +    %%   {move,{y,2},{x,0}}. +    %%   {line,...}. +    %%   {call,1,{f,31}}. +    %%   {select_val,{x,0},{f,33},{list,[{atom,true},{f,35},{atom,false},{f,29}]}}. +    %%               ^^^^^                                  ^^^^^^^^^^^^^^^^^^^ +    %% {label,29}. +    %%   {move,{atom,false},{y,0}}.  <=== REMOVED (unsafely). +    %%   {jump,{f,37}}. + +    Simple = case case Simple0 of +                      false -> false; +                      true -> id(Left) +                  end +             of +                 false -> +                     false; +                 true -> +                     id(Right) +             end, +    {id({Left,Right,Simple}),Simple}. +  -record(message2, {id, p1}).  -record(message3, {id, p1, p2}). @@ -87,3 +127,45 @@ do_build_tuple(Message) ->  	    Res = {res, rand:uniform(100)},  	    {Message#message3.id, Res}      end. + +coverage(_Config) -> +    ok = coverage_1(ok), +    {error,badarg} = coverage_1({error,badarg}), + +    gt = coverage_2(100, 42), +    le = coverage_2(100, 999), +    le = coverage_2([], []), +    gt = coverage_2([], xxx), + +    ok. + +coverage_1(Var) -> +    case id(Var) of +	ok -> ok; +	Error -> Error +    end. + +%% Cover beam_jump:invert_test(is_ne_exact). +coverage_2(Pre1, Pre2) -> +    case +        case Pre1 == [] of +            false -> +                false; +            true -> +                Pre2 /= [] +        end +    of +        true -> +            gt; +        false -> +            case Pre1 > Pre2 of +                true -> +                    gt; +                false -> +                    le +            end +    end. + + +id(I) -> +    I. diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl new file mode 100644 index 0000000000..15cf9bcbf3 --- /dev/null +++ b/lib/compiler/test/beam_ssa_SUITE.erl @@ -0,0 +1,497 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%%     http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(beam_ssa_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, +	 init_per_group/2,end_per_group/2, +         calls/1,tuple_matching/1,recv/1,maps/1, +         cover_ssa_dead/1,combine_sw/1,share_opt/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> +    [{group,p}]. + +groups() -> +    [{p,test_lib:parallel(), +      [tuple_matching, +       calls, +       recv, +       maps, +       cover_ssa_dead, +       combine_sw, +       share_opt +      ]}]. + +init_per_suite(Config) -> +    test_lib:recompile(?MODULE), +    Config. + +end_per_suite(_Config) -> +    ok. + +init_per_group(_GroupName, Config) -> +    Config. + +end_per_group(_GroupName, Config) -> +    Config. + +calls(Config) -> +    Ret = {return,value,Config}, +    Ret = fun_call(fun(42) -> ok end, Ret), +    Ret = apply_fun(fun(a, b) -> ok end, [a,b], Ret), +    Ret = apply_mfa(test_lib, id, [anything], Ret), +    {'EXIT',{badarg,_}} = (catch call_error()), +    {'EXIT',{badarg,_}} = (catch call_error(42)), +    5 = start_it([erlang,length,1,2,3,4,5]), +    ok. + +fun_call(Fun, X0) -> +    X = id(X0), +    Fun(42), +    X. + +apply_fun(Fun, Args, X0) -> +    X = id(X0), +    apply(Fun, Args), +    X. + +apply_mfa(Mod, Name, Args, X0) -> +    X = id(X0), +    apply(Mod, Name, Args), +    X. + +call_error() -> +    error(badarg), +    ok. + +call_error(I) -> +    <<I:(-8)>>, +    ok. + +start_it([_|_]=MFA) -> +    case MFA of +	[M,F|Args] -> M:F(Args) +    end. + +tuple_matching(_Config) -> +    do_tuple_matching({tag,42}), + +    true = is_two_tuple({a,b}), +    false = is_two_tuple({a,b,c}), +    false = is_two_tuple(atom), + +    ok. + +do_tuple_matching(Arg) -> +    Res = do_tuple_matching_1(Arg), +    Res = do_tuple_matching_2(Arg), +    Res = do_tuple_matching_3(Arg), +    Res. + +do_tuple_matching_1({tag,V}) -> +    {ok,V}. + +do_tuple_matching_2(Tuple) when is_tuple(Tuple) -> +    Size = tuple_size(Tuple), +    if +        Size =:= 2 -> +            {ok,element(2, Tuple)} +    end. + +do_tuple_matching_3(Tuple) when is_tuple(Tuple) -> +    Size = tuple_size(Tuple), +    if +        Size =:= 2 -> +            2 = id(Size), +            {ok,element(2, Tuple)} +    end. + +is_two_tuple(Arg) -> +    case is_tuple(Arg) of +        false -> false; +        true -> tuple_size(Arg) == 2 +    end. + +-record(reporter_state, {res,run_config}). +-record(run_config, {report_interval=0}). + +recv(_Config) -> +    Parent = self(), + +    %% Test sync_wait_mon/2. +    Succ = fun() -> Parent ! {ack,self(),{result,42}} end, +    {result,42} = sync_wait_mon(spawn_monitor(Succ), infinity), + +    Down = fun() -> exit(down) end, +    {error,down} = sync_wait_mon(spawn_monitor(Down), infinity), + +    Exit = fun() -> +                   Self = self(), +                   spawn(fun() -> exit(Self, kill_me) end), +                   receive _ -> ok end +           end, +    {error,kill_me} = sync_wait_mon(spawn_monitor(Exit), infinity), + +    Timeout = fun() -> receive _ -> ok end end, +    {error,timeout} = sync_wait_mon(spawn_monitor(Timeout), 0), + +    %% Test reporter_loop/1. +    {a,Parent} = reporter_loop(#reporter_state{res={a,Parent}, +                                               run_config=#run_config{}}), + +    %% Test bad_sink/0. +    bad_sink(), + +    %% Test tricky_recv_1/0. +    self() ! 1, +    a = tricky_recv_1(), +    self() ! 2, +    b = tricky_recv_1(), + +    %% Test tricky_recv_2/0. +    self() ! 1, +    {1,yes} = tricky_recv_2(), +    self() ! 2, +    {2,maybe} = tricky_recv_2(), + +    %% Test 'receive after infinity' in try/catch. +    Pid = spawn(fun recv_after_inf_in_try/0), +    exit(Pid, done), + +    %% Test tricky_recv_3(). +    self() ! {{self(),r0},{1,42,"name"}}, +    {Parent,r0,[<<1:32,1:8,42:8>>,"name",0]} = tricky_recv_3(), +    self() ! {{self(),r1},{2,99,<<"data">>}}, +    {Parent,r1,<<1:32,2:8,99:8,"data">>} = tricky_recv_3(), + +    %% Test tricky_recv_4(). +    self() ! {[self(),r0],{1,42,"name"}}, +    {Parent,r0,[<<1:32,1:8,42:8>>,"name",0]} = tricky_recv_4(), +    self() ! {[self(),r1],{2,99,<<"data">>}}, +    {Parent,r1,<<1:32,2:8,99:8,"data">>} = tricky_recv_4(), + +    ok. + +sync_wait_mon({Pid, Ref}, Timeout) -> +    receive +	{ack,Pid,Return} -> +	    erlang:demonitor(Ref, [flush]), +	    Return; +	{'DOWN',Ref,_Type,Pid,Reason} -> +	    {error,Reason}; +	{'EXIT',Pid,Reason} -> +	    erlang:demonitor(Ref, [flush]), +	    {error,Reason} +    after Timeout -> +            erlang:demonitor(Ref, [flush]), +            exit(Pid, kill), +            {error,timeout} +    end. + +reporter_loop(State) -> +    RC = State#reporter_state.run_config, +    receive after RC#run_config.report_interval -> +                    State#reporter_state.res +    end. + +bad_sink() -> +    {ok,Pid} = my_spawn(self()), +    %% The get_tuple_element instruction for the matching +    %% above was sinked into the receive loop. That will +    %% not work (and would be bad for performance if it +    %% would work). +    receive +        {ok,Pid} -> +            ok; +        error -> +            exit(failed) +    end, +    exit(Pid, kill). + +my_spawn(Parent) -> +    Pid = spawn(fun() -> +                        Parent ! {ok,self()}, +                        receive _ -> ok end +                end), +    {ok,Pid}. + +tricky_recv_1() -> +    receive +        X=1 -> +            id(42), +            a; +        X=2 -> +            b +    end, +    case X of +        1 -> a; +        2 -> b +    end. + +tricky_recv_2() -> +    receive +        X=1 -> +            Y = case id(X) of +                    1 -> yes; +                    _ -> no +                end, +            a; +        X=2 -> +            Y = maybe, +            b +    end, +    {X,Y}. + +recv_after_inf_in_try() -> +    try +        %% Used to crash beam_kernel_to_ssa. +        receive after infinity -> ok end +    catch +	_A:_B -> +	    receive after infinity -> ok end +    end. + +tricky_recv_3() -> +    {Pid, R, Request} = +	receive +	    {{Pid0,R0}, {1, Proto0, Name0}} -> +		{Pid0, R0, +		 [<<1:32, 1:8, Proto0:8>>,Name0,0]}; +	    {{Pid1,R1}, {2, Proto1, Data1}}  -> +		{Pid1, R1, +		 <<1:32, 2:8, Proto1:8, Data1/binary>>} +	end, +    id({Pid,R,Request}). + +tricky_recv_4() -> +    {Pid, R, Request} = +	receive +	    {[Pid0,R0], {1, Proto0, Name0}} -> +		{Pid0, R0, +		 [<<1:32, 1:8, Proto0:8>>,Name0,0]}; +	    {[Pid1,R1], {2, Proto1, Data1}}  -> +		{Pid1, R1, +		 <<1:32, 2:8, Proto1:8, Data1/binary>>} +	end, +    id({Pid,R,Request}). + +maps(_Config) -> +    {'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)), +    ok. + +maps_1(K) -> +    _ = id(42), +    #{K:=V} = #{}, +    V. + +-record(wx_ref, {type=any_type,ref=any_ref}). + +cover_ssa_dead(_Config) -> +    str = format_str(str, escapable, [], true), +    [iolist,str] = format_str(str, escapable, iolist, true), +    bad = format_str(str, not_escapable, [], true), +    bad = format_str(str, not_escapable, iolist, true), +    bad = format_str(str, escapable, [], false), +    bad = format_str(str, escapable, [], bad), + +    DefWxRef = #wx_ref{}, +    {DefWxRef,77,9999,[]} = contains(#wx_ref{}, 77, 9999), +    {DefWxRef,77.0,9999,[]} = contains(#wx_ref{}, 77.0, 9999), +    {DefWxRef,77,9999.0,[]} = contains(#wx_ref{}, 77, 9999.0), +    {DefWxRef,77.0,9999.0,[]} = contains(#wx_ref{}, 77.0, 9999.0), +    {any_type,any_ref,42,43,[option]} = contains(#wx_ref{}, {42,43}, [option]), +    {any_type,any_ref,42,43,[]} = contains(#wx_ref{}, {42,43}, []), +    {any_type,any_ref,42.0,43,[]} = contains(#wx_ref{}, {42.0,43}, []), +    {any_type,any_ref,42,43.0,[]} = contains(#wx_ref{}, {42,43.0}, []), +    {any_type,any_ref,42.0,43.0,[]} = contains(#wx_ref{}, {42.0,43.0}, []), + +    nope = conv_alub(false, '=:='), +    ok = conv_alub(true, '=:='), +    ok = conv_alub(true, none), +    error = conv_alub(false, none), + +    {false,false} = eval_alu(false, false, false), +    {true,false}  = eval_alu(false, false, true), +    {false,true}  = eval_alu(false, true, false), +    {false,false} = eval_alu(false, true, true), +    {false,true}  = eval_alu(true, false, false), +    {false,false} = eval_alu(true, false, true), +    {true,true}   = eval_alu(true, true, false), +    {false,true}  = eval_alu(true, true, true), + +    100.0 = percentage(1.0, 0.0), +    100.0 = percentage(1, 0), +    0.0 = percentage(0, 0), +    0.0 = percentage(0.0, 0.0), +    40.0 = percentage(4.0, 10.0), +    60.0 = percentage(6, 10), + +    %% Cover '=:=', followed by '=/='. +    false = 'cover__=:=__=/='(41), +    true = 'cover__=:=__=/='(42), +    false = 'cover__=:=__=/='(43), + +    %% Cover '<', followed by '=/='. +    true = 'cover__<__=/='(41), +    false = 'cover__<__=/='(42), +    false = 'cover__<__=/='(43), + +    %% Cover '=<', followed by '=/='. +    true = 'cover__=<__=/='(41), +    true = 'cover__=<__=/='(42), +    false = 'cover__=<__=/='(43), + +    %% Cover '>=', followed by '=/='. +    false = 'cover__>=__=/='(41), +    true = 'cover__>=__=/='(42), +    true = 'cover__>=__=/='(43), + +    %% Cover '>', followed by '=/='. +    false = 'cover__>__=/='(41), +    false = 'cover__>__=/='(42), +    true = 'cover__>__=/='(43), + +    ok. + +'cover__=:=__=/='(X) when X =:= 42 -> X =/= 43; +'cover__=:=__=/='(_) -> false. + +'cover__<__=/='(X) when X < 42 -> X =/= 42; +'cover__<__=/='(_) -> false. + +'cover__=<__=/='(X) when X =< 42 -> X =/= 43; +'cover__=<__=/='(_) -> false. + +'cover__>=__=/='(X) when X >= 42 -> X =/= 41; +'cover__>=__=/='(_) -> false. + +'cover__>__=/='(X) when X > 42 -> X =/= 42; +'cover__>__=/='(_) -> false. + +format_str(Str, FormatData, IoList, EscChars) -> +    Escapable = FormatData =:= escapable, +    case id(Str) of +        IoStr when Escapable, EscChars, IoList == [] -> +            id(IoStr); +        IoStr when Escapable, EscChars -> +            [IoList,id(IoStr)]; +        _ -> +            bad +    end. + +contains(This, X, Y) when is_record(This, wx_ref), is_number(X), is_number(Y) -> +    {This,X,Y,[]}; +contains(#wx_ref{type=ThisT,ref=ThisRef}, {CX,CY}, Options) +  when is_number(CX), is_number(CY), is_list(Options) -> +    {ThisT,ThisRef,CX,CY,Options}. + +conv_alub(HasDst, CmpOp) -> +    case (not HasDst) andalso CmpOp =/= none of +        true -> nope; +        false -> +            case HasDst of +                false -> error; +                true -> ok +            end +    end. + +eval_alu(Sign1, Sign2, N) -> +    V = (Sign1 andalso Sign2 andalso (not N)) +        or ((not Sign1) andalso (not Sign2) andalso N), +    C = (Sign1 andalso Sign2) +          or ((not N) andalso (Sign1 orelse Sign2)), +    {V,C}. + +percentage(Divident, Divisor) -> +    if Divisor == 0 andalso Divident /= 0 -> +            100.0; +       Divisor == 0 -> +            0.0; +       true -> +            Divident / Divisor * 100 +    end. + +combine_sw(_Config) -> +    [a] = do_comb_sw_1(a), +    [b,b] = do_comb_sw_1(b), +    [c] = do_comb_sw_1(c), +    [c] = do_comb_sw_1(c), +    [] = do_comb_sw_1(z), + +    [a] = do_comb_sw_2(a), +    [b2,b1] = do_comb_sw_2(b), +    [c] = do_comb_sw_2(c), +    [c] = do_comb_sw_2(c), +    [] = do_comb_sw_2(z), + +    ok. + +do_comb_sw_1(X) -> +    put(?MODULE, []), +    if +        X == a; X == b -> +            put(?MODULE, [X|get(?MODULE)]); +        true -> +            ok +    end, +    if +        X == b; X == c -> +            put(?MODULE, [X|get(?MODULE)]); +        true -> +            ok +    end, +    erase(?MODULE). + +do_comb_sw_2(X) -> +    put(?MODULE, []), +    case X of +        a -> +            put(?MODULE, [a|get(?MODULE)]); +        b -> +            put(?MODULE, [b1|get(?MODULE)]); +        _ -> +            ok +    end, +    case X of +        b -> +            put(?MODULE, [b2|get(?MODULE)]); +        c -> +            put(?MODULE, [c|get(?MODULE)]); +        _ -> +            ok +    end, +    erase(?MODULE). + +share_opt(_Config) -> +    ok = do_share_opt(0). + +do_share_opt(A) -> +    %% The compiler would be stuck in an infinite loop in beam_ssa_share. +    case A of +        0 -> a; +        1 -> b; +        2 -> c +    end, +    receive after 1 -> ok end. + + +%% The identity function. +id(I) -> I. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 9f691716e3..a7ffc3f60a 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -21,9 +21,10 @@  -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,  	 init_per_group/2,end_per_group/2, -	 integers/1,coverage/1,booleans/1,setelement/1,cons/1, -	 tuple/1,record_float/1,binary_float/1,float_compare/1, -	 arity_checks/1,elixir_binaries/1,find_best/1]). +	 integers/1,numbers/1,coverage/1,booleans/1,setelement/1, +	 cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1, +	 arity_checks/1,elixir_binaries/1,find_best/1, +         test_size/1]).  suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -33,6 +34,7 @@ all() ->  groups() ->      [{p,[parallel],        [integers, +       numbers,         coverage,         booleans,         setelement, @@ -43,7 +45,8 @@ groups() ->         float_compare,         arity_checks,         elixir_binaries, -       find_best +       find_best, +       test_size        ]}].  init_per_suite(Config) -> @@ -113,8 +116,8 @@ do_integers_4(_, _, Res) ->      Res.  do_integers_5(X0, Y0) -> -    %% X and Y will use the same register. -    X = X0 band 1, +    %% _X and Y will use the same register. +    _X = X0 band 1,      Y = Y0 band 3,      case Y of          0 -> zero; @@ -123,6 +126,59 @@ do_integers_5(X0, Y0) ->          3 -> three      end. +numbers(_Config) -> +    Int = id(42), +    true = is_integer(Int), +    true = is_number(Int), +    false = is_float(Int), + +    Float = id(42.0), +    true = is_float(Float), +    true = is_number(Float), +    false = is_integer(Float), + +    Number = id(1) + id(2), +    true = is_number(Number), +    true = is_integer(Number), +    false = is_float(Number), + +    AnotherNumber = id(99.0) + id(1), +    true = is_float(AnotherNumber), +    true = is_number(AnotherNumber), +    false = is_integer(AnotherNumber), + +    NotNumber = id(atom), +    true = is_atom(NotNumber), +    false = is_number(NotNumber), +    false = is_integer(NotNumber), +    false = is_float(NotNumber), + +    true = is_number(Int), +    true = is_number(Float), +    true = is_number(Number), +    true = is_number(AnotherNumber), + +    %% Cover beam_ssa_type:join/2. + +    Join1 = case id(a) of +                a -> 3 + id(7);                 %Number. +                b -> id(5) / id(2)              %Float. +            end, +    true = is_integer(Join1), + +    Join2 = case id(a) of +                a -> id(5) / 2;                 %Float. +                b -> 3 + id(7)                  %Number. +            end, +    true = is_float(Join2), + +    %% Cover beam_ssa_type:meet/2. + +    Meet1 = id(0) + -10.0,                       %Float. +    10.0 = abs(Meet1),                           %Number. + +    ok. +  coverage(Config) ->      {'EXIT',{badarith,_}} = (catch id(1) bsl 0.5),      {'EXIT',{badarith,_}} = (catch id(2.0) bsl 2), @@ -164,15 +220,52 @@ coverage(Config) ->      ok.  booleans(_Config) -> -    {'EXIT',{{case_clause,_},_}} = (catch do_booleans(42)), +    {'EXIT',{{case_clause,_},_}} = (catch do_booleans_1(42)), + +    ok = do_booleans_2(42, 41), +    error = do_booleans_2(42, 42), + +    AnyAtom = id(atom), +    true = is_atom(AnyAtom), +    false = is_boolean(AnyAtom), + +    MaybeBool = id(maybe), +    case MaybeBool of +        true -> ok; +        maybe -> ok; +        false -> ok +    end, +    false = is_boolean(MaybeBool), + +    NotBool = id(a), +    case NotBool of +        a -> ok; +        b -> ok; +        c -> ok +    end, +    false = is_boolean(NotBool), +      ok. -do_booleans(B) -> +do_booleans_1(B) ->      case is_integer(B) of  	yes -> yes;  	no -> no      end. +do_booleans_2(A, B) -> +    Not = not do_booleans_cmp(A, B), +    case Not of +        true -> +            case Not of +                true -> error; +                false -> ok +            end; +        false -> ok +    end. + +do_booleans_cmp(A, B) -> A > B. +  setelement(_Config) ->      T0 = id({a,42}),      {a,_} = T0, @@ -181,13 +274,59 @@ setelement(_Config) ->  cons(_Config) ->      [did] = cons(assigned, did), + +    true = cons_is_empty_list([]), +    false = cons_is_empty_list([a]), + +    false = cons_not(true), +    true = cons_not(false), + +    {$a,"bc"} = cons_hdtl(true), +    {$d,"ef"} = cons_hdtl(false),      ok.  cons(assigned, Instrument) ->      [Instrument] = [did]. +cons_is_empty_list(L) -> +    Cons = case L of +               [] -> "true"; +               _ -> "false" +           end, +    id(1), +    case Cons of +        "true" -> true; +        "false" -> false +    end. + +cons_not(B) -> +    Cons = case B of +               true -> "true"; +               false -> "false" +           end, +    id(1), +    case Cons of +        "true" -> false; +        "false" -> true +    end. + +cons_hdtl(B) -> +    Cons = case B of +               true -> "abc"; +               false -> "def" +           end, +    id(1), +    {id(hd(Cons)),id(tl(Cons))}. + +-record(bird, {a=a,b=id(42)}). +  tuple(_Config) ->      {'EXIT',{{badmatch,{necessary}},_}} = (catch do_tuple()), + +    [] = [X || X <- [], #bird{a = a} == {r,X,foo}], +    [] = [X || X <- [], #bird{b = b} == {bird,X}], +    [] = [X || X <- [], 3 == X#bird.a], +      ok.  do_tuple() -> @@ -324,6 +463,15 @@ find_best([], <<"a">>) ->  find_best([], nil) ->      {error,<<"should not get here">>}. +test_size(_Config) -> +    2 = do_test_size({a,b}), +    4 = do_test_size(<<42:32>>), +    ok. + +do_test_size(Term) when is_tuple(Term) -> +    size(Term); +do_test_size(Term) when is_binary(Term) -> +    size(Term).  id(I) ->      I. diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl index ff0f72d519..eb0af59f9d 100644 --- a/lib/compiler/test/beam_utils_SUITE.erl +++ b/lib/compiler/test/beam_utils_SUITE.erl @@ -197,7 +197,7 @@ do_bs_init_4(Arg1, Arg2) ->                           id(Rewrite)                   end/binary,                   "/shared">>); -        Other -> +        _Other ->              error      end. @@ -553,7 +553,7 @@ not_used_p(_C, S, K, L) when is_record(K, k) ->              id(K)      end. -is_used_fr(Config) -> +is_used_fr(_Config) ->      1 = is_used_fr(self(), self()),      1 = is_used_fr(self(), other),      receive 1 -> ok end, @@ -572,7 +572,7 @@ is_used_fr(X, Y) ->      X ! 1.  %% ERL-778. -unsafe_is_function(Config) -> +unsafe_is_function(_Config) ->      {undefined,any} = unsafe_is_function(undefined, any),      {ok,any} = unsafe_is_function(fun() -> ok end, any),      {'EXIT',{{case_clause,_},_}} = (catch unsafe_is_function(fun(_) -> ok end, any)), diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index d3e544a9cc..585d0e7191 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -34,7 +34,7 @@  	 undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,  	 map_field_lists/1,cover_bin_opt/1,  	 val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1, -         receive_stacked/1]). +         receive_stacked/1,aliased_types/1]).  -include_lib("common_test/include/ct.hrl"). @@ -63,7 +63,7 @@ groups() ->         undef_label,illegal_instruction,failing_gc_guard_bif,         map_field_lists,cover_bin_opt,val_dsetel,         bad_tuples,bad_try_catch_nesting, -       receive_stacked]}]. +       receive_stacked,aliased_types]}].  init_per_suite(Config) ->      test_lib:recompile(?MODULE), @@ -579,6 +579,58 @@ receive_stacked(Config) ->      ok. +aliased_types(Config) -> +    Seq = lists:seq(1, 5), +    1 = aliased_types_1(Seq, Config), + +    {1,1} = aliased_types_2(Seq), +    {42,none} = aliased_types_2([]), + +    gurka = aliased_types_3([gurka]), +    gaffel = aliased_types_3([gaffel]), + +    ok. + +%% ERL-735: validator failed to track types on aliased registers, rejecting +%% legitimate optimizations. +%% +%%    move x0 y0 +%%    bif hd L1 x0 +%%    get_hd y0     %% The validator failed to see that y0 was a list +%% +aliased_types_1(Bug, Config) -> +    if +        Config =/= [gurka, gaffel] -> %% Pointless branch. +            _ = hd(Bug), +            lists:seq(1, 5), +            hd(Bug) +    end. + +%% ERL-832: validator failed to realize that a Y register was a cons. +aliased_types_2(Bug) -> +    Res = case Bug of +              [] -> id(42); +              _ -> hd(Bug) +          end, +    {Res,case Bug of +             [] -> none; +             _ -> hd(Bug) +         end}. + +%% ERL-832 part deux; validator failed to realize that an aliased register was +%% a cons. +aliased_types_3(Bug) -> +    List = [Y || Y <- Bug], +    case List of +        [] -> Bug; +        _ -> +            if +                hd(List) -> a:a(); +                true -> ok +            end, +            hd(List) +    end. +  %%%-------------------------------------------------------------------------  transform_remove(Remove, Module) -> @@ -637,3 +689,6 @@ night(Turned) ->      ok.  participating(_, _, _, _) -> ok. + +id(I) -> +    I. diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S b/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S index a60ca1e89a..c7610971f1 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S +++ b/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S @@ -11,5 +11,5 @@    {label,1}.      {func_info,{atom,t},{atom,t},1}.    {label,2}. -    {test,bs_start_match2,{f,1},1,[{x,0},0],{x,0}}. +    {test,bs_start_match3,{f,1},1,[{x,0}],{x,0}}.      return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/receive_stacked.S b/lib/compiler/test/beam_validator_SUITE_data/receive_stacked.S index cca052a9c4..5b974119c6 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/receive_stacked.S +++ b/lib/compiler/test/beam_validator_SUITE_data/receive_stacked.S @@ -172,7 +172,7 @@      {allocate_zero,1,0}.    {label,28}.      {loop_rec,{f,30},{x,0}}. -    {test,bs_start_match2,{f,29},1,[{x,0},0],{x,0}}. +    {test,bs_start_match3,{f,29},1,[{x,0}],{x,0}}.      {test,bs_get_integer2,            {f,29},            1, @@ -219,7 +219,7 @@      {allocate_zero,1,0}.    {label,33}.      {loop_rec,{f,35},{x,0}}. -    {test,bs_start_match2,{f,34},1,[{x,0},0],{x,0}}. +    {test,bs_start_match3,{f,34},1,[{x,0}],{x,0}}.      {test,bs_get_integer2,            {f,34},            1, @@ -262,7 +262,7 @@      {allocate_zero,1,0}.    {label,38}.      {loop_rec,{f,40},{x,0}}. -    {test,bs_start_match2,{f,39},1,[{x,0},0],{x,1}}. +    {test,bs_start_match3,{f,39},1,[{x,0}],{x,1}}.      {test,bs_get_integer2,            {f,39},            2, diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index ccc49df005..69017d87e7 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -153,6 +153,8 @@ l(I_13, I_big1, I_16, Bin) ->  	[0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,  	 16#77,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,  	 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF]), +     ?T(<< (<<"abc",7:3>>):3/binary >>, +        [$a,$b,$c]),       %% Mix different units.       ?T(<<37558955:(I_16-12)/unit:8,1:1>>, @@ -311,6 +313,9 @@ fail(Config) when is_list(Config) ->      {'EXIT',{badarg,_}} = (catch <<0:(-(1 bsl 100))>>),      {'EXIT',{badarg,_}} = (catch <<Bin/binary,0:(-(1 bsl 100))>>), +    %% Unaligned sizes with literal binaries. +    {'EXIT',{badarg,_}} = (catch <<0,(<<7777:17>>)/binary>>), +      ok.  float_bin(Config) when is_list(Config) -> diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index a751f6fda5..2cfcb841a7 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -40,8 +40,10 @@  	 map_and_binary/1,unsafe_branch_caching/1,  	 bad_literals/1,good_literals/1,constant_propagation/1,  	 parse_xml/1,get_payload/1,escape/1,num_slots_different/1, -         beam_bsm/1,guard/1,is_ascii/1,non_opt_eq/1,erl_689/1, -         bs_start_match2_defs/1]). +         beam_bsm/1,guard/1,is_ascii/1,non_opt_eq/1, +         expression_before_match/1,erl_689/1,restore_on_call/1, +         restore_after_catch/1,matches_on_parameter/1,big_positions/1, +         matching_meets_apply/1,bs_start_match2_defs/1]).  -export([coverage_id/1,coverage_external_ignore/2]). @@ -57,7 +59,7 @@ all() ->      [{group,p}].  groups() ->  -    [{p,[parallel], +    [{p,[],        [size_shadow,int_float,otp_5269,null_fields,wiger,         bin_tail,save_restore,         partitioned_bs_match,function_clause,unit, @@ -73,8 +75,10 @@ groups() ->         map_and_binary,unsafe_branch_caching,         bad_literals,good_literals,constant_propagation,parse_xml,         get_payload,escape,num_slots_different, -       beam_bsm,guard,is_ascii,non_opt_eq,erl_689, -       bs_start_match2_defs]}]. +       beam_bsm,guard,is_ascii,non_opt_eq, +       expression_before_match,erl_689,restore_on_call, +       matches_on_parameter,big_positions, +       matching_meets_apply,bs_start_match2_defs]}].  init_per_suite(Config) -> @@ -250,6 +254,12 @@ bin_tail(Config) when is_list(Config) ->      ok = bin_tail_e(<<2:2,1:1,1:5,42:64>>),      error = bin_tail_e(<<3:2,1:1,1:5,42:64>>),      error = bin_tail_e(<<>>), + +    MD5 = erlang:md5(<<42>>), +    <<"abc">> = bin_tail_f(<<MD5/binary,"abc">>, MD5, 3), +    error = bin_tail_f(<<MD5/binary,"abc">>, MD5, 999), +    {'EXIT',{_,_}} = (catch bin_tail_f(<<0:16/unit:8>>, MD5, 0)), +      ok.  bin_tail_c(Bin, Offset) -> @@ -306,6 +316,14 @@ bin_tail_e_var(Bin) ->  	<<2:2,_:1,1:5,Tail/binary>> -> Tail;  	_ -> error      end. + +bin_tail_f(Bin, MD5, Size) -> +    case Bin of +        <<MD5:16/binary, Tail:Size/binary>> -> +            Tail; +        <<MD5:16/binary, _/binary>> -> +            error +    end.  save_restore(Config) when is_list(Config) ->      0 = save_restore_1(<<0:2,42:6>>), @@ -457,6 +475,15 @@ unit(Config) when is_list(Config) ->      127 = peek7(<<127:7>>),      100 = peek7(<<100:7,19:7>>),      fc(peek7, [<<1,2>>], catch peek7(<<1,2>>)), + +    1 = unit_opt(1, -1), +    8 = unit_opt(8, -1), + +    <<1:32,"abc">> = unit_opt_2(<<1:32,"abc">>), +    <<"def">> = unit_opt_2(<<2:32,"def">>), +    {'EXIT',_} = (catch unit_opt_2(<<1:32,33:7>>)), +    {'EXIT',_} = (catch unit_opt_2(<<2:32,55:7>>)), +      ok.  peek1(<<B:8,_/bitstring>>) -> B. @@ -467,6 +494,27 @@ peek8(<<B:8,_/binary>>) -> B.  peek16(<<B:16,_/binary-unit:16>>) -> B. +unit_opt(U, X) -> +    %% Cover type analysis in beam_ssa_type. +    Bin = case U of +              1 -> <<X:7>>; +              8 -> <<X>> +          end, +    %% The type of Bin will be set to {binary,gcd(1, 8)}. +    case Bin of +        <<_/binary-unit:8>> -> 8; +        <<_/binary-unit:1>> -> 1 +    end. + +unit_opt_2(<<St:32,KO/binary>> = Bin0) -> +    Bin = if +              St =:= 1 -> +                  Bin0; +              St =:= 2 -> +                  <<KO/binary>> +          end, +    id(Bin). +  shared_sub_bins(Config) when is_list(Config) ->      {15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0),      ok. @@ -694,6 +742,20 @@ coverage(Config) when is_list(Config) ->      binary = coverage_bitstring(<<7>>),      bitstring = coverage_bitstring(<<7:4>>),      other = coverage_bitstring([a]), + +    %% Cover code in beam_trim. + +    {done,<<17,53>>,[253,155,200]} = +        coverage_trim(<<253,155,200,17,53>>, e0, e1, e2, e3, []), + +    <<"(right|linux)">> = coverage_trim_1(<<"">>, <<"right">>, <<"linux">>), +    <<"/(right|linux)">> = coverage_trim_1(<<"/">>, <<"right">>, <<"linux">>), +    <<"(left|linux)/(right|linux)">> = +        coverage_trim_1(<<"left">>, <<"right">>, <<"linux">>), + +    {10,<<"-">>,""} = coverage_trim_2(<<"-">>, 10, []), +    {8,<<"-">>,"aa"} = coverage_trim_2(<<"aa-">>, 10, []), +      ok.  coverage_fold(Fun, Acc, <<H,T/binary>>) -> @@ -788,6 +850,37 @@ coverage_bitstring(Bin) when is_binary(Bin) -> binary;  coverage_bitstring(<<_/bitstring>>) -> bitstring;  coverage_bitstring(_) -> other. +coverage_trim(<<C:8,T/binary>> = Bin, E0, E1, E2, E3, Acc) -> +    case id(C > 128) of +        true -> +            coverage_trim(T, E0, E1, E2, E3, [C|Acc]); +        false -> +            {done,Bin,lists:reverse(Acc)} +    end. + +coverage_trim_1(<<>>, Right, OsType) -> +    do_coverage_trim_1(Right, OsType); +coverage_trim_1(<<"/">>, Right, OsType) -> +    <<"/",(do_coverage_trim_1(Right, OsType))/binary>>; +coverage_trim_1(Left, Right, OsType) -> +    <<(do_coverage_trim_1(Left, OsType))/binary, +      "/", +      (do_coverage_trim_1(Right, OsType))/binary>>. + +do_coverage_trim_1(A, OsType) -> +    <<"(",A/binary,"|",OsType/binary,")">>. + +coverage_trim_2(<<C/utf8,R/binary>> = Bin, I, L) -> +    case printable_char(C) of +        true -> +            coverage_trim_2(R, I - 1, [C | L]); +        false -> +            {I,Bin,lists:reverse(L)} +    end. + +printable_char($a) -> true; +printable_char(_) -> false. +  multiple_uses(Config) when is_list(Config) ->      {344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>),      true = multiple_uses_2(<<0,0,197,18>>), @@ -1754,11 +1847,10 @@ do_erl_689_2b(_, <<Length, Data/binary>>) ->  %% ERL-753  bs_start_match2_defs(_Config) -> -    {<<"http://127.0.0.1:1234/vsaas/hello">>} = api_url(<<"hello">>, dummy), -    {"https://127.0.0.1:4321/vsaas/hello"} = api_url({https, "hello"}, dummy). +    {<<"http://127.0.0.1:1234/vsaas/hello">>} = api_url(<<"hello">>), +    {"https://127.0.0.1:4321/vsaas/hello"} = api_url({https, "hello"}). -api_url(URL, Auth) -> -    Header = [], +api_url(URL) ->      case URL of          <<_/binary>> -> {<<"http://127.0.0.1:1234/vsaas/",URL/binary>>};          {https, [_|_] = URL1} -> {"https://127.0.0.1:4321/vsaas/"++URL1} @@ -1767,4 +1859,113 @@ api_url(URL, Auth) ->  check(F, R) ->      R = F(). +%% Make sure that an expression that comes between function start and a match +%% expression passes validation. +expression_before_match(Config) when is_list(Config) -> +    <<_,R/binary>> = id(<<0,1,2,3>>), +    {1, <<2,3>>} = expression_before_match_1(R), +    ok. + +expression_before_match_1(R) -> +    A = id(1), +    case R of +        <<1,Bar/binary>> -> {A, Bar}; +        <<>> -> {A, baz} +    end. + +%% Make sure that context positions are updated on calls. +restore_on_call(Config) when is_list(Config) -> +    ok = restore_on_call_1(<<0, 1, 2>>). + +restore_on_call_1(<<0, Rest/binary>>) -> +    <<2>> = restore_on_call_2(Rest), +    <<2>> = restore_on_call_2(Rest), %% {badmatch, <<>>} on missing restore. +    ok. + +restore_on_call_2(<<1, Rest/binary>>) -> Rest; +restore_on_call_2(Other) -> Other. + +%% 'catch' must invalidate positions. +restore_after_catch(Config) when is_list(Config) -> +    <<0, 1>> = restore_after_catch_1(<<0, 1>>), +    ok. + +restore_after_catch_1(<<A/binary>>) -> +    try throw_after_byte(A) of +        _ -> impossible +    catch +        throw:_Any -> +            %% Will equal <<1>> if the bug is present. +            A +    end. + +throw_after_byte(<<_,_/binary>>) -> +    throw(away). + +matches_on_parameter(Config) when is_list(Config) -> +    %% This improves coverage for matching on "naked" parameters. +    {<<"urka">>, <<"a">>} = matches_on_parameter_1(<<"gurka">>), +    ok = (catch matches_on_parameter_2(<<"10001110101">>, 0)). + +matches_on_parameter_1(Bin) -> +    <<"g", A/binary>> = Bin, +    <<_,_,"rk", B/binary>> = Bin, +    {A, B}. + +matches_on_parameter_2(Bin, Offset) -> +    <<_:Offset, Bit:1, Rest/bits>> = Bin, +    case bit_size(Rest) of +        0 -> throw(ok); +        _ -> [Bit | matches_on_parameter_2(Bin, Offset + 1)] +    end. + +big_positions(Config) when is_list(Config) -> +    %% This provides coverage for when match context positions no longer fit +    %% into an immediate on 32-bit platforms. + +    A = <<0:((1 bsl 27) - 8), $A, 1:1, "gurka", $A>>, +    B = <<0:((1 bsl 27) - 8), $B, "hello", $B>>, + +    {a,$A} = bp_start_match(A), +    {b,$B} = bp_start_match(B), +    {a,$A} = bp_getpos(A), +    {b,$B} = bp_getpos(B), + +    ok. + +%% After the first iteration the context's position will no longer fit into an +%% immediate. To improve performance the bs_start_match3 instruction will +%% return a new context with an updated base position so that we won't have to +%% resort to using bigints. +bp_start_match(<<_:(1 bsl 27),T/bits>>) -> bp_start_match(T); +bp_start_match(<<1:1,"gurka",A>>) -> {a,A}; +bp_start_match(<<"hello",B>>) -> {b,B}. + +%% This is a corner case where the above didn't work perfectly; if the position +%% was _just_ small enough to fit into an immediate when bs_start_match3 was +%% hit, but too large at bs_get_position, then it must be saved as a bigint. +bp_getpos(<<_:((1 bsl 27) - 8),T/bits>>) -> bp_getpos(T); +bp_getpos(<<A,1:1,"gurka",A>>) -> {a,A}; +bp_getpos(<<B,"hello",B>>) -> {b,B}. + +matching_meets_apply(_Config) -> +    <<"abc">> = do_matching_meets_apply(<<"/abc">>, []), +    42 = do_matching_meets_apply(<<"">>, {erlang,-42}), +    100 = do_matching_meets_apply(no_binary, {erlang,-100}), +    ok. + +do_matching_meets_apply(<<$/, Rest/binary>>, _Handler) -> +    id(Rest); +do_matching_meets_apply(<<_/binary>>=Name, never_matches_a) -> +    %% Used to crash the compiler because variables in a remote +    %% were not handled properly by beam_ssa_bsm. +    Name:foo(gurka); +do_matching_meets_apply(<<_/binary>>=Name, never_matches_b) -> +    %% Another case of the above. +    foo:Name(gurka); +do_matching_meets_apply(_Bin, {Handler, State}) -> +    %% Another case of the above. +    Handler:abs(State). + +  id(I) -> I. diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index 139f7af0d4..74f9dbd9b4 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -170,7 +170,7 @@ try_it(Module, Conf) ->  			atom_to_list(Module)),      Out = proplists:get_value(priv_dir,Conf),      io:format("Compiling: ~s\n", [Src]), -    CompRc0 = compile:file(Src, [clint0,clint,{outdir,Out},report, +    CompRc0 = compile:file(Src, [clint0,clint,ssalint,{outdir,Out},report,  				 bin_opt_info|OtherOpts]),      io:format("Result: ~p\n",[CompRc0]),      {ok,_Mod} = CompRc0, @@ -189,7 +189,7 @@ try_it(Module, Conf) ->      ct:timetrap(Timetrap),      io:format("Compiling (with old inliner): ~s\n", [Src]), -    CompRc2 = compile:file(Src, [clint, +    CompRc2 = compile:file(Src, [clint,ssalint,  				 {outdir,Out},report,bin_opt_info,  				 {inline,1000}|OtherOpts]),      io:format("Result: ~p\n",[CompRc2]), @@ -355,7 +355,7 @@ compile_compiler(Files, OutDir, Version, InlineOpts) ->      io:format("~ts", [code:which(compile)]),      io:format("Compiling ~s into ~ts", [Version,OutDir]),      Opts = [report, -	    clint0,clint, +	    clint0,clint,ssalint,  	    bin_opt_info,  	    {outdir,OutDir},  	    {d,'COMPILER_VSN',"\""++Version++"\""}, diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 6b230710b3..dade5d20d5 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -33,7 +33,7 @@  	 other_output/1, kernel_listing/1, encrypted_abstr/1,  	 strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1,  	 cover/1, env/1, core_pp/1, tuple_calls/1, -	 core_roundtrip/1, asm/1, optimized_guards/1, +	 core_roundtrip/1, asm/1,  	 sys_pre_attributes/1, dialyzer/1,  	 warnings/1, pre_load_check/1, env_compiler_options/1,           bc_options/1, deterministic_include/1, deterministic_paths/1 @@ -50,7 +50,7 @@ all() ->       binary, makedep, cond_and_ifdef, listings, listings_big,       other_output, kernel_listing, encrypted_abstr, tuple_calls,       strict_record, utf8_atoms, utf8_functions, extra_chunks, -     cover, env, core_pp, core_roundtrip, asm, optimized_guards, +     cover, env, core_pp, core_roundtrip, asm,       sys_pre_attributes, dialyzer, warnings, pre_load_check,       env_compiler_options, custom_debug_info, bc_options,       custom_compile_info, deterministic_include, deterministic_paths]. @@ -385,16 +385,17 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->      do_listing(Simple, TargetDir, dcbsm, ".core_bsm"),      do_listing(Simple, TargetDir, dsetel, ".dsetel"),      do_listing(Simple, TargetDir, dkern, ".kernel"), +    do_listing(Simple, TargetDir, dssa, ".ssa"), +    do_listing(Simple, TargetDir, dssaopt, ".ssaopt"), +    do_listing(Simple, TargetDir, dprecg, ".precodegen"),      do_listing(Simple, TargetDir, dcg, ".codegen"),      do_listing(Simple, TargetDir, dblk, ".block"),      do_listing(Simple, TargetDir, dexcept, ".except"), -    do_listing(Simple, TargetDir, dbs, ".bs"), -    do_listing(Simple, TargetDir, dtype, ".type"), -    do_listing(Simple, TargetDir, ddead, ".dead"),      do_listing(Simple, TargetDir, djmp, ".jump"),      do_listing(Simple, TargetDir, dclean, ".clean"),      do_listing(Simple, TargetDir, dpeep, ".peep"),      do_listing(Simple, TargetDir, dopt, ".optimize"), +    do_listing(Simple, TargetDir, diffable, ".S"),      %% First clean up.      Listings = filename:join(PrivDir, listings), @@ -424,6 +425,9 @@ listings_big(Config) when is_list(Config) ->      do_listing(Big, TargetDir, 'E'),      do_listing(Big, TargetDir, 'P'),      do_listing(Big, TargetDir, dkern, ".kernel"), +    do_listing(Big, TargetDir, dssa, ".ssa"), +    do_listing(Big, TargetDir, dssaopt, ".ssaopt"), +    do_listing(Big, TargetDir, dprecg, ".precodegen"),      do_listing(Big, TargetDir, to_dis, ".dis"),      TargetNoext = filename:rootname(Target, code:objfile_extension()), @@ -920,7 +924,7 @@ do_core_pp_1(M, A, Outdir) ->      ok = file:delete(CoreFile),      %% Compile as usual (including optimizations). -    compile_forms(M, Core, [clint,from_core,binary]), +    compile_forms(M, Core, [clint,ssalint,from_core,binary]),      %% Don't optimize to test that we are not dependent      %% on the Core Erlang optmimization passes. @@ -929,7 +933,7 @@ do_core_pp_1(M, A, Outdir) ->      %% records; if sys_core_fold was run it would fix      %% that; if sys_core_fold was not run v3_kernel would      %% crash.) -    compile_forms(M, Core, [clint,from_core,no_copt,binary]), +    compile_forms(M, Core, [clint,ssalint,from_core,no_copt,binary]),      ok. @@ -1170,95 +1174,6 @@ do_asm(Beam, Outdir) ->  	    error      end. -%% Make sure that guards are fully optimized. Guards should -%% should use 'test' instructions, not 'bif' instructions. - -optimized_guards(_Config) -> -    TestBeams = get_unique_beam_files(), -    test_lib:p_run(fun(F) -> do_opt_guards(F) end, TestBeams). - -do_opt_guards(Beam) -> -    {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} = -	beam_lib:chunks(Beam, [abstract_code]), -    try -	{ok,M,Asm} = compile:forms(A, ['S']), -	do_opt_guards_mod(Asm) -    catch Class:Error:Stk -> -	    io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]), -	    error -    end. - -do_opt_guards_mod({Mod,_Exp,_Attr,Asm,_NumLabels}) -> -    case do_opt_guards_fs(Mod, Asm) of -	[] -> -	    ok; -	[_|_]=Bifs -> -	    io:format("ERRORS FOR ~p:\n~p\n", [Mod,Bifs]), -	    error -    end. - -do_opt_guards_fs(Mod, [{function,Name,Arity,_,Is}|Fs]) -> -    Bifs0 = do_opt_guards_fun(Is), - -    %% The compiler does not attempt to optimize 'xor'. -    %% Therefore, ignore all functions that use 'xor' in -    %% a guard. -    Bifs = case lists:any(fun({bif,'xor',_,_,_}) -> true; -			     (_) -> false -			  end, Bifs0) of -	       true -> []; -	       false -> Bifs0 -	   end, - -    %% Filter out the allowed exceptions. -    FA = {Name,Arity}, -    case {Bifs,is_exception(Mod, FA)} of -	{[_|_],true} -> -	    io:format("~p:~p/~p IGNORED:\n~p\n", -		      [Mod,Name,Arity,Bifs]), -	    do_opt_guards_fs(Mod, Fs); -	{[_|_],false} -> -	    [{FA,Bifs}|do_opt_guards_fs(Mod, Fs)]; -	{[],false} -> -	    do_opt_guards_fs(Mod, Fs); -	{[],true} -> -	    io:format("Redundant exception for ~p:~p/~p\n", -		      [Mod,Name,Arity]), -	    error(redundant) -    end; -do_opt_guards_fs(_, []) -> []. - -do_opt_guards_fun([{bif,Name,{f,F},As,_}=I|Is]) when F =/= 0 -> -    Arity = length(As), -    case erl_internal:comp_op(Name, Arity) orelse -	erl_internal:bool_op(Name, Arity) orelse -	erl_internal:new_type_test(Name, Arity) of -	true -> -	    [I|do_opt_guards_fun(Is)]; -	false -> -	    do_opt_guards_fun(Is) -    end; -do_opt_guards_fun([_|Is]) -> -    do_opt_guards_fun(Is); -do_opt_guards_fun([]) -> []. - -is_exception(bs_match_SUITE, {matching_and_andalso_2,2}) -> true; -is_exception(bs_match_SUITE, {matching_and_andalso_3,2}) -> true; -is_exception(guard_SUITE, {'-complex_not/1-fun-4-',1}) -> true; -is_exception(guard_SUITE, {'-complex_not/1-fun-5-',1}) -> true; -is_exception(guard_SUITE, {basic_andalso_orelse,1}) -> true; -is_exception(guard_SUITE, {bad_guards,1}) -> true; -is_exception(guard_SUITE, {bad_guards_2,2}) -> true; -is_exception(guard_SUITE, {bad_guards_3,2}) -> true; -is_exception(guard_SUITE, {cqlc,4}) -> true; -is_exception(guard_SUITE, {csemi7,3}) -> true; -is_exception(guard_SUITE, {misc,1}) -> true; -is_exception(guard_SUITE, {nested_not_2b,4}) -> true; -is_exception(guard_SUITE, {tricky_1,2}) -> true; -is_exception(map_SUITE, {map_guard_update,2}) -> true; -is_exception(map_SUITE, {map_guard_update_variables,3}) -> true; -is_exception(_, _) -> false. -  sys_pre_attributes(Config) ->      DataDir = proplists:get_value(data_dir, Config),      File = filename:join(DataDir, "attributes.erl"), @@ -1475,36 +1390,49 @@ env_compiler_options(_Config) ->  bc_options(Config) ->      DataDir = proplists:get_value(data_dir, Config), -    101 = highest_opcode(DataDir, small_float, [no_get_hd_tl,no_line_info]), - -    103 = highest_opcode(DataDir, big, -                         [no_get_hd_tl,no_record_opt, -                          no_line_info,no_stack_trimming]), - -    125 = highest_opcode(DataDir, small_float, -                         [no_get_hd_tl,no_line_info,no_float_opt]), - -    132 = highest_opcode(DataDir, small, -                         [no_get_hd_tl,no_record_opt,no_float_opt,no_line_info]), - -    136 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt,no_line_info]), - -    153 = highest_opcode(DataDir, big, [no_get_hd_tl,no_record_opt]), -    153 = highest_opcode(DataDir, big, [r16]), -    153 = highest_opcode(DataDir, big, [r17]), -    153 = highest_opcode(DataDir, big, [r18]), -    153 = highest_opcode(DataDir, big, [r19]), -    153 = highest_opcode(DataDir, small_float, [r16]), -    153 = highest_opcode(DataDir, small_float, []), - -    158 = highest_opcode(DataDir, small_maps, [r17]), -    158 = highest_opcode(DataDir, small_maps, [r18]), -    158 = highest_opcode(DataDir, small_maps, [r19]), -    158 = highest_opcode(DataDir, small_maps, [r20]), -    158 = highest_opcode(DataDir, small_maps, []), - -    163 = highest_opcode(DataDir, big, []), - +    L = [{101, small_float, [no_get_hd_tl,no_line_info]}, +         {103, big, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, +                     no_line_info,no_stack_trimming]}, +         {125, small_float, [no_get_hd_tl,no_line_info,no_ssa_opt_float]}, + +         {132, small, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record, +                       no_ssa_opt_float,no_line_info,no_bsm3]}, + +         {153, small, [r20]}, +         {153, small, [r21]}, + +         {136, big, [no_put_tuple2,no_get_hd_tl, +                     no_ssa_opt_record,no_line_info]}, + +         {153, big, [no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]}, +         {153, big, [r16]}, +         {153, big, [r17]}, +         {153, big, [r18]}, +         {153, big, [r19]}, +         {153, small_float, [r16]}, +         {153, small_float, []}, + +         {158, small_maps, [r17]}, +         {158, small_maps, [r18]}, +         {158, small_maps, [r19]}, +         {158, small_maps, [r20]}, +         {158, small_maps, [r21]}, + +         {164, small_maps, []}, +         {164, big, []} +        ], + +    Test = fun({Expected,Mod,Options}) -> +                   case highest_opcode(DataDir, Mod, Options) of +                       Expected -> +                           ok; +                       Got -> +                           io:format("*** module ~p, options ~p => got ~p; expected ~p\n", +                                     [Mod,Options,Got,Expected]), +                           error +                   end +           end, +    test_lib:p_run(Test, L),      ok.  highest_opcode(DataDir, Mod, Opt) -> diff --git a/lib/compiler/test/compiler.cover b/lib/compiler/test/compiler.cover index 3fd7fc1937..fac0f9947c 100644 --- a/lib/compiler/test/compiler.cover +++ b/lib/compiler/test/compiler.cover @@ -1,5 +1,4 @@ -{incl_app,compiler,details}. -  %% -*- erlang -*- +{local_only,compiler,true}. +{incl_app,compiler,details}.  {excl_mods,compiler,[core_scan,core_parse]}. - diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 47606014c3..adfebd5158 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -212,9 +212,14 @@ bifs(Config) when is_list(Config) ->      {ok,#{K:=V}} = id(list_to_tuple([ok,#{K=>V}])),      ok. --define(CMP_SAME(A0, B), (fun(A) -> true = A == B, false = A /= B end)(id(A0))). --define(CMP_DIFF(A0, B), (fun(A) -> false = A == B, true = A /= B end)(id(A0))). -	        +-define(CMP_SAME0(A0, B), (fun(A) -> true = A == B, false = A /= B end)(id(A0))). +-define(CMP_SAME1(A0, B), (fun(A) -> false = A /= B, true = A == B end)(id(A0))). +-define(CMP_SAME(A0, B), (true = ?CMP_SAME0(A0, B) =:= not ?CMP_SAME1(A0, B))). + +-define(CMP_DIFF0(A0, B), (fun(A) -> false = A == B, true = A /= B end)(id(A0))). +-define(CMP_DIFF1(A0, B), (fun(A) -> true = A /= B, false = A == B end)(id(A0))). +-define(CMP_DIFF(A0, B), (true = ?CMP_DIFF0(A0, B) =:= not ?CMP_DIFF1(A0, B))). +  eq(Config) when is_list(Config) ->      ?CMP_SAME([a,b,c], [a,b,c]),      ?CMP_SAME([42.0], [42.0]), @@ -278,6 +283,8 @@ coverage(Config) when is_list(Config) ->      a = cover_remove_non_vars_alias({a,b,c}),      error = cover_will_match_lit_list(),      {ok,[a]} = cover_is_safe_bool_expr(a), +    false = cover_is_safe_bool_expr2(a), +    ok = cover_eval_is_function(fun id/1),      ok = cover_opt_guard_try(#cover_opt_guard_try{list=[a]}),      error = cover_opt_guard_try(#cover_opt_guard_try{list=[]}), @@ -341,6 +348,15 @@ cover_is_safe_bool_expr(X) ->  	    false      end. +cover_is_safe_bool_expr2(X) -> +    try +	V = [X], +    is_function(V, 1) +    catch +	_:_ -> +	    false +    end. +  cover_opt_guard_try(Msg) ->      if  	length(Msg#cover_opt_guard_try.list) =/= 1 -> @@ -349,6 +365,12 @@ cover_opt_guard_try(Msg) ->  	    ok      end. +cover_eval_is_function(X) -> +    case X of +        {a,_} -> is_function(X); +        _ -> ok +    end. +  bsm_an_inlined(<<_:8>>, _) -> ok;  bsm_an_inlined(_, _) -> error. @@ -356,7 +378,7 @@ unused_multiple_values_error(Config) when is_list(Config) ->      PrivDir = proplists:get_value(priv_dir, Config),      Dir = test_lib:get_data_dir(Config),      Core = filename:join(Dir, "unused_multiple_values_error"), -    Opts = [no_copt,clint,return,from_core,{outdir,PrivDir} +    Opts = [no_copt,clint,ssalint,return,from_core,{outdir,PrivDir}  	   |test_lib:opt_opts(?MODULE)],      {error,[{unused_multiple_values_error,  	     [{none,core_lint,{return_mismatch,{hello,1}}}]}], @@ -480,7 +502,7 @@ source(true, Activities) ->  	    Activities      end. -tim(#{reduction := Emergency}) -> +tim(#{reduction := _Emergency}) ->      try  	fun() -> surgery end      catch diff --git a/lib/compiler/test/float_SUITE.erl b/lib/compiler/test/float_SUITE.erl index 012810aba2..831e8279aa 100644 --- a/lib/compiler/test/float_SUITE.erl +++ b/lib/compiler/test/float_SUITE.erl @@ -20,7 +20,8 @@  -module(float_SUITE).  -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,   	 init_per_group/2,end_per_group/2, -	 pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1]). +	 pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1, +         subtract_number_type/1]).  -include_lib("common_test/include/ct.hrl"). @@ -28,7 +29,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].  all() ->       [pending, bif_calls, math_functions, -     mixed_float_and_int]. +     mixed_float_and_int, subtract_number_type].  groups() ->       []. @@ -176,5 +177,15 @@ mixed_float_and_int(Config) when is_list(Config) ->  pc(Cov, NotCov, X) ->      round(Cov/(Cov+NotCov)*100) + 42 + 2.0*X. +subtract_number_type(Config) when is_list(Config) -> +    120 = fact(5). + +fact(N) -> +    fact(N, 1). + +fact(0, P) -> P; +fact(1, P) -> P; +fact(N, P) -> fact(N-1, P*N). +  id(I) -> I. diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl index e00885fcd6..1df0a05275 100644 --- a/lib/compiler/test/fun_SUITE.erl +++ b/lib/compiler/test/fun_SUITE.erl @@ -249,6 +249,13 @@ badfun(_Config) ->      expect_badfun(X, catch X(put(?FUNCTION_NAME, of_course))),      of_course = erase(?FUNCTION_NAME), +    %% A literal as a Fun used to crash the code generator. This only happened +    %% when type optimization had reduced `Fun` to a literal, hence the match. +    Literal = fun(literal = Fun) -> +                      Fun() +              end, +    expect_badfun(literal, catch Literal(literal)), +      ok.  expect_badfun(Term, Exit) -> diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 6ad73b46f7..ed0a56f064 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -35,8 +35,7 @@  	 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, -         guard_in_catch/1,beam_bool_SUITE/1, -         cover_beam_dead/1]). +         guard_in_catch/1,beam_bool_SUITE/1]).  suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -54,8 +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,guard_in_catch,beam_bool_SUITE, -       cover_beam_dead]}]. +       bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE]}].  init_per_suite(Config) ->      test_lib:recompile(?MODULE), @@ -1297,6 +1295,32 @@ rel_ops(Config) when is_list(Config) ->      Empty = id([]),      ?T(==, [], Empty), +    %% Cover beam_ssa_dead:turn_op('/='). +    ok = (fun(A, B) when is_atom(A) -> +                  X = id(A /= B), +                  if +                      X -> ok; +                      true -> error +                  end +          end)(a, b), +    ok = (fun(A, B) when is_atom(A) -> +                  X = id(B /= A), +                  if +                        X -> ok; +                        true -> error +                    end +            end)(a, b), + +    %% Cover beam_ssa_dead. +    Arrow = fun([T1,T2]) when T1 == $>, T2 == $>; +                              T1 == $<, T2 == $| ->  true; +               (_) -> false +            end, +    true = Arrow(">>"), +    true = Arrow("<|"), +    false = Arrow("><"), +    false = Arrow(""), +      ok.  -undef(TestOp). @@ -1330,6 +1354,9 @@ rel_op_combinations_1(N, Digits) ->      Bool = is_digit_6(N),      Bool = is_digit_7(N),      Bool = is_digit_8(N), +    Bool = is_digit_9(42, N), +    Bool = is_digit_10(N, 0), +    Bool = is_digit_11(N, 0),      rel_op_combinations_1(N-1, Digits).  is_digit_1(X) when 16#0660 =< X, X =< 16#0669 -> true; @@ -1373,6 +1400,24 @@ is_digit_8(X) when X =< 16#0669, X > (16#0660-1) -> true;  is_digit_8(16#0670) -> false;  is_digit_8(_) -> false. +is_digit_9(A, 0) when A =:= 42 -> false; +is_digit_9(_, X) when X > 16#065F, X < 16#066A -> true; +is_digit_9(_, X) when 16#0030 =< X, X =< 16#0039 -> true; +is_digit_9(_, X) when 16#06F0 =< X, X =< 16#06F9 -> true; +is_digit_9(_, _) -> false. + +is_digit_10(0, 0) -> false; +is_digit_10(X, _) when X < 16#066A, 16#0660 =< X -> true; +is_digit_10(X, _) when 16#0030 =< X, X =< 16#0039 -> true; +is_digit_10(X, _) when 16#06F0 =< X, X =< 16#06F9 -> true; +is_digit_10(_, _) -> false. + +is_digit_11(0, 0) -> false; +is_digit_11(X, _) when X =< 16#0669, 16#0660 =< X -> true; +is_digit_11(X, _) when 16#0030 =< X, X =< 16#0039 -> true; +is_digit_11(X, _) when 16#06F0 =< X, X =< 16#06F9 -> true; +is_digit_11(_, _) -> false. +  rel_op_combinations_2(0, _) ->      ok;  rel_op_combinations_2(N, Range) -> @@ -1473,6 +1518,7 @@ rel_op_combinations_3(N, Red) ->      Val = redundant_9(N),      Val = redundant_10(N),      Val = redundant_11(N), +    Val = redundant_11(N),      rel_op_combinations_3(N-1, Red).  redundant_1(X) when X >= 51, X =< 80 -> 5*X; @@ -1527,6 +1573,10 @@ redundant_11(X) when X =:= 10 -> 2*X;  redundant_11(X) when X >= 51, X =< 80 -> 5*X;  redundant_11(_) -> none. +redundant_12(X) when X >= 50, X =< 80 -> 2*X; +redundant_12(X) when X < 51 -> 5*X; +redundant_12(_) -> none. +  %% Test type tests on literal values. (From emulator test suites.)  literal_type_tests(Config) when is_list(Config) ->      case ?MODULE of @@ -1779,15 +1829,10 @@ t_tuple_size(Config) when is_list(Config) ->      error = ludicrous_tuple_size({a,b,c}),      error = ludicrous_tuple_size([a,b,c]), -    %% Test the "unsafe case" - the register assigned the tuple size is -    %% not killed. -    DataDir = test_lib:get_data_dir(Config), -    File = filename:join(DataDir, "guard_SUITE_tuple_size"), -    {ok,Mod,Code} = compile:file(File, [from_asm,binary]), -    code:load_binary(Mod, File, Code), -    14 = Mod:t({1,2,3,4}), -    _ = code:delete(Mod), -    _ = code:purge(Mod), +    good_ip({1,2,3,4}), +    good_ip({1,2,3,4,5,6,7,8}), +    error = validate_ip({42,11}), +    error = validate_ip(atom),      ok. @@ -1805,6 +1850,16 @@ ludicrous_tuple_size(T)    when tuple_size(T) =:= 16#FFFFFFFFFFFFFFFF -> ok;  ludicrous_tuple_size(_) -> error. +good_ip(IP) -> +    IP = validate_ip(IP). + +validate_ip(Value) when is_tuple(Value) andalso +                        ((size(Value) =:= 4) orelse (size(Value) =:= 8)) -> +    %% size/1 (converted to tuple_size) used more than once. +    Value; +validate_ip(_) -> +    error. +  %%  %% The binary_part/2,3 guard BIFs  %% @@ -2206,32 +2261,6 @@ maps() ->  evidence(#{0 := Charge}) when 0; #{[] => Charge} == #{[] => 42} ->      ok. -cover_beam_dead(_Config) -> -    Mod = ?FUNCTION_NAME, -    Attr = [], -    Fs = [{function,test,1,2, -           [{label,1}, -            {line,[]}, -            {func_info,{atom,Mod},{atom,test},1}, -            {label,2}, -            %% Cover beam_dead:turn_op/1 using swapped operand order. -            {test,is_ne_exact,{f,3},[{integer,1},{x,0}]}, -            {test,is_eq_exact,{f,1},[{atom,a},{x,0}]}, -            {label,3}, -            {move,{atom,ok},{x,0}}, -            return]}], -    Exp = [{test,1}], -    Asm = {Mod,Exp,Attr,Fs,3}, -    {ok,Mod,Beam} = compile:forms(Asm, [from_asm,binary,report]), -    {module,Mod} = code:load_binary(Mod, Mod, Beam), -    ok = Mod:test(1), -    ok = Mod:test(a), -    {'EXIT',_} = (catch Mod:test(other)), -    true = code:delete(Mod), -    _ = code:purge(Mod), - -    ok. -  %% Call this function to turn off constant propagation.  id(I) -> I. diff --git a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S deleted file mode 100644 index cffb792920..0000000000 --- a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S +++ /dev/null @@ -1,30 +0,0 @@ -{module, guard_SUITE_tuple_size}.  %% version = 0 - -{exports, [{t,1}]}. - -{attributes, []}. - -{labels, 5}. - - -{function, t, 1, 2}. -  {label,1}. -    {func_info,{atom,guard_SUITE_tuple_size},{atom,t},1}. -  {label,2}. -    {bif,tuple_size,{f,4},[{x,0}],{x,1}}. -    {test,is_eq_exact,{f,4},[{x,1},{integer,4}]}. -    {test,is_tuple,{f,3},[{x,0}]}. -    {test,test_arity,{f,3},[{x,0},4]}. -    {get_tuple_element,{x,0},0,{x,5}}. -    {get_tuple_element,{x,0},1,{x,2}}. -    {get_tuple_element,{x,0},2,{x,3}}. -    {get_tuple_element,{x,0},3,{x,4}}. -    {gc_bif,'+',{f,0},6,[{x,1},{x,2}],{x,0}}. -    {gc_bif,'+',{f,0},6,[{x,0},{x,3}],{x,0}}. -    {gc_bif,'+',{f,0},6,[{x,0},{x,4}],{x,0}}. -    {gc_bif,'+',{f,0},6,[{x,0},{x,5}],{x,0}}. -    return. -  {label,3}. -    {badmatch,{x,0}}. -  {label,4}. -    {jump,{f,1}}. diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index dcc703c3e1..f700059d20 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -42,13 +42,9 @@ groups() ->  init_per_suite(Config) ->      test_lib:recompile(?MODULE), -    Pa = "-pa " ++ filename:dirname(code:which(?MODULE)), -    {ok,Node} = start_node(compiler, Pa), -    [{testing_node,Node}|Config]. +    Config. -end_per_suite(Config) -> -    Node = proplists:get_value(testing_node, Config), -    test_server:stop_node(Node), +end_per_suite(_Config) ->      ok.  init_per_group(_GroupName, Config) -> @@ -89,26 +85,26 @@ attribute(Config) when is_list(Config) ->  ?comp(maps_inline_test).  try_inline(Mod, Config) -> -    Node = proplists:get_value(testing_node, Config),      Src = filename:join(proplists:get_value(data_dir, Config),  			atom_to_list(Mod)),      Out = proplists:get_value(priv_dir,Config),      %% Normal compilation.      io:format("Compiling: ~s\n", [Src]), -    {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info,clint]), +    {ok,Mod} = compile:file(Src, [{outdir,Out},report, +                                  bin_opt_info,clint,ssalint]),      ct:timetrap({minutes,10}), -    NormalResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), +    NormalResult = load_and_call(Out, Mod),      %% Inlining.      io:format("Compiling with old inliner: ~s\n", [Src]),      {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info, -					{inline,1000},clint]), +                                  {inline,1000},clint,ssalint]),      %% Run inlined code.      ct:timetrap({minutes,10}), -    OldInlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), +    OldInlinedResult = load_and_call(Out, Mod),      %% Compare results.      compare(NormalResult, OldInlinedResult), @@ -117,11 +113,11 @@ try_inline(Mod, Config) ->      %% Inlining.      io:format("Compiling with new inliner: ~s\n", [Src]),      {ok,Mod} = compile:file(Src, [{outdir,Out},report, -					bin_opt_info,inline,clint]), +					bin_opt_info,inline,clint,ssalint]),      %% Run inlined code.      ct:timetrap({minutes,10}), -    InlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), +    InlinedResult = load_and_call(Out, Mod),      %% Compare results.      compare(NormalResult, InlinedResult), @@ -130,6 +126,11 @@ try_inline(Mod, Config) ->      %% Delete Beam file.      ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())), +    %% Delete loaded module. +    _ = code:purge(Mod), +    _ = code:delete(Mod), +    _ = code:purge(Mod), +      ok.  compare(Same, Same) -> ok; @@ -143,12 +144,6 @@ compare([H1|_], [H2|_]) ->      ct:fail(different);  compare([], []) -> ok. -start_node(Name, Args) -> -    case test_server:start_node(Name, slave, [{args,Args}]) of -	{ok,Node} -> {ok, Node}; -	Error  -> ct:fail(Error) -    end. -  load_and_call(Out, Module) ->      io:format("Loading...\n",[]),      code:purge(Module), @@ -351,7 +346,8 @@ otp_7223_2({a}) ->  coverage(Config) when is_list(Config) ->      Mod = bsdecode,      Src = filename:join(proplists:get_value(data_dir, Config), Mod), -    {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0},clint]), +    {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0}, +                                    clint,ssalint]),      {ok,Mod,_} = compile:file(Src, [binary,report,{inline,20}, -				    verbose,clint]), +				    verbose,clint,ssalint]),      ok. diff --git a/lib/compiler/test/inline_SUITE_data/barnes2.erl b/lib/compiler/test/inline_SUITE_data/barnes2.erl index a986331060..49e9bdfb6b 100644 --- a/lib/compiler/test/inline_SUITE_data/barnes2.erl +++ b/lib/compiler/test/inline_SUITE_data/barnes2.erl @@ -6,7 +6,7 @@  ?MODULE() ->      Stars = create_scenario(1000, 1.0),      R = hd(loop(10,1000.0,Stars,0)), -    Str = lists:flatten(io:lib_format("~s", [R])), +    Str = lists:flatten(io_lib:format("~p", [R])),      {R,Str =:= {1.00000,-1.92269e+4,-1.92269e+4,2.86459e-2,2.86459e-2}}.  create_scenario(N, M) -> diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 3e0ab78390..440b632381 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -70,7 +70,10 @@  	t_bad_update/1,          %% new in OTP 21 -        t_reused_key_variable/1 +        t_reused_key_variable/1, + +        %% new in OTP 22 +        t_mixed_clause/1,cover_beam_trim/1      ]).  suite() -> []. @@ -124,7 +127,10 @@ all() ->          t_bad_update,          %% new in OTP 21 -        t_reused_key_variable +        t_reused_key_variable, + +        %% new in OTP 22 +        t_mixed_clause,cover_beam_trim      ].  groups() -> []. @@ -1373,22 +1379,22 @@ map_usage(Def, Used) ->  t_guard_sequence(Config) when is_list(Config) -> -	{1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), -	{2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), -	{3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), -	{4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), -	{5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), - -	{1,M1}       = map_guard_sequence_2(M1 = id(#{a=>3})), -	{2,M2}       = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), -	{3,gg,M3}    = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), -	{4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), -	{5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), -	 -	%% error case -	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), -	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), -	ok. +    {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}), +    {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}), +    {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}), +    {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}), +    {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}), + +    {1,M1}       = map_guard_sequence_2(M1 = id(#{a=>3})), +    {2,M2}       = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})), +    {3,gg,M3}    = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})), +    {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})), +    {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})), + +    %% error case +    {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})), +    {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), +    ok.  t_guard_sequence_large(Config) when is_list(Config) ->      M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", @@ -1443,21 +1449,21 @@ t_guard_sequence_large(Config) when is_list(Config) ->                    18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",                    19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), -	{1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}), -	{2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}), -	{3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}), -	{4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}), -	{5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}), +    {1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}), +    {2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}), +    {3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}), +    {4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}), +    {5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}), -	{1,M1}       = map_guard_sequence_2(M1 = id(M0#{a=>3})), -	{2,M2}       = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})), -	{3,gg,M3}    = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})), -	{4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})), -	{5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})), +    {1,M1}       = map_guard_sequence_2(M1 = id(M0#{a=>3})), +    {2,M2}       = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})), +    {3,gg,M3}    = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})), +    {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})), +    {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})), -	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})), -	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})), -        ok. +    {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})), +    {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})), +    ok.  map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};  map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; @@ -2079,7 +2085,7 @@ t_register_corruption(Config) when is_list(Config) ->      {3,wanted,<<"value">>} = register_corruption_foo(wanted,M),      ok. -register_corruption_foo(A,#{a := V1, b := V2}) -> +register_corruption_foo(_,#{a := V1, b := V2}) ->      register_corruption_dummy_call(1,V1,V2);  register_corruption_foo(A,#{b := V}) ->      register_corruption_dummy_call(2,A,V); @@ -2161,6 +2167,31 @@ t_reused_key_variable(Config) when is_list(Config) ->              ok      end. +t_mixed_clause(_Config) -> +    put(fool_inliner, x), +    K = get(fool_inliner), +    {42,100} = case #{K=>42,y=>100} of +                   #{x:=X,y:=Y} -> +                       {X,Y} +               end, +    nomatch = case #{K=>42,y=>100} of +                  #{x:=X,y:=0} -> +                      {X,Y}; +                  #{} -> +                      nomatch +              end, +    ok. + +cover_beam_trim(_Config) -> +    val = do_cover_beam_trim(id, max, max, id, #{id=>val}), +    ok. + +do_cover_beam_trim(Id, OldMax, Max, Id, M) -> +    OldMax = id(Max), +    #{Id:=Val} = id(M), +    Val. + +  %% aux  rand_terms(0) -> []; diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 72e5356a8d..94bfbb0efe 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -25,7 +25,7 @@  	 match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,  	 selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,  	 coverage/1,grab_bag/1,literal_binary/1, -         unary_op/1]). +         unary_op/1,eq_types/1,match_after_return/1]).  -include_lib("common_test/include/ct.hrl"). @@ -40,7 +40,8 @@ groups() ->         match_in_call,untuplify,         shortcut_boolean,letify_guard,selectify,deselectify,         underscore,match_map,map_vars_used,coverage, -       grab_bag,literal_binary,unary_op]}]. +       grab_bag,literal_binary,unary_op,eq_types, +       match_after_return]}].  init_per_suite(Config) -> @@ -254,6 +255,8 @@ non_matching_aliases(_Config) ->      none = mixed_aliases([d]),      none = mixed_aliases({a,42}),      none = mixed_aliases(42), +    none = mixed_aliases(<<6789:16>>), +    none = mixed_aliases(#{key=>value}),      {'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)),      {'EXIT',{{badmatch,job},_}} = (catch entirely()), @@ -279,6 +282,16 @@ mixed_aliases(<<X:8>> = x) -> {a,X};  mixed_aliases([b] = <<X:8>>) -> {b,X};  mixed_aliases(<<X:8>> = {a,X}) -> {c,X};  mixed_aliases([X] = <<X:8>>) -> {d,X}; +mixed_aliases(<<X:16>> = X) -> {e,X}; +mixed_aliases(X = <<X:16>>) -> {f,X}; +mixed_aliases(<<X:16,_/binary>> = X) -> {g,X}; +mixed_aliases(X = <<X:16,_/binary>>) -> {h,X}; +mixed_aliases(X = #{key:=X}) -> {i,X}; +mixed_aliases(#{key:=X} = X) -> {j,X}; +mixed_aliases([X] = #{key:=X}) -> {k,X}; +mixed_aliases(#{key:=X} = [X]) -> {l,X}; +mixed_aliases({a,X} = #{key:=X}) -> {m,X}; +mixed_aliases(#{key:=X} = {a,X}) -> {n,X};  mixed_aliases(_) -> none.  nomatch_alias(I) -> @@ -456,6 +469,7 @@ letify_guard(A, B) ->  selectify(Config) when is_list(Config) ->      integer = sel_different_types({r,42}),      atom = sel_different_types({r,forty_two}), +    float = sel_different_types({r,100.0}),      none = sel_different_types({r,18}),      {'EXIT',_} = (catch sel_different_types([a,b,c])), @@ -466,12 +480,15 @@ selectify(Config) when is_list(Config) ->      integer42 = sel_same_value2(42),      integer43 = sel_same_value2(43),      error = sel_same_value2(44), +      ok.  sel_different_types({r,_}=T) when element(2, T) =:= forty_two ->      atom;  sel_different_types({r,_}=T) when element(2, T) =:= 42 ->      integer; +sel_different_types({r,_}=T) when element(2, T) =:= 100.0 -> +    float;  sel_different_types({r,_}) ->      none. @@ -489,9 +506,8 @@ sel_same_value2(V) when V =:= 42; V =:= 43 ->  sel_same_value2(_) ->      error. -%% Test deconstruction of select_val instructions in beam_peep into -%% regular tests with just one possible value left. Hitting proper cases -%% in beam_peep relies on unification of labels by beam_jump. +%% Test deconstruction of select_val instructions to regular tests +%% with zero or one values left.  deselectify(Config) when is_list(Config) ->      one_or_other = desel_tuple_arity({1}), @@ -512,7 +528,31 @@ deselectify(Config) when is_list(Config) ->      one_or_other = dsel_atom_typecheck(one),      two = dsel_atom_typecheck(two), -    one_or_other = dsel_atom_typecheck(three). +    one_or_other = dsel_atom_typecheck(three), + +    %% Cover deconstruction of select_val instructions in +    %% beam_peep. + +    stop = dsel_peek_0(stop), +    ignore = dsel_peek_0(ignore), +    Config = dsel_peek_0(Config), + +    stop = dsel_peek_1(stop, any), +    Config = dsel_peek_1(ignore, Config), +    other = dsel_peek_1(other, ignored), + +    0 = dsel_peek_2(0, any), +    Config = dsel_peek_2(1, Config), +    2 = dsel_peek_2(2, ignored), + +    true = dsel_peek_3(true), +    false = dsel_peek_3(false), +    {error,Config} = dsel_peek_3(Config), + +    ok. + +%% The following will be optimized by the sharing optimizations +%% in beam_ssa_opt.  desel_tuple_arity(Tuple) when is_tuple(Tuple) ->      case Tuple of @@ -549,6 +589,39 @@ dsel_atom_typecheck(Val) when is_atom(Val) ->          _ -> one_or_other      end. +%% The following functions are carefully crafted so that the sharing +%% optimizations in beam_ssa_opt can't be applied. After applying the +%% beam_jump:eliminate_moves/1 optimization and beam_clean:clean_labels/1 +%% has unified labels, beam_peep is able to optimize these functions. + +dsel_peek_0(A0) -> +    case id(A0) of +        stop ->   stop; +        ignore -> ignore; +        A ->      A +    end. + +dsel_peek_1(A0, B) -> +    case id(A0) of +        stop ->   stop; +        ignore -> B; +        A ->      A +    end. + +dsel_peek_2(A0, B) -> +    case id(A0) of +        0 -> 0; +        1 -> B; +        A -> A +    end. + +dsel_peek_3(A0) -> +    case id(A0) of +        true ->  true; +        false -> false; +        Other -> {error,Other} +    end. +  underscore(Config) when is_list(Config) ->      case Config of  	[] -> @@ -591,13 +664,26 @@ do_map_vars_used(X, Y, Map) ->  	    Val      end. +-record(coverage_id, {bool=false,id}).  coverage(Config) when is_list(Config) ->      %% Cover beam_dead.      ok = coverage_1(x, a),      ok = coverage_1(x, b),      %% Cover sys_pre_expand. -    ok = coverage_3("abc"). +    ok = coverage_3("abc"), + +    %% Cover beam_ssa_dead. +    {expr,key} = coverage_4([literal,get], [[expr,key]]), +    {expr,key} = coverage_4([expr,key], []), + +    a = coverage_5([8,8,8], #coverage_id{bool=true}), +    b = coverage_5([], #coverage_id{bool=true}), + +    %% Cover beam_ssa_opt. +    ok = coverage_6(), + +    ok.  coverage_1(B, Tag) ->      case Tag of @@ -610,6 +696,37 @@ coverage_2(2, b, x) -> ok.  coverage_3([$a]++[]++"bc") -> ok. +%% Cover beam_ssa_dead:eval_type_test_1(is_nonempty_list, Arg). +coverage_4([literal,get], [Expr]) -> +    coverage_4(Expr, []); +coverage_4([Expr,Key], []) -> +    {Expr,Key}. + +%% Cover beam_ssa_dead:eval_type_test_1(is_tagged_tuple, Arg). +coverage_5(Config, TermId) +  when TermId =:= #coverage_id{bool=true}, +       Config =:= [8,8,8] -> +    a; +coverage_5(_Config, #coverage_id{bool=true}) -> +    b. + +coverage_6() -> +    X = 17, +    case +        case id(1) > 0 of +            true -> +                17; +            false -> +                42 +        end +    of +        X -> +            ok; +        V -> +            %% Cover beam_ssa_opt:make_literal/2. +            error([error,X,V]) +    end. +  grab_bag(_Config) ->      [_|T] = id([a,b,c]),      [b,c] = id(T), @@ -754,5 +871,35 @@ unary_op_1(Vop@1) ->              end      end. +eq_types(_Config) -> +    Ref = make_ref(), +    Ref = eq_types(Ref, any), +    ok. + +eq_types(A, B) -> +    %% {put_tuple2,{y,0},{list,[{x,0},{x,1}]}}. +    Term0 = {A, B}, +    Term = id(Term0), + +    %% {test,is_eq_exact,{f,3},[{y,0},{x,0}]}. +    %% Here beam_validator must infer that {x,0} has the +    %% same type as {y,0}. +    Term = Term0, + +    %% {get_tuple_element,{x,0},0,{x,0}}. +    {Ref22,_} = Term, + +    Ref22. + +match_after_return(Config) when is_list(Config) -> +    %% The return type of the following call will never match the 'wont_happen' +    %% clauses below, and the beam_ssa_type was clever enough to see that but +    %% didn't remove the blocks, so it crashed when trying to extract A. +    ok = case mar_test_tuple(erlang:unique_integer()) of +            {gurka, never_matches, A} -> {wont_happen, A}; +            _ -> ok +         end. + +mar_test_tuple(I) -> {gurka, I}.  id(I) -> I. diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index a1d931b994..e999c8ffae 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -171,7 +171,7 @@ silly_coverage(Config) when is_list(Config) ->      expect_error(fun() -> sys_core_dsetel:module(BadCoreErlang, []) end),      expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end), -    %% v3_codegen +    %% beam_kernel_to_ssa      BadKernel = {k_mdef,[],?MODULE,  		 [{foo,0}],  		 [], @@ -179,7 +179,33 @@ silly_coverage(Config) when is_list(Config) ->  		   {k,[],[],[]},  		   f,0,[],  		   seriously_bad_body}]}, -    expect_error(fun() -> v3_codegen:module(BadKernel, []) end), +    expect_error(fun() -> beam_kernel_to_ssa:module(BadKernel, []) end), + +    %% beam_ssa_lint +    %% beam_ssa_recv +    %% beam_ssa_share +    %% beam_ssa_pre_codegen +    %% beam_ssa_opt +    %% beam_ssa_codegen +    BadSSA = {b_module,#{},a,b,c, +              [{b_function,#{func_info=>{mod,foo,0}},args,bad_blocks,0}]}, +    expect_error(fun() -> beam_ssa_lint:module(BadSSA, []) end), +    expect_error(fun() -> beam_ssa_recv:module(BadSSA, []) end), +    expect_error(fun() -> beam_ssa_share:module(BadSSA, []) end), +    expect_error(fun() -> beam_ssa_pre_codegen:module(BadSSA, []) end), +    expect_error(fun() -> beam_ssa_opt:module(BadSSA, []) end), +    expect_error(fun() -> beam_ssa_codegen:module(BadSSA, []) end), + +    %% beam_ssa_lint, beam_ssa_pp +    {error,[{_,Errors}]} = beam_ssa_lint:module(bad_ssa_lint_input(), []), +    _ = [io:put_chars(Mod:format_error(Reason)) || +            {Mod,Reason} <- Errors], + +    %% Cover printing of annotations in beam_ssa_pp +    PPAnno = #{func_info=>{mod,foo,0},other_anno=>value,map_anno=>#{k=>v}}, +    PPBlocks = #{0=>{b_blk,#{},[],{b_ret,#{},{b_literal,42}}}}, +    PP = {b_function,PPAnno,[],PPBlocks,0}, +    io:put_chars(beam_ssa_pp:format_function(PP)),      %% beam_a      BeamAInput = {?MODULE,[{foo,0}],[], @@ -189,14 +215,6 @@ silly_coverage(Config) when is_list(Config) ->  		     {label,2}|non_proper_list]}],99},      expect_error(fun() -> beam_a:module(BeamAInput, []) end), -    %% beam_reorder -    BlockInput = {?MODULE,[{foo,0}],[], -		  [{function,foo,0,2, -		    [{label,1}, -		     {func_info,{atom,?MODULE},{atom,foo},0}, -		     {label,2}|non_proper_list]}],99}, -    expect_error(fun() -> beam_reorder:module(BlockInput, []) end), -      %% beam_block      BlockInput = {?MODULE,[{foo,0}],[],  		  [{function,foo,0,2, @@ -205,19 +223,6 @@ silly_coverage(Config) when is_list(Config) ->  		     {label,2}|non_proper_list]}],99},      expect_error(fun() -> beam_block:module(BlockInput, []) end), -    %% beam_bs -    BsInput = BlockInput, -    expect_error(fun() -> beam_bs:module(BsInput, []) end), - -    %% beam_type -    TypeInput = {?MODULE,[{foo,0}],[], -		   [{function,foo,0,2, -		     [{label,1}, -		      {line,loc}, -		      {func_info,{atom,?MODULE},{atom,foo},0}, -		      {label,2}|non_proper_list]}],99}, -    expect_error(fun() -> beam_type:module(TypeInput, []) end), -      %% beam_except      ExceptInput = {?MODULE,[{foo,0}],[],  		   [{function,foo,0,2, @@ -227,15 +232,9 @@ silly_coverage(Config) when is_list(Config) ->  		      {label,2}|non_proper_list]}],99},      expect_error(fun() -> beam_except:module(ExceptInput, []) end), -    %% beam_dead. This is tricky. Our function must look OK to -    %% beam_utils:clean_labels/1, but must crash beam_dead. -    DeadInput = {?MODULE,[{foo,0}],[], -		  [{function,foo,0,2, -		    [{label,1}, -		     {func_info,{atom,?MODULE},{atom,foo},0}, -		     {label,2}, -		     {test,is_eq_exact,{f,1},[bad,operands]}]}],99}, -    expect_error(fun() -> beam_dead:module(DeadInput, []) end), +    %% beam_jump +    JumpInput = BlockInput, +    expect_error(fun() -> beam_jump:module(JumpInput, []) end),      %% beam_clean      CleanInput = {?MODULE,[{foo,0}],[], @@ -246,6 +245,10 @@ silly_coverage(Config) when is_list(Config) ->  		     {jump,{f,42}}]}],99},      expect_error(fun() -> beam_clean:module(CleanInput, []) end), +    %% beam_jump +    TrimInput = BlockInput, +    expect_error(fun() -> beam_trim:module(TrimInput, []) end), +      %% beam_peep. This is tricky. Use a select instruction with      %% an odd number of elements in the list to crash      %% prune_redundant_values/2 but not beam_clean:clean_labels/1. @@ -253,48 +256,10 @@ silly_coverage(Config) when is_list(Config) ->  		 [{function,foo,0,2,  		   [{label,1},  		    {func_info,{atom,?MODULE},{atom,foo},0}, -		    {label,2},{select,op,r,{f,2},[{f,2}]}]}], +		    {label,2},{select,select_val,r,{f,2},[{f,2}]}]}],  		 2},      expect_error(fun() -> beam_peep:module(PeepInput, []) end), -    %% beam_bsm. This is tricky. Our function must be sane enough to not crash -    %% btb_index/1, but must crash the main optimization pass. -    BsmInput = {?MODULE,[{foo,0}],[], -		[{function,foo,0,2, -		  [{label,1}, -		   {func_info,{atom,?MODULE},{atom,foo},0}, -		   {label,2}, -		   {test,bs_get_binary2,{f,99},0,[{x,0},{atom,all},1,[]],{x,0}}, -		   {block,[a|b]}]}],0}, -    expect_error(fun() -> beam_bsm:module(BsmInput, []) end), - -    %% beam_receive. -    ReceiveInput = {?MODULE,[{foo,0}],[], -		    [{function,foo,0,2, -		      [{label,1}, -		       {func_info,{atom,?MODULE},{atom,foo},0}, -		       {label,2}, -		       {call_ext,0,{extfunc,erlang,make_ref,0}}, -		       {block,[a|b]}]}],0}, -    expect_error(fun() -> beam_receive:module(ReceiveInput, []) end), - -    %% beam_record. -    RecordInput = {?MODULE,[{foo,0}],[], -		    [{function,foo,1,2, -		      [{label,1}, -		       {func_info,{atom,?MODULE},{atom,foo},1}, -                       {label,2}, -                       {test,is_tuple,{f,1},[{x,0}]}, -                       {test,test_arity,{f,1},[{x,0},3]}, -                       {block,[{set,[{x,1}],[{x,0}],{get_tuple_element,0}}]}, -                       {test,is_eq_exact,{f,1},[{x,1},{atom,bar}]}, -                       {block,[{set,[{x,2}],[{x,0}],{get_tuple_element,1}}|a]}, -                       {test,is_eq_exact,{f,1},[{x,2},{integer,1}]}, -                       {block,[{set,[{x,0}],[{atom,ok}],move}]}, -                       return]}],0}, - -    expect_error(fun() -> beam_record:module(RecordInput, []) end), -      BeamZInput = {?MODULE,[{foo,0}],[],  		  [{function,foo,0,2,  		    [{label,1}, @@ -312,6 +277,31 @@ silly_coverage(Config) when is_list(Config) ->      ok. +bad_ssa_lint_input() -> +    {b_module,#{},t, +     [{foobar,1},{module_info,0},{module_info,1}], +     [], +     [{b_function, +       #{func_info => {t,foobar,1},location => {"t.erl",4}}, +       [{b_var,0}], +       #{0 => {b_blk,#{},[],{b_ret,#{},{b_var,'@undefined_var'}}}}, +       3}, +      {b_function, +       #{func_info => {t,module_info,0}}, +       [], +       #{0 => +             {b_blk,#{}, +              [{b_set,#{}, +                {b_var,{'@ssa_ret',3}}, +                call, +                [{b_remote, +                  {b_literal,erlang}, +                  {b_literal,get_module_info}, +                  1}, +                 {b_var,'@unknown_variable'}]}], +              {b_ret,#{},{b_var,{'@ssa_ret',3}}}}}, +       4}]}. +  expect_error(Fun) ->      try	Fun() of  	Any -> diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index 4219768d6f..12108445f0 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -25,7 +25,7 @@  	 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, -	 wait/1,recv_in_try/1,double_recv/1]). +	 wait/1,recv_in_try/1,double_recv/1,receive_var_zero/1]).  -include_lib("common_test/include/ct.hrl"). @@ -45,7 +45,7 @@ all() ->  groups() ->       [{p,test_lib:parallel(),        [recv,coverage,otp_7980,ref_opt,export,wait, -       recv_in_try,double_recv]}]. +       recv_in_try,double_recv,receive_var_zero]}].  init_per_suite(Config) -> @@ -378,4 +378,27 @@ do_double_recv(_, Msg) ->              error      end. +%% Test 'after Z', when Z =:= 0 been propagated as an immediate by the type +%% optimization pass. +receive_var_zero(Config) when is_list(Config) -> +    self() ! x, +    self() ! y, +    Z = zero(), +    timeout = receive +                  z -> ok +              after Z -> timeout +              end, +    timeout = receive +              after Z -> timeout +              end, +    self() ! w, +    receive +	x -> ok; +	Other -> +	    ct:fail({bad_message,Other}) +    end. + +zero() -> 0. + +  id(I) -> I. diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/no_5.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/no_5.erl new file mode 100644 index 0000000000..4fbde3a83d --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/no_5.erl @@ -0,0 +1,38 @@ +-module(no_5). +-compile([export_all,nowarn_export_all]). + +?MODULE() -> +    ok. + +%% Nested receives were not handled properly. + +confusing_recv_mark(Pid) -> +    Ref = make_ref(), +    %% There would be a recv_mark here. +    MRef = erlang:monitor(process, Pid), +    receive +        Ref -> +            %% And a recv_set here. +            receive +                MRef -> gurka +            end; +        MRef -> +            gaffel +    end. + +%% The optimization could potentially be improved to +%% handle matching of multiple refs, like this: + +proper_recv_mark(Pid) -> +    %% Place the recv_mark before the creation of both refs. +    Ref = make_ref(), +    MRef = erlang:monitor(process, Pid), +    %% Place the recv_set here. +    receive +        Ref -> +            receive +                MRef -> gurka +            end; +        MRef -> +            gaffel +    end. 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 deleted file mode 100644 index fd14228135..0000000000 --- a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S +++ /dev/null @@ -1,71 +0,0 @@ -{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_14.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.erl new file mode 100644 index 0000000000..aa47c02af9 --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.erl @@ -0,0 +1,27 @@ +-module(yes_14). +-compile(export_all). + +?MODULE() -> +    ok. + +do_call(Process, Request) -> +    Mref = erlang:monitor(process, Process), +    Process ! Request, +    Local = case node(Process) of +                Node when Node =:= node() -> true; +                _Node -> false +            end, +    id(Local), +    receive +	{X,Y,Z} when Mref =/= X, Z =:= 42, Mref =:= Y -> +	    error; +	{X,Y,_} when Mref =/= X, Mref =:= Y -> +	    error; +	{Mref, Reply} -> +	    erlang:demonitor(Mref, [flush]), +	    {ok, Reply}; +	{'DOWN', Mref, _, _, _} -> +	    error +    end. + +id(I) -> I. diff --git a/lib/compiler/test/regressions_SUITE.erl b/lib/compiler/test/regressions_SUITE.erl index 9b0b9b0c38..39febf060f 100644 --- a/lib/compiler/test/regressions_SUITE.erl +++ b/lib/compiler/test/regressions_SUITE.erl @@ -23,7 +23,7 @@  -export([all/0,groups/0,init_per_testcase/2,end_per_testcase/2,           init_per_group/2,end_per_group/2, -	 init_per_testcase/2,end_per_testcase/2, +	 init_per_suite/1,end_per_suite/1,           suite/0]).  -export([maps/1]). diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl index 8954a9f5fb..7fb4751b42 100644 --- a/lib/compiler/test/test_lib.erl +++ b/lib/compiler/test/test_lib.erl @@ -72,14 +72,17 @@ opt_opts(Mod) ->      {options,Opts} = lists:keyfind(options, 1, Comp),      lists:filter(fun(no_copt) -> true;  		    (no_postopt) -> true; -		    (no_float_opt) -> true; -		    (no_new_funs) -> true; -		    (no_new_binaries) -> true; -		    (no_new_apply) -> true; -		    (no_gc_bifs) -> true; +                    (no_ssa_opt) -> true; +                    (no_recv_opt) -> true; +		    (no_ssa_float) -> true;  		    (no_stack_trimming) -> true;  		    (debug_info) -> true;  		    (inline) -> true; +                    (no_put_tuple2) -> true; +                    (no_bsm3) -> true; +                    (no_bsm_opt) -> true; +                    (no_module_opt) -> true; +                    (no_type_opt) -> true;  		    (_) -> false  		 end, Opts). @@ -91,17 +94,21 @@ get_data_dir(Config) ->      Data0 = proplists:get_value(data_dir, Config),      Opts = [{return,list}],      Data1 = re:replace(Data0, "_no_opt_SUITE", "_SUITE", Opts), -    Data = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts), -    re:replace(Data, "_inline_SUITE", "_SUITE", Opts). +    Data2 = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts), +    Data3 = re:replace(Data2, "_inline_SUITE", "_SUITE", Opts), +    Data4 = re:replace(Data3, "_r21_SUITE", "_SUITE", Opts), +    re:replace(Data4, "_no_module_opt_SUITE", "_SUITE", Opts).  is_cloned_mod(Mod) ->      is_cloned_mod_1(atom_to_list(Mod)).  %% Test whether Mod is a cloned module. -is_cloned_mod_1("no_opt_SUITE") -> true; -is_cloned_mod_1("post_opt_SUITE") -> true; -is_cloned_mod_1("inline_SUITE") -> true; +is_cloned_mod_1("_no_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("_no_module_opt_SUITE") -> true;  is_cloned_mod_1([_|T]) -> is_cloned_mod_1(T);  is_cloned_mod_1([]) -> false. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index 1c23eba06d..c5d0bf8420 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -523,25 +523,43 @@ bin_opt_info(Config) when is_list(Config) ->  	         <<>> -> ok               end. +             %% We use a tail in a BIF instruction, remote call, function +             %% return, and an optimizable tail call for better coverage. +             t2(<<A,B,T/bytes>>) -> +                 if +                     A > B -> t2(T); +                     A =< B -> T +                 end; +             t2(<<_,T/bytes>>) when byte_size(T) < 4 -> +                 foo;               t2(<<_,T/bytes>>) -> -               split_binary(T, 4). +                 split_binary(T, 4).             ">>, -    Ts1 = [{bsm1, -	    Code, -	    [bin_opt_info], -	    {warnings, -	     [{4,sys_core_bsm,orig_bin_var_used_in_guard}, -	      {5,beam_bsm,{no_bin_opt,{{t1,1},no_suitable_bs_start_match}}}, -	      {9,beam_bsm,{no_bin_opt, -			   {binary_used_in,{extfunc,erlang,split_binary,2}}}} ]}}], -    [] = run(Config, Ts1), + +    Ws = (catch run_test(Config, Code, [bin_opt_info])), + +    %% This is an inexact match since the pass reports exact instructions as +    %% part of the warnings, which may include annotations that vary from run +    %% to run. +    {warnings, +     [{5,beam_ssa_bsm,{unsuitable_call, +                        {{b_local,{b_literal,t1},1}, +                         {used_before_match, +                            {b_set,_,_,{bif,byte_size},[_]}}}}}, +      {5,beam_ssa_bsm,{binary_created,_,_}}, +      {11,beam_ssa_bsm,{binary_created,_,_}}, %% A =< B -> T +      {13,beam_ssa_bsm,context_reused},       %% A > B -> t2(T); +      {16,beam_ssa_bsm,{binary_created,_,_}}, %% when byte_size(T) < 4 -> +      {19,beam_ssa_bsm,{remote_call, +                         {b_remote, +                          {b_literal,erlang}, +                           {b_literal,split_binary},2}}}, +      {19,beam_ssa_bsm,{binary_created,_,_}}  %% split_binary(T, 4) +     ]} = Ws,      %% For coverage: don't give the bin_opt_info option. -    Ts2 = [{bsm2, -	    Code, -	    [], -	    []}], -    [] = run(Config, Ts2), +    [] = (catch run_test(Config, Code, [])), +      ok.  bin_construction(Config) when is_list(Config) -> @@ -747,7 +765,7 @@ maps_bin_opt_info(Config) when is_list(Config) ->                   M.             ">>,             [bin_opt_info], -           {warnings,[{2,beam_bsm,bin_opt}]}}], +           {warnings,[{3,beam_ssa_bsm,context_reused}]}}],      [] = run(Config, Ts),      ok. @@ -984,7 +1002,6 @@ run(Config, Tests) ->          end,      lists:foldl(F, [], Tests). -  %% Compiles a test module and returns the list of errors and warnings.  run_test(Conf, Test0, Warnings) -> | 
