1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2000-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%%% Purpose : Tests inlining. 21 22-module(inline_SUITE). 23 24-include_lib("common_test/include/ct.hrl"). 25 26-compile(export_all). 27-compile({inline,[badarg/2]}). 28 29%% Needed by test case `lists'. 30-compile(inline_list_funcs). 31 32suite() -> [{ct_hooks,[ts_install_cth]}]. 33 34all() -> 35 [{group,p}]. 36 37groups() -> 38 [{p,test_lib:parallel(), 39 [attribute,bsdecode,bsdes,barnes2,decode1,smith,fname, 40 itracer,pseudoknot,maps_inline_test,comma_splitter,lists,really_inlined,otp_7223, 41 coverage]}]. 42 43init_per_suite(Config) -> 44 test_lib:recompile(?MODULE), 45 Config. 46 47end_per_suite(_Config) -> 48 ok. 49 50init_per_group(_GroupName, Config) -> 51 Config. 52 53end_per_group(_GroupName, Config) -> 54 Config. 55 56 57attribute(Config) when is_list(Config) -> 58 Name = "attribute", 59 Src = filename:join(proplists:get_value(data_dir, Config), Name), 60 Out = proplists:get_value(priv_dir,Config), 61 62 {ok,attribute=Mod} = compile:file(Src, [{outdir,Out},report,time]), 63 Outfile = filename:join(Out, Name++".beam"), 64 {ok,{Mod,[{locals,Locals}]}} = beam_lib:chunks(Outfile, [locals]), 65 io:format("locals: ~p\n", [Locals]), 66 67 %% The inliner should have removed all local functions. 68 [] = Locals, 69 70 ok. 71 72-define(comp(Name), 73 Name(Config) when is_list(Config) -> 74 try_inline(Name, Config)). 75 76?comp(bsdecode). 77?comp(bsdes). 78?comp(barnes2). 79?comp(decode1). 80?comp(smith). 81?comp(itracer). 82?comp(pseudoknot). 83?comp(comma_splitter). 84?comp(fname). 85?comp(maps_inline_test). 86 87try_inline(Mod, Config) -> 88 Src = filename:join(proplists:get_value(data_dir, Config), 89 atom_to_list(Mod)), 90 Out = proplists:get_value(priv_dir,Config), 91 92 %% Normal compilation. 93 io:format("Compiling: ~s\n", [Src]), 94 {ok,Mod} = compile:file(Src, [{outdir,Out},report, 95 bin_opt_info,clint,ssalint]), 96 97 ct:timetrap({minutes,10}), 98 NormalResult = load_and_call(Out, Mod), 99 100 %% Inlining. 101 io:format("Compiling with old inliner: ~s\n", [Src]), 102 {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info, 103 {inline,1000},clint,ssalint]), 104 105 %% Run inlined code. 106 ct:timetrap({minutes,10}), 107 OldInlinedResult = load_and_call(Out, Mod), 108 109 %% Compare results. 110 compare(NormalResult, OldInlinedResult), 111 NormalResult = OldInlinedResult, 112 113 %% Inlining. 114 io:format("Compiling with new inliner: ~s\n", [Src]), 115 {ok,Mod} = compile:file(Src, [{outdir,Out},report, 116 bin_opt_info,inline,clint,ssalint]), 117 118 %% Run inlined code. 119 ct:timetrap({minutes,10}), 120 InlinedResult = load_and_call(Out, Mod), 121 122 %% Compare results. 123 compare(NormalResult, InlinedResult), 124 NormalResult = InlinedResult, 125 126 %% Delete Beam file. 127 ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())), 128 129 %% Delete loaded module. 130 _ = code:purge(Mod), 131 _ = code:delete(Mod), 132 _ = code:purge(Mod), 133 134 ok. 135 136compare(Same, Same) -> ok; 137compare([Same|T1], [Same|T2]) -> 138 compare(T1, T2); 139compare([{X,Y,RGB1}|T1], [{X,Y,RGB2}|T2]) -> 140 io:format("X = ~p, Y = ~p, RGB normal = ~p, RGB inlined ~p\n", [X,Y,RGB1,RGB2]), 141 compare(T1, T2); 142compare([H1|_], [H2|_]) -> 143 io:format("Normal = ~p, Inlined = ~p\n", [H1,H2]), 144 ct:fail(different); 145compare([], []) -> ok. 146 147load_and_call(Out, Module) -> 148 io:format("Loading...\n",[]), 149 code:purge(Module), 150 LoadRc = code:load_abs(filename:join(Out, Module)), 151 {module,Module} = LoadRc, 152 153 io:format("Calling...\n",[]), 154 {Time,CallResult} = timer:tc(Module, Module, []), 155 io:format("Time: ~p\n", [Time]), 156 CallResult. 157 158%% Macros used by lists/1 below. 159 160-define(TestHighOrder_2(Name, Body, List), 161 begin 162 put(?MODULE, []), 163 (fun({Res,Res2}) -> 164 {Res,Res2} = my_apply(lists, Name, [Body,List], []) 165 end)(begin 166 (fun(R) -> 167 {R,get(?MODULE)} 168 end)(lists:Name(Body, List)) 169 end) 170 end). 171 172-define(TestHighOrder_3(Name, Body, Init, List), 173 begin 174 put(?MODULE, []), 175 (fun({Res,Res2}) -> 176 {Res,Res2} = my_apply(lists, Name, [Body,Init,List], []) 177 end)(begin 178 (fun(R) -> 179 {R,get(?MODULE)} 180 end)(lists:Name(Body, Init, List)) 181 end) 182 end). 183 184%% For each high order function in the lists module, verify 185%% that the inlined version produces the same result and is evaluated 186%% in the same order as the function in the lists module. 187%% 188%% Note: This module must be compiled with the inline_lists_funcs option. 189 190lists(Config) when is_list(Config) -> 191 List = lists:seq(1, 20), 192 193 %% lists:map/2 194 ?TestHighOrder_2(map, 195 (fun(E) -> 196 R = E band 16#ff, 197 put(?MODULE, [E|get(?MODULE)]), 198 R 199 end), List), 200 201 %% lists:flatmap/2 202 ?TestHighOrder_2(flatmap, 203 (fun(E) -> 204 R = lists:duplicate(E, E), 205 put(?MODULE, [E|get(?MODULE)]), 206 R 207 end), List), 208 209 %% lists:foreach/2 210 ?TestHighOrder_2(foreach, 211 (fun(E) -> 212 put(?MODULE, [E bor 7|get(?MODULE)]) 213 end), List), 214 215 %% lists:filter/2 216 ?TestHighOrder_2(filter, 217 (fun(E) -> 218 put(?MODULE, [E|get(?MODULE)]), 219 (E bsr 1) band 1 =/= 0 220 end), List), 221 222 %% lists:any/2 223 ?TestHighOrder_2(any, 224 (fun(E) -> 225 put(?MODULE, [E|get(?MODULE)]), 226 false %Force it to go through all. 227 end), List), 228 229 %% lists:all/2 230 ?TestHighOrder_2(all, 231 (fun(E) -> 232 put(?MODULE, [E|get(?MODULE)]), 233 true %Force it to go through all. 234 end), List), 235 236 %% lists:foldl/3 237 ?TestHighOrder_3(foldl, 238 (fun(E, A) -> 239 put(?MODULE, [E|get(?MODULE)]), 240 A bxor E 241 end), 0, List), 242 243 %% lists:foldr/3 244 ?TestHighOrder_3(foldr, 245 (fun(E, A) -> 246 put(?MODULE, [E|get(?MODULE)]), 247 A bxor (bnot E) 248 end), 0, List), 249 250 %% lists:mapfoldl/3 251 ?TestHighOrder_3(mapfoldl, 252 (fun(E, A) -> 253 put(?MODULE, [E|get(?MODULE)]), 254 {bnot E,A bxor (bnot E)} 255 end), 0, List), 256 257 %% lists:mapfoldr/3 258 ?TestHighOrder_3(mapfoldr, 259 (fun(E, A) -> 260 put(?MODULE, [E|get(?MODULE)]), 261 {bnot E,A bxor (bnot E)} 262 end), 0, List), 263 264 %% Cleanup. 265 erase(?MODULE), 266 267 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 268 (catch lists:map(fun (X) -> X end, not_a_list)), 269 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 270 (catch lists:flatmap(fun (X) -> X end, not_a_list)), 271 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 272 (catch lists:foreach(fun (X) -> X end, not_a_list)), 273 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 274 (catch lists:filter(fun (_) -> true end, not_a_list)), 275 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 276 (catch lists:any(fun (_) -> false end, not_a_list)), 277 {'EXIT',{function_clause,[{?MODULE,_,[_,not_a_list],_}|_]}} = 278 (catch lists:all(fun (_) -> true end, not_a_list)), 279 {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = 280 (catch lists:foldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), 281 {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = 282 (catch lists:foldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), 283 {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = 284 (catch lists:mapfoldl(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), 285 {'EXIT',{function_clause,[{?MODULE,_,[_,acc,not_a_list],_}|_]}} = 286 (catch lists:mapfoldr(fun (X, Acc) -> {X,Acc} end, acc, not_a_list)), 287 288 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 289 (catch lists:map(not_a_function, [])), 290 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 291 (catch lists:flatmap(not_a_function, [])), 292 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 293 (catch lists:foreach(not_a_function, [])), 294 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 295 (catch lists:filter(not_a_function, [])), 296 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 297 (catch lists:any(not_a_function, [])), 298 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,[]],_}|_]}} = 299 (catch lists:all(not_a_function, [])), 300 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = 301 (catch lists:foldl(not_a_function, acc, [])), 302 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = 303 (catch lists:foldr(not_a_function, acc, [])), 304 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = 305 (catch lists:mapfoldl(not_a_function, acc, [])), 306 {'EXIT',{function_clause,[{?MODULE,_,[not_a_function,acc,[]],_}|_]}} = 307 (catch lists:mapfoldr(not_a_function, acc, [])), 308 309 ok. 310 311my_apply(M, F, A, Init) -> 312 put(?MODULE, Init), 313 Res = apply(M, F, A), 314 {Res,get(?MODULE)}. 315 316really_inlined(Config) when is_list(Config) -> 317 %% Make sure that badarg/2 really gets inlined. 318 {'EXIT',{badarg,[{?MODULE,fail_me_now,[],_}|_]}} = 319 (catch fail_me_now()), 320 ok. 321 322fail_me_now() -> 323 badarg(foo(bar), []). 324 325foo(_X) -> 326 badarg. 327 328%% Inlined. 329badarg(badarg, A) -> 330 erlang:error(badarg, A); 331badarg(Reply, _A) -> 332 Reply. 333 334otp_7223(Config) when is_list(Config) -> 335 {'EXIT', {{case_clause,{1}},_}} = (catch otp_7223_1(1)), 336 ok. 337 338-compile({inline,[{otp_7223_1,1}]}). 339otp_7223_1(X) -> 340 otp_7223_2(X). 341 342-compile({inline,[{otp_7223_2,1}]}). 343otp_7223_2({a}) -> 344 1. 345 346coverage(Config) when is_list(Config) -> 347 Mod = attribute, 348 Src = filename:join(proplists:get_value(data_dir, Config), Mod), 349 {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0}, 350 clint,ssalint]), 351 ok. 352