diff options
-rw-r--r-- | lib/compiler/src/beam_clean.erl | 25 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 6 | ||||
-rw-r--r-- | lib/compiler/test/Makefile | 3 | ||||
-rw-r--r-- | lib/compiler/test/beam_utils_SUITE.erl | 236 | ||||
-rw-r--r-- | lib/compiler/test/beam_validator_SUITE.erl | 13 | ||||
-rw-r--r-- | lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S | 38 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 18 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE_data/dialyzer_test.erl | 39 | ||||
-rw-r--r-- | lib/compiler/test/map_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/compiler/test/receive_SUITE.erl | 38 |
10 files changed, 376 insertions, 44 deletions
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index b653998252..b68b8702e0 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -234,31 +234,6 @@ replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D); replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D); -replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); -replace([{bs_init_bits,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_init_bits,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); -replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_put_utf8=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_utf16=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_utf32=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D); -replace([{bs_append,{f,Lbl},_,_,_,_,_,_,_}=I0|Is], Acc, D) when Lbl =/= 0 -> - I = setelement(2, I0, {f,label(Lbl, D)}), - replace(Is, [I|Acc], D); -replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); -replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D); diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index e82ba82d38..84a2b782e9 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -758,13 +758,9 @@ live_opt([{line,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); %% The following instructions can occur if the "compilation" has been -%% started from a .S file using the 'asm' option. +%% started from a .S file using the 'from_asm' option. live_opt([{trim,_,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); -live_opt([{allocate,_,Live}=I|Is], _, D, Acc) -> - live_opt(Is, live_call(Live), D, [I|Acc]); -live_opt([{allocate_heap,_,_,Live}=I|Is], _, D, Acc) -> - live_opt(Is, live_call(Live), D, [I|Acc]); live_opt([{'%',_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{recv_set,_}=I|Is], Regs, D, Acc) -> diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 892a401c75..73d52a48bc 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -11,6 +11,7 @@ MODULES= \ beam_validator_SUITE \ beam_disasm_SUITE \ beam_except_SUITE \ + beam_utils_SUITE \ bs_bincomp_SUITE \ bs_bit_binaries_SUITE \ bs_construct_SUITE \ @@ -40,6 +41,7 @@ NO_OPT= \ andor \ apply \ beam_except \ + beam_utils \ bs_construct \ bs_match \ bs_utf \ @@ -59,6 +61,7 @@ NO_OPT= \ INLINE= \ andor \ apply \ + beam_utils \ bs_bincomp \ bs_bit_binaries \ bs_construct \ diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl new file mode 100644 index 0000000000..9142ca6485 --- /dev/null +++ b/lib/compiler/test/beam_utils_SUITE.erl @@ -0,0 +1,236 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(beam_utils_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + apply_fun/1,apply_mf/1,bs_init/1,bs_save/1, + is_not_killed/1,is_not_used_at/1, + select/1,y_catch/1]). +-export([id/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,test_lib:parallel(), + [apply_fun, + apply_mf, + bs_init, + bs_save, + is_not_killed, + is_not_used_at, + select, + y_catch + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +apply_fun(_Config) -> + 3 = do_apply_fun(false, false), + 3 = do_apply_fun(false, true), + 3 = do_apply_fun(true, false), + 2 = do_apply_fun(true, true), + ok. + +do_apply_fun(X, Y) -> + F = fun(I) -> I+1 end, + Arg = case X andalso id(Y) of + true -> 1; + false -> 2 + end, + F(Arg). + +apply_mf(_Config) -> + ok = do_apply_mf_used({a,b}, ?MODULE, id), + error = do_apply_mf_used([a], ?MODULE, id), + {'EXIT',{{case_clause,{[],b}},_}} = (catch do_apply_mf_used({[],b}, ?MODULE, id)), + + error = do_apply_mf_killed({error,[a]}, ?MODULE, id), + ok = do_apply_mf_killed([b], ?MODULE, id), + {'EXIT',{{case_clause,{a,[b]}},_}} = (catch do_apply_mf_killed({a,[b]}, ?MODULE, id)), + {'EXIT',{{case_clause,{error,[]}},_}} = (catch do_apply_mf_killed({error,[]}, ?MODULE, id)), + + ok. + +do_apply_mf_used(Arg, Mod, Func) -> + Res = case id(Arg) of + {Decoded,_} when Decoded =/= [] -> + ok; + List when is_list(List) -> + error + end, + Mod:Func(Res). + +do_apply_mf_killed(Arg, Mod, Func) -> + Res = case id(Arg) of + {Tag,Decoded} when Decoded =/= [], Tag =:= error -> + error; + List when is_list(List) -> + ok + end, + Mod:Func(Res). + +bs_init(_Config) -> + <<7>> = do_bs_init_1([?MODULE], 7), + error = do_bs_init_1([?MODULE], 0.0), + error = do_bs_init_1([?MODULE], -43), + error = do_bs_init_1([?MODULE], 42), + + <<>> = do_bs_init_2([]), + <<0:32,((1 bsl 32)-1):32>> = do_bs_init_2([0,(1 bsl 32)-1]), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([0.5])), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([-1])), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([1 bsl 32])), + ok. + +do_bs_init_1([?MODULE], Sz) -> + if + is_integer(Sz), Sz >= -42, Sz < 42 -> + id(<<Sz:8>>); + true -> + error + end. + +do_bs_init_2(SigNos) -> + << <<SigNo:32>> || + SigNo <- SigNos, + (is_integer(SigNo) andalso SigNo >= 0 andalso SigNo < (1 bsl 32)) orelse + erlang:error(badarg) + >>. + + +bs_save(_Config) -> + {a,30,<<>>} = do_bs_save(<<1:1,30:5>>), + {b,127,<<>>} = do_bs_save(<<1:1,31:5,0:1,127:7>>), + {c,127,<<>>} = do_bs_save(<<1:1,31:5,1:1,127:7>>), + {c,127,<<>>} = do_bs_save(<<0:1,31:5,1:1,127:7>>), + {d,1024,<<>>} = do_bs_save(<<0:1,31:5>>), + ok. + +do_bs_save(<<_:1, Tag:5, T/binary>>) when Tag < 31 -> + {a,Tag,T}; +do_bs_save(<<1:1, 31:5, 0:1, Tag:7, T/binary>>) -> + {b,Tag,T}; +do_bs_save(<<_:1, 31:5, 1:1, Tag:7, T/binary>>) -> + {c,Tag,T}; +do_bs_save(<<_:1, 31:5, T/binary>>) -> + {d,1024,T}. + +is_not_killed(_Config) -> + {Pid,Ref} = spawn_monitor(fun() -> exit(banan) end), + receive + {'DOWN', Ref, process, Pid, banan} -> + ok + end, + receive after 0 -> ok end. + +is_not_used_at(_Config) -> + {a,b} = do_is_not_used_at(a, [{a,b}]), + {a,b} = do_is_not_used_at(a, [x,{a,b}]), + {a,b} = do_is_not_used_at(a, [{x,y},{a,b}]), + none = do_is_not_used_at(z, [{a,b}]), + none = do_is_not_used_at(a, [x]), + none = do_is_not_used_at(a, [{x,y}]), + ok. + +do_is_not_used_at(Key, [P|Ps]) -> + if + tuple_size(P) >= 1, element(1, P) =:= Key -> + P; + true -> + do_is_not_used_at(Key, Ps) + end; +do_is_not_used_at(_Key, []) -> none. + +-record(select, {fixed=false}). + +select(_Config) -> + a = do_select(#select{}, 0, 0), + b = do_select(#select{}, 0, 1), + c = do_select(#select{fixed=true}, 0, 0), + c = do_select(#select{fixed=true}, 0, 1), + ok. + +do_select(Head, OldSize, BSize) -> + Overwrite0 = + if + OldSize =:= BSize -> same; + true -> true + end, + Overwrite = + if + Head#select.fixed =/= false -> + false; + true -> + Overwrite0 + end, + if + Overwrite =:= same -> + a; + Overwrite -> + b; + true -> + c + end. + +y_catch(_Config) -> + ok = try + do_y_catch(<<"<?xmlX">>, {state}), + failed + catch + throw:{<<"<?xmlX">>,{state}} -> + ok + end. + +do_y_catch(<<"<?xml",Rest0/binary>> = Bytes, State0) -> + {Rest1,State1} = + case do_y_catch_1(Rest0, State0) of + false -> + {Bytes,State0}; + true -> + {_XmlAttributes, R, S} = do_y_catch_2(Rest0), + {R,S} + end, + case catch id({Rest1,State1}) of + Other -> + throw(Other) + end. + +do_y_catch_1(<<_,_/binary>>, _) -> + false. + +do_y_catch_2(_) -> {a,b,c}. + + +%% The identity function. +id(I) -> I. diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index 626f89ba7a..27979647c6 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -96,9 +96,16 @@ do_beam_file(F) -> compiler_bug(Config) when is_list(Config) -> %% Check that the compiler returns an error if we try to %% assemble one of the bad '.S' files. - ?line Data = ?config(data_dir, Config), - ?line File = filename:join(Data, "stack"), - ?line error = compile:file(File, [asm,report_errors,binary,time]), + Data = ?config(data_dir, Config), + File = filename:join(Data, "compiler_bug"), + error = compile:file(File, [from_asm,report_errors,time]), + + %% Make sure that the error was reported by + %% the beam_validator module. + {error, + [{"compiler_bug", + [{beam_validator,_}]}], + []} = compile:file(File, [from_asm,return_errors,time]), ok. %% The following code is stupid but it should compile. diff --git a/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S b/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S new file mode 100644 index 0000000000..ba27bf5c47 --- /dev/null +++ b/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S @@ -0,0 +1,38 @@ +{module, compiler_bug}. %% version = 0 + +{exports, [{module_info,0},{module_info,1},{sum,2}]}. + +{attributes, []}. + +{labels, 7}. + + +{function, sum, 2, 2}. + {label,1}. + {line,[{location,"compiler_bug.erl",4}]}. + {func_info,{atom,compiler_bug},{atom,sum},2}. + {label,2}. + {line,[{location,"compiler_bug.erl",5}]}. + {gc_bif,'+',{f,0},2,[{y,0},{y,1}],{x,0}}. + return. + + +{function, module_info, 0, 4}. + {label,3}. + {line,[]}. + {func_info,{atom,compiler_bug},{atom,module_info},0}. + {label,4}. + {move,{atom,compiler_bug},{x,0}}. + {line,[]}. + {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. + + +{function, module_info, 1, 6}. + {label,5}. + {line,[]}. + {func_info,{atom,compiler_bug},{atom,module_info},1}. + {label,6}. + {move,{x,0},{x,1}}. + {move,{atom,compiler_bug},{x,0}}. + {line,[]}. + {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 128291dc67..0ff03c4295 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -30,7 +30,7 @@ other_output/1, encrypted_abstr/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, missing_testheap/1, cover/1, env/1, core/1, asm/1, - sys_pre_attributes/1]). + sys_pre_attributes/1, dialyzer/1]). -export([init/3]). @@ -47,7 +47,7 @@ all() -> other_output, encrypted_abstr, {group, bad_record_use}, strict_record, missing_testheap, cover, env, core, asm, - sys_pre_attributes]. + sys_pre_attributes, dialyzer]. groups() -> [{bad_record_use, [], @@ -854,6 +854,20 @@ sys_pre_attributes(Config) -> [report,verbose]), ok. +%% Test the dialyzer option to cover more code. +dialyzer(Config) -> + Priv = ?config(priv_dir, Config), + file:set_cwd(?config(data_dir, Config)), + Opts = [{outdir,Priv},report_errors], + M = dialyzer_test, + {ok,M} = c:c(M, [dialyzer|Opts]), + [{a,b,c}] = M:M(), + + %% Cover huge line numbers without the 'dialyzer' option. + {ok,M} = c:c(M, Opts), + [{a,b,c}] = M:M(), + ok. + %%% %%% Utilities. %%% diff --git a/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl b/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl new file mode 100644 index 0000000000..ed65ff9c43 --- /dev/null +++ b/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl @@ -0,0 +1,39 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(dialyzer_test). +-export([?MODULE/0,turtle/0,test/1,huge/1]). + +-record(turtle, {a,b,c}). +-record(tortoise, {a,b,c}). + +?MODULE() -> + [{a,b,c}]. + +turtle() -> + #turtle{a=1,b=2,c=3}. + +test(T) -> + {T#tortoise.a,T#tortoise.b}. + +-file("dialyzer_test", 100000000). + +huge(X) -> + #turtle{a=42,b=100,c=511}, + X#tortoise.a. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 75efce9d7b..dbac61765b 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -61,7 +61,9 @@ suite() -> []. -all() -> [ +all() -> + test_lib:recompile(?MODULE), + [ %% literals t_build_and_match_literals, t_update_literals, t_match_and_update_literals, diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index 00a6e900d4..fb82bf6101 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -187,12 +187,13 @@ ref_opt(Config) when is_list(Config) -> end. ref_opt_1(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])), - ?line test_lib:p_run(fun(Src) -> - do_ref_opt(Src, PrivDir) - end, Sources), + test_lib:p_run(fun(Src) -> + do_ref_opt(Src, PrivDir) + end, Sources), + cover_recv_instructions(), ok. do_ref_opt(Source, PrivDir) -> @@ -202,9 +203,9 @@ do_ref_opt(Source, PrivDir) -> {outdir,PrivDir}] ++ [from_asm || Ext =:= ".S" ]), Base = filename:rootname(filename:basename(Source), Ext), - code:purge(list_to_atom(Base)), - BeamFile = filename:join(PrivDir, Base), - code:load_abs(BeamFile), + code:purge(list_to_atom(Base)), + BeamFile = filename:join(PrivDir, Base), + code:load_abs(BeamFile), ok = Mod:Mod(), {beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile), case Base of @@ -232,6 +233,27 @@ collect_recv_opt_instrs(Code) -> end] || {function,_,_,_,Is} <- Code], lists:append(L). +cover_recv_instructions() -> + %% We want to cover the handling of recv_mark and recv_set in beam_utils. + %% Since those instructions are introduced in a late optimization pass, + %% beam_utils:live_opt() will not see them unless the compilation is + %% started from a .S file. The compile_SUITE:asm/1 test case will + %% compile all test suite files to .S and then run them through the + %% compiler again. + %% + %% Here will we will ensure that this modules contains recv_mark + %% and recv_set instructions. + Pid = spawn_link(fun() -> + receive {Parent,Ref} -> + Parent ! Ref + end + end), + Ref = make_ref(), + Pid ! {self(),Ref}, + receive + Ref -> ok + end. + export(Config) when is_list(Config) -> Ref = make_ref(), ?line self() ! {result,Ref,42}, |