%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2000-2022. 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% %% %%% Purpose : Tests inlining. -module(inline_SUITE). -include_lib("common_test/include/ct.hrl"). -compile(export_all). -compile({inline,[badarg/2]}). %% Needed by test case `lists'. -compile(inline_list_funcs). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group,p}]. groups() -> [{p,test_lib:parallel(), [attribute,bsdecode,bsdes,barnes2,decode1,smith,fname, itracer,pseudoknot,maps_inline_test,comma_splitter,lists,really_inlined,otp_7223, coverage]}]. 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. attribute(Config) when is_list(Config) -> Name = "attribute", Src = filename:join(proplists:get_value(data_dir, Config), Name), Out = proplists:get_value(priv_dir,Config), {ok,attribute=Mod} = compile:file(Src, [{outdir,Out},report,time]), Outfile = filename:join(Out, Name++".beam"), {ok,{Mod,[{locals,Locals}]}} = beam_lib:chunks(Outfile, [locals]), io:format("locals: ~p\n", [Locals]), %% The inliner should have removed all local functions. [] = Locals, ok. -define(comp(Name), Name(Config) when is_list(Config) -> try_inline(Name, Config)). ?comp(bsdecode). ?comp(bsdes). ?comp(barnes2). ?comp(decode1). ?comp(smith). ?comp(itracer). ?comp(pseudoknot). ?comp(comma_splitter). ?comp(fname). ?comp(maps_inline_test). try_inline(Mod, 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,recv_opt_info,clint,ssalint]), ct:timetrap({minutes,10}), 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, recv_opt_info,{inline,1000},clint,ssalint]), %% Run inlined code. ct:timetrap({minutes,10}), OldInlinedResult = load_and_call(Out, Mod), %% Compare results. compare(NormalResult, OldInlinedResult), NormalResult = OldInlinedResult, %% Inlining. io:format("Compiling with new inliner: ~s\n", [Src]), {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info, inline,recv_opt_info,clint,ssalint]), %% Run inlined code. ct:timetrap({minutes,10}), InlinedResult = load_and_call(Out, Mod), %% Compare results. compare(NormalResult, InlinedResult), NormalResult = InlinedResult, %% 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; compare([Same|T1], [Same|T2]) -> compare(T1, T2); compare([{X,Y,RGB1}|T1], [{X,Y,RGB2}|T2]) -> io:format("X = ~p, Y = ~p, RGB normal = ~p, RGB inlined ~p\n", [X,Y,RGB1,RGB2]), compare(T1, T2); compare([H1|_], [H2|_]) -> io:format("Normal = ~p, Inlined = ~p\n", [H1,H2]), ct:fail(different); compare([], []) -> ok. load_and_call(Out, Module) -> io:format("Loading...\n",[]), code:purge(Module), LoadRc = code:load_abs(filename:join(Out, Module)), {module,Module} = LoadRc, io:format("Calling...\n",[]), {Time,CallResult} = timer:tc(Module, Module, []), io:format("Time: ~p\n", [Time]), CallResult. %% Macros used by lists/1 below. -define(TestHighOrder_2(Name, Body, List), begin put(?MODULE, []), (fun({Res,Res2}) -> {Res,Res2} = my_apply(lists, Name, [Body,List], []) end)(begin (fun(R) -> {R,get(?MODULE)} end)(lists:Name(Body, List)) end) end). -define(TestHighOrder_3(Name, Body, Init, List), begin put(?MODULE, []), (fun({Res,Res2}) -> {Res,Res2} = my_apply(lists, Name, [Body,Init,List], []) end)(begin (fun(R) -> {R,get(?MODULE)} end)(lists:Name(Body, Init, List)) end) end). %% For each high order function in the lists module, verify %% that the inlined version produces the same result and is evaluated %% in the same order as the function in the lists module. %% %% Note: This module must be compiled with the inline_lists_funcs option. lists(Config) when is_list(Config) -> List = lists:seq(1, 20), %% lists:map/2 ?TestHighOrder_2(map, (fun(E) -> R = E band 16#ff, put(?MODULE, [E|get(?MODULE)]), R end), List), %% lists:flatmap/2 ?TestHighOrder_2(flatmap, (fun(E) -> R = lists:duplicate(E, E), put(?MODULE, [E|get(?MODULE)]), R end), List), %% lists:foreach/2 ?TestHighOrder_2(foreach, (fun(E) -> put(?MODULE, [E bor 7|get(?MODULE)]) end), List), %% lists:filter/2 ?TestHighOrder_2(filter, (fun(E) -> put(?MODULE, [E|get(?MODULE)]), (E bsr 1) band 1 =/= 0 end), List), %% lists:any/2 ?TestHighOrder_2(any, (fun(E) -> put(?MODULE, [E|get(?MODULE)]), false %Force it to go through all. end), List), %% lists:all/2 ?TestHighOrder_2(all, (fun(E) -> put(?MODULE, [E|get(?MODULE)]), true %Force it to go through all. end), List), %% lists:foldl/3 ?TestHighOrder_3(foldl, (fun(E, A) -> put(?MODULE, [E|get(?MODULE)]), A bxor E end), 0, List), %% lists:foldr/3 ?TestHighOrder_3(foldr, (fun(E, A) -> put(?MODULE, [E|get(?MODULE)]), A bxor (bnot E) end), 0, List), %% lists:mapfoldl/3 ?TestHighOrder_3(mapfoldl, (fun(E, A) -> put(?MODULE, [E|get(?MODULE)]), {bnot E,A bxor (bnot E)} end), 0, List), %% lists:mapfoldr/3 ?TestHighOrder_3(mapfoldr, (fun(E, A) -> put(?MODULE, [E|get(?MODULE)]), {bnot E,A bxor (bnot E)} end), 0, List), %% Cleanup. erase(?MODULE), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:map(fun (X) -> X end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:flatmap(fun (X) -> X end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:foreach(fun (X) -> X end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:filter(fun (_) -> true end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:any(fun (_) -> false end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = (catch lists:all(fun (_) -> true end, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = (catch lists:foldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = (catch lists:foldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = (catch lists:mapfoldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = (catch lists:mapfoldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:map(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:flatmap(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:foreach(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:filter(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:any(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = (catch lists:all(not_a_function, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = (catch lists:foldl(not_a_function, acc, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = (catch lists:foldr(not_a_function, acc, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = (catch lists:mapfoldl(not_a_function, acc, [])), {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = (catch lists:mapfoldr(not_a_function, acc, [])), ok. my_apply(M, F, A, Init) -> put(?MODULE, Init), Res = apply(M, F, A), {Res,get(?MODULE)}. really_inlined(Config) when is_list(Config) -> %% Make sure that badarg/2 really gets inlined. {'EXIT',{badarg,[{?MODULE,fail_me_now,[],_}|_]}} = (catch fail_me_now()), ok. fail_me_now() -> badarg(foo(bar), []). foo(_X) -> badarg. %% Inlined. badarg(badarg, A) -> erlang:error(badarg, A); badarg(Reply, _A) -> Reply. otp_7223(Config) when is_list(Config) -> {'EXIT', {function_clause, [{?MODULE,_,[1],_}|_]}} = (catch otp_7223_1(1)), ok. -compile({inline,[{otp_7223_1,1}]}). otp_7223_1(X) -> otp_7223_2(X). -compile({inline,[{otp_7223_2,1}]}). otp_7223_2({a}) -> 1. coverage(Config) when is_list(Config) -> Mod = attribute, Src = filename:join(proplists:get_value(data_dir, Config), Mod), {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0}, clint,ssalint]), ok.