1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2018-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-module(beam_ssa_SUITE).
21
22-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24         calls/1,tuple_matching/1,recv/1,maps/1,
25         cover_ssa_dead/1,combine_sw/1,share_opt/1,
26         beam_ssa_dead_crash/1,stack_init/1,
27         mapfoldl/0,mapfoldl/1,
28         grab_bag/1,coverage/1]).
29
30suite() -> [{ct_hooks,[ts_install_cth]}].
31
32all() ->
33    [mapfoldl,
34     {group,p}].
35
36groups() ->
37    [{p,test_lib:parallel(),
38      [tuple_matching,
39       calls,
40       recv,
41       maps,
42       cover_ssa_dead,
43       combine_sw,
44       share_opt,
45       beam_ssa_dead_crash,
46       stack_init,
47       grab_bag,
48       coverage
49      ]}].
50
51init_per_suite(Config) ->
52    test_lib:recompile(?MODULE),
53    Config.
54
55end_per_suite(_Config) ->
56    ok.
57
58init_per_group(_GroupName, Config) ->
59    Config.
60
61end_per_group(_GroupName, Config) ->
62    Config.
63
64calls(Config) ->
65    Ret = {return,value,Config},
66    Ret = fun_call(fun(42) -> ok end, Ret),
67    Ret = apply_fun(fun(a, b) -> ok end, [a,b], Ret),
68    Ret = apply_mfa(test_lib, id, [anything], Ret),
69    {'EXIT',{badarg,_}} = (catch call_error()),
70    {'EXIT',{badarg,_}} = (catch call_error(42)),
71    5 = start_it([erlang,length,1,2,3,4,5]),
72    ok.
73
74fun_call(Fun, X0) ->
75    X = id(X0),
76    Fun(42),
77    X.
78
79apply_fun(Fun, Args, X0) ->
80    X = id(X0),
81    apply(Fun, Args),
82    X.
83
84apply_mfa(Mod, Name, Args, X0) ->
85    X = id(X0),
86    apply(Mod, Name, Args),
87    X.
88
89call_error() ->
90    error(badarg),
91    ok.
92
93call_error(I) ->
94    <<I:(-8)>>,
95    ok.
96
97start_it([_|_]=MFA) ->
98    case MFA of
99	[M,F|Args] -> M:F(Args)
100    end.
101
102tuple_matching(_Config) ->
103    do_tuple_matching({tag,42}),
104
105    true = is_two_tuple({a,b}),
106    false = is_two_tuple({a,b,c}),
107    false = is_two_tuple(atom),
108
109    ok.
110
111do_tuple_matching(Arg) ->
112    Res = do_tuple_matching_1(Arg),
113    Res = do_tuple_matching_2(Arg),
114    Res = do_tuple_matching_3(Arg),
115    Res.
116
117do_tuple_matching_1({tag,V}) ->
118    {ok,V}.
119
120do_tuple_matching_2(Tuple) when is_tuple(Tuple) ->
121    Size = tuple_size(Tuple),
122    if
123        Size =:= 2 ->
124            {ok,element(2, Tuple)}
125    end.
126
127do_tuple_matching_3(Tuple) when is_tuple(Tuple) ->
128    Size = tuple_size(Tuple),
129    if
130        Size =:= 2 ->
131            2 = id(Size),
132            {ok,element(2, Tuple)}
133    end.
134
135is_two_tuple(Arg) ->
136    case is_tuple(Arg) of
137        false -> false;
138        true -> tuple_size(Arg) == 2
139    end.
140
141-record(reporter_state, {res,run_config}).
142-record(run_config, {report_interval=0}).
143
144recv(_Config) ->
145    Parent = self(),
146
147    %% Test sync_wait_mon/2.
148    Succ = fun() -> Parent ! {ack,self(),{result,42}} end,
149    {result,42} = sync_wait_mon(spawn_monitor(Succ), infinity),
150
151    Down = fun() -> exit(down) end,
152    {error,down} = sync_wait_mon(spawn_monitor(Down), infinity),
153
154    Exit = fun() ->
155                   Self = self(),
156                   spawn(fun() -> exit(Self, kill_me) end),
157                   receive _ -> ok end
158           end,
159    {error,kill_me} = sync_wait_mon(spawn_monitor(Exit), infinity),
160
161    Timeout = fun() -> receive _ -> ok end end,
162    {error,timeout} = sync_wait_mon(spawn_monitor(Timeout), 0),
163
164    %% Test reporter_loop/1.
165    {a,Parent} = reporter_loop(#reporter_state{res={a,Parent},
166                                               run_config=#run_config{}}),
167
168    %% Test bad_sink/0.
169    bad_sink(),
170
171    %% Test tricky_recv_1/0.
172    self() ! 1,
173    a = tricky_recv_1(),
174    self() ! 2,
175    b = tricky_recv_1(),
176
177    %% Test tricky_recv_2/0.
178    self() ! 1,
179    {1,yes} = tricky_recv_2(),
180    self() ! 2,
181    {2,maybe} = tricky_recv_2(),
182
183    %% Test 'receive after infinity' in try/catch.
184    Pid = spawn(fun recv_after_inf_in_try/0),
185    exit(Pid, done),
186
187    %% Test tricky_recv_3().
188    self() ! {{self(),r0},{1,42,"name"}},
189    {Parent,r0,[<<1:32,1:8,42:8>>,"name",0]} = tricky_recv_3(),
190    self() ! {{self(),r1},{2,99,<<"data">>}},
191    {Parent,r1,<<1:32,2:8,99:8,"data">>} = tricky_recv_3(),
192
193    %% Test tricky_recv_4().
194    self() ! {[self(),r0],{1,42,"name"}},
195    {Parent,r0,[<<1:32,1:8,42:8>>,"name",0]} = tricky_recv_4(),
196    self() ! {[self(),r1],{2,99,<<"data">>}},
197    {Parent,r1,<<1:32,2:8,99:8,"data">>} = tricky_recv_4(),
198
199    %% Test tricky_recv_5/0.
200    self() ! 1,
201    a = tricky_recv_5(),
202    self() ! 2,
203    b = tricky_recv_5(),
204
205    %% Test tricky_recv_5a/0.
206    self() ! 1,
207    a = tricky_recv_5a(),
208    self() ! 2,
209    b = tricky_recv_5a(),
210    self() ! any,
211    b = tricky_recv_5a(),
212
213    %% tricky_recv_6/0 is a compile-time error.
214    tricky_recv_6(),
215
216    recv_coverage(),
217
218    ok.
219
220sync_wait_mon({Pid, Ref}, Timeout) ->
221    receive
222	{ack,Pid,Return} ->
223	    erlang:demonitor(Ref, [flush]),
224	    Return;
225	{'DOWN',Ref,_Type,Pid,Reason} ->
226	    {error,Reason};
227	{'EXIT',Pid,Reason} ->
228	    erlang:demonitor(Ref, [flush]),
229	    {error,Reason}
230    after Timeout ->
231            erlang:demonitor(Ref, [flush]),
232            exit(Pid, kill),
233            {error,timeout}
234    end.
235
236reporter_loop(State) ->
237    RC = State#reporter_state.run_config,
238    receive after RC#run_config.report_interval ->
239                    State#reporter_state.res
240    end.
241
242bad_sink() ->
243    {ok,Pid} = my_spawn(self()),
244    %% The get_tuple_element instruction for the matching
245    %% above was sinked into the receive loop. That will
246    %% not work (and would be bad for performance if it
247    %% would work).
248    receive
249        {ok,Pid} ->
250            ok;
251        error ->
252            exit(failed)
253    end,
254    exit(Pid, kill).
255
256my_spawn(Parent) ->
257    Pid = spawn(fun() ->
258                        Parent ! {ok,self()},
259                        receive _ -> ok end
260                end),
261    {ok,Pid}.
262
263tricky_recv_1() ->
264    receive
265        X=1 ->
266            id(42),
267            a;
268        X=2 ->
269            b
270    end,
271    case X of
272        1 -> a;
273        2 -> b
274    end.
275
276tricky_recv_2() ->
277    receive
278        X=1 ->
279            Y = case id(X) of
280                    1 -> yes;
281                    _ -> no
282                end,
283            a;
284        X=2 ->
285            Y = maybe,
286            b
287    end,
288    {X,Y}.
289
290recv_after_inf_in_try() ->
291    try
292        %% Used to crash beam_kernel_to_ssa.
293        receive after infinity -> ok end
294    catch
295	_A:_B ->
296	    receive after infinity -> ok end
297    end.
298
299tricky_recv_3() ->
300    {Pid, R, Request} =
301	receive
302	    {{Pid0,R0}, {1, Proto0, Name0}} ->
303		{Pid0, R0,
304		 [<<1:32, 1:8, Proto0:8>>,Name0,0]};
305	    {{Pid1,R1}, {2, Proto1, Data1}}  ->
306		{Pid1, R1,
307		 <<1:32, 2:8, Proto1:8, Data1/binary>>}
308	end,
309    id({Pid,R,Request}).
310
311tricky_recv_4() ->
312    {Pid, R, Request} =
313	receive
314	    {[Pid0,R0], {1, Proto0, Name0}} ->
315		{Pid0, R0,
316		 [<<1:32, 1:8, Proto0:8>>,Name0,0]};
317	    {[Pid1,R1], {2, Proto1, Data1}}  ->
318		{Pid1, R1,
319		 <<1:32, 2:8, Proto1:8, Data1/binary>>}
320	end,
321    id({Pid,R,Request}).
322
323%% beam_ssa_pre_codegen would accidentally create phi nodes on critical edges
324%% when fixing up receives; the call to id/2 can either succeed or land in the
325%% catch block, and we added a phi node to its immediate successor.
326tricky_recv_5() ->
327    try
328        receive
329            X=1 ->
330                id(42),
331                a;
332            X=2 ->
333                b
334        end,
335        case X of
336            1 -> a;
337            2 -> b
338        end
339    catch
340        _:_ -> c
341    end.
342
343%% beam_ssa_pre_codegen would find the wrong exit block when fixing up
344%% receives.
345tricky_recv_5a() ->
346    try
347        receive
348            X=1 ->
349                id(42),
350                a;
351            X=_ ->
352                b
353        end,
354        %% The following is the code in the common exit block.
355        if X =:= 1 -> a;
356           true -> b
357        end
358    catch
359        %% But this code with the landingpad instruction was found,
360        %% because it happened to occur before the true exit block
361        %% in the reverse post order.
362        _:_ -> c
363    end.
364
365
366%% When fixing tricky_recv_5, we introduced a compiler crash when the common
367%% exit block was ?EXCEPTION_BLOCK and floats were in the picture.
368tricky_recv_6() ->
369    RefA = make_ref(),
370    RefB = make_ref(),
371    receive
372        {RefA, Number} -> Number + 1.0;
373        {RefB, Number} -> Number + 2.0
374    after 0 ->
375        ok
376    end.
377
378recv_coverage() ->
379    self() ! 1,
380    a = recv_coverage_1(),
381    self() ! 2,
382    b = recv_coverage_1(),
383
384    self() ! 1,
385    a = recv_coverage_2(),
386    self() ! 2,
387    b = recv_coverage_2(),
388
389    ok.
390
391%% Similar to tricky_recv_5/0, but provides test coverage for the #b_switch{}
392%% terminator.
393recv_coverage_1() ->
394    receive
395        X=1 ->
396            %% Jump to common exit block through #b_switch{list=L}
397            case id(0) of
398                0 -> a;
399                1 -> b;
400                2 -> c;
401                3 -> d
402            end;
403        X=2 ->
404            %% Jump to common exit block through #b_switch{fail=F}
405            case id(42) of
406                0 -> exit(quit);
407                1 -> exit(quit);
408                2 -> exit(quit);
409                3 -> exit(quit);
410                _ -> b
411            end
412    end,
413    case X of
414        1 -> a;
415        2 -> b
416    end.
417
418%% Similar to recv_coverage_1/0, providing test coverage for #b_br{}.
419recv_coverage_2() ->
420    receive
421        X=1 ->
422            A = id(1),
423            %% Jump to common exit block through #b_br{succ=S}.
424            if
425                A =:= 1 -> a;
426                true -> exit(quit)
427            end;
428        X=2 ->
429            A = id(2),
430            %% Jump to common exit block through #b_br{fail=F}.
431            if
432                A =:= 1 -> exit(quit);
433                true -> a
434            end
435    end,
436    case X of
437        1 -> a;
438        2 -> b
439    end.
440
441maps(_Config) ->
442    {'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)),
443
444    {jkl,nil,nil} = maps_2(#{abc => 0, jkl => 0}),
445    {def,ghi,abc} = maps_2(#{abc => 0, def => 0}),
446    {def,ghi,jkl} = maps_2(#{def => 0, jkl => 0}),
447    {mno,nil,abc} = maps_2(#{abc => 0, mno => 0, jkl => 0}),
448    {jkl,nil,nil} = maps_2(#{jkl => 0}),
449    error = maps_2(#{}),
450
451    ok.
452
453maps_1(K) ->
454    _ = id(42),
455    #{K:=V} = #{},
456    V.
457
458maps_2(Map) ->
459    Res = maps_2a(Map),
460    Res = maps_2b(Map),
461    Res.
462
463maps_2a(#{} = Map) ->
464    case case Abc = is_map_key(abc, Map) of
465             false -> false;
466             _ -> is_map_key(def, Map)
467         end of
468        true ->
469            {def, ghi, abc};
470        false ->
471            case case Jkl = is_map_key(jkl, Map) of
472                     false -> false;
473                     _ -> is_map_key(def, Map)
474                 end of
475                true ->
476                    {def, ghi, jkl};
477                false ->
478                    case case Abc of
479                             false -> false;
480                             _ -> is_map_key(mno, Map)
481                         end of
482                        true ->
483                            {mno, nil, abc};
484                        false ->
485                            case Jkl of
486                                true -> {jkl, nil, nil};
487                                false -> error
488                            end
489                    end
490            end
491    end.
492
493maps_2b(#{}=Map) ->
494    case case is_map_key(abc, Map) of
495             false -> false;
496             _ -> is_map_key(def, Map)
497         end of
498        true ->
499            {def, ghi, abc};
500        false ->
501            case case is_map_key(jkl, Map) of
502                     false -> false;
503                     _ -> is_map_key(def, Map)
504                 end of
505                true ->
506                    {def, ghi, jkl};
507                false ->
508                    case case is_map_key(abc, Map) of
509                             false -> false;
510                             _ -> is_map_key(mno, Map)
511                         end of
512                        true ->
513                            {mno, nil, abc};
514                        false ->
515                            case is_map_key(jkl, Map) of
516                                true -> {jkl, nil, nil};
517                                false -> error
518                            end
519                    end
520            end
521    end.
522
523-record(wx_ref, {type=any_type,ref=any_ref}).
524
525cover_ssa_dead(_Config) ->
526    str = format_str(str, escapable, [], true),
527    [iolist,str] = format_str(str, escapable, iolist, true),
528    bad = format_str(str, not_escapable, [], true),
529    bad = format_str(str, not_escapable, iolist, true),
530    bad = format_str(str, escapable, [], false),
531    bad = format_str(str, escapable, [], bad),
532
533    DefWxRef = #wx_ref{},
534    {DefWxRef,77,9999,[]} = contains(#wx_ref{}, 77, 9999),
535    {DefWxRef,77.0,9999,[]} = contains(#wx_ref{}, 77.0, 9999),
536    {DefWxRef,77,9999.0,[]} = contains(#wx_ref{}, 77, 9999.0),
537    {DefWxRef,77.0,9999.0,[]} = contains(#wx_ref{}, 77.0, 9999.0),
538    {any_type,any_ref,42,43,[option]} = contains(#wx_ref{}, {42,43}, [option]),
539    {any_type,any_ref,42,43,[]} = contains(#wx_ref{}, {42,43}, []),
540    {any_type,any_ref,42.0,43,[]} = contains(#wx_ref{}, {42.0,43}, []),
541    {any_type,any_ref,42,43.0,[]} = contains(#wx_ref{}, {42,43.0}, []),
542    {any_type,any_ref,42.0,43.0,[]} = contains(#wx_ref{}, {42.0,43.0}, []),
543
544    nope = conv_alub(false, '=:='),
545    ok = conv_alub(true, '=:='),
546    ok = conv_alub(true, none),
547    error = conv_alub(false, none),
548
549    {false,false} = eval_alu(false, false, false),
550    {true,false}  = eval_alu(false, false, true),
551    {false,true}  = eval_alu(false, true, false),
552    {false,false} = eval_alu(false, true, true),
553    {false,true}  = eval_alu(true, false, false),
554    {false,false} = eval_alu(true, false, true),
555    {true,true}   = eval_alu(true, true, false),
556    {false,true}  = eval_alu(true, true, true),
557
558    100.0 = percentage(1.0, 0.0),
559    100.0 = percentage(1, 0),
560    0.0 = percentage(0, 0),
561    0.0 = percentage(0.0, 0.0),
562    40.0 = percentage(4.0, 10.0),
563    60.0 = percentage(6, 10),
564
565    {'EXIT',{{badmatch,42},_}} = (catch #{key => abs(("a" = id(42)) /= teacher)}),
566
567    <<>> = id(<< V || V <- [], V andalso false >>),
568
569    false = id(([] = id([])) =/= []),
570
571    ok.
572
573format_str(Str, FormatData, IoList, EscChars) ->
574    Escapable = FormatData =:= escapable,
575    case id(Str) of
576        IoStr when Escapable, EscChars, IoList == [] ->
577            id(IoStr);
578        IoStr when Escapable, EscChars ->
579            [IoList,id(IoStr)];
580        _ ->
581            bad
582    end.
583
584contains(This, X, Y) when is_record(This, wx_ref), is_number(X), is_number(Y) ->
585    {This,X,Y,[]};
586contains(#wx_ref{type=ThisT,ref=ThisRef}, {CX,CY}, Options)
587  when is_number(CX), is_number(CY), is_list(Options) ->
588    {ThisT,ThisRef,CX,CY,Options}.
589
590conv_alub(HasDst, CmpOp) ->
591    case (not HasDst) andalso CmpOp =/= none of
592        true -> nope;
593        false ->
594            case HasDst of
595                false -> error;
596                true -> ok
597            end
598    end.
599
600eval_alu(Sign1, Sign2, N) ->
601    V = (Sign1 andalso Sign2 andalso (not N))
602        or ((not Sign1) andalso (not Sign2) andalso N),
603    C = (Sign1 andalso Sign2)
604          or ((not N) andalso (Sign1 orelse Sign2)),
605    {V,C}.
606
607percentage(Divident, Divisor) ->
608    if Divisor == 0 andalso Divident /= 0 ->
609            100.0;
610       Divisor == 0 ->
611            0.0;
612       true ->
613            Divident / Divisor * 100
614    end.
615
616combine_sw(_Config) ->
617    [a] = do_comb_sw_1(a),
618    [b,b] = do_comb_sw_1(b),
619    [c] = do_comb_sw_1(c),
620    [c] = do_comb_sw_1(c),
621    [] = do_comb_sw_1(z),
622
623    [a] = do_comb_sw_2(a),
624    [b2,b1] = do_comb_sw_2(b),
625    [c] = do_comb_sw_2(c),
626    [c] = do_comb_sw_2(c),
627    [] = do_comb_sw_2(z),
628
629    ok.
630
631do_comb_sw_1(X) ->
632    put(?MODULE, []),
633    if
634        X == a; X == b ->
635            put(?MODULE, [X|get(?MODULE)]);
636        true ->
637            ok
638    end,
639    if
640        X == b; X == c ->
641            put(?MODULE, [X|get(?MODULE)]);
642        true ->
643            ok
644    end,
645    erase(?MODULE).
646
647do_comb_sw_2(X) ->
648    put(?MODULE, []),
649    case X of
650        a ->
651            put(?MODULE, [a|get(?MODULE)]);
652        b ->
653            put(?MODULE, [b1|get(?MODULE)]);
654        _ ->
655            ok
656    end,
657    case X of
658        b ->
659            put(?MODULE, [b2|get(?MODULE)]);
660        c ->
661            put(?MODULE, [c|get(?MODULE)]);
662        _ ->
663            ok
664    end,
665    erase(?MODULE).
666
667share_opt(_Config) ->
668    ok = do_share_opt_1(0),
669    ok = do_share_opt_2(),
670    ok.
671
672do_share_opt_1(A) ->
673    %% The compiler would be stuck in an infinite loop in beam_ssa_share.
674    case A of
675        0 -> a;
676        1 -> b;
677        2 -> c
678    end,
679    receive after 1 -> ok end.
680
681do_share_opt_2() ->
682    ok = sopt_2({[pointtopoint], [{dstaddr,any}]}, ok),
683    ok = sopt_2({[broadcast], [{broadaddr,any}]}, ok),
684    ok = sopt_2({[], []}, ok),
685    ok.
686
687sopt_2({Flags, Opts}, ok) ->
688    Broadcast = lists:member(broadcast, Flags),
689    P2P = lists:member(pointtopoint, Flags),
690    case Opts of
691        %% The following two clauses would be combined to one, silently
692        %% discarding the guard test of the P2P variable.
693        [{broadaddr,_}|Os] when Broadcast ->
694            sopt_2({Flags, Os}, ok);
695        [{dstaddr,_}|Os] when P2P ->
696            sopt_2({Flags, Os}, ok);
697        [] ->
698            ok
699    end.
700
701beam_ssa_dead_crash(_Config) ->
702    not_A_B = do_beam_ssa_dead_crash(id(false), id(true)),
703    not_A_not_B = do_beam_ssa_dead_crash(false, false),
704    neither = do_beam_ssa_dead_crash(true, false),
705    neither = do_beam_ssa_dead_crash(true, true),
706    ok.
707
708do_beam_ssa_dead_crash(A, B) ->
709    %% beam_ssa_dead attempts to shortcut branches that branch other
710    %% branches. When a two-way branch is encountered, beam_ssa_dead
711    %% will simulate execution along both paths, in the hope that both
712    %% paths happens to end up in the same place.
713    %%
714    %% During the simulated execution of this function, the boolean
715    %% varible for a `br` instruction would be replaced with the
716    %% literal atom `nil`, which is not allowed, and would crash the
717    %% compiler. In practice, during the actual execution, control
718    %% would never be transferred to that `br` instruction when the
719    %% variable in question had the value `nil`.
720    %%
721    %% beam_ssa_dead has been updated to immediately abort the search
722    %% along the current path if there is an attempt to substitute a
723    %% non-boolean value into a `br` instruction.
724
725    case
726        case not A of
727            false ->
728                false;
729            true ->
730                B
731        end
732    of
733        V
734            when
735                V /= nil
736                andalso
737                V /= false ->
738            not_A_B;
739        _ ->
740            case
741                case not A of
742                    false ->
743                        false;
744                    true ->
745                        not B
746                end
747            of
748                true ->
749                    not_A_not_B;
750                false ->
751                    neither
752            end
753    end.
754
755stack_init(_Config) ->
756    6 = stack_init(a, #{a => [1,2,3]}),
757    0 = stack_init(missing, #{}),
758    ok.
759
760stack_init(Key, Map) ->
761    %% beam_ssa_codegen would wrongly assume that y(0) would always be
762    %% initialized by the `get_map_elements` instruction that follows, and
763    %% would set up the stack frame using an `allocate` instruction and
764    %% would not generate an `init` instruction to initialize y(0).
765    Res = case Map of
766              #{Key := Elements} ->
767                  %% Elements will be assigned to y(0) if the key Key exists.
768                  lists:foldl(fun(El, Acc) ->
769                                      Acc + El
770                              end, 0, Elements);
771              #{} ->
772                  %% y(0) will be left uninitialized when the key is not
773                  %% present in the map.
774                  0
775          end,
776    %% y(0) would be uninitialized here if the key was not present in the map
777    %% (if the second clause was executed).
778    id(Res).
779
780%% Test that compiler "optimizations" don't rewrite mapfold/3 to the
781%% equivalent of slow_mapfoldl/3.
782mapfoldl() ->
783    {N,Size} = mapfoldl_limits(),
784    {Time,_} = timer:tc(fun() ->
785                                mapfoldl(fun(Sz, _) ->
786                                                 erlang:garbage_collect(),
787                                                 {Sz,erlang:make_tuple(Sz, a)}
788                                         end, [], [Size])
789                        end),
790    Seconds = 15 + ceil(10 * Time * N / 1_000_000),
791    io:format("~p seconds timetrap\n", [Seconds]),
792    [{timetrap,{seconds,Seconds}}].
793
794mapfoldl(_Config) ->
795    test_mapfoldl_implementations(),
796    F = fun(Sz, _) ->
797                erlang:garbage_collect(),
798                {Sz,erlang:make_tuple(Sz, a)}
799        end,
800    {N,Size} = mapfoldl_limits(),
801    List = lists:duplicate(N, Size),
802    {List,Tuple} = mapfoldl(F, [], List),
803    {List,Tuple} = fast_mapfoldl(F, [], List),
804    Size = tuple_size(Tuple),
805    ok.
806
807mapfoldl_limits() ->
808    {1_000,100_000}.
809
810test_mapfoldl_implementations() ->
811    Seq = lists:seq(1, 10),
812    F = fun(N, Sum) -> {N,Sum+N} end,
813    {Seq,55} = mapfoldl(F, 0, Seq),
814    {Seq,55} = fast_mapfoldl(F, 0, Seq),
815    {Seq,55} = slow_mapfoldl(F, 0, Seq),
816    ok.
817
818mapfoldl(F, Acc0, [Hd|Tail]) ->
819    {R,Acc1} = F(Hd, Acc0),
820    {Rs,Acc2} = mapfoldl(F, Acc1, Tail),
821    {[R|Rs],Acc2};
822mapfoldl(F, Acc, []) when is_function(F, 2) -> {[],Acc}.
823
824%% Here is an illustration of how the compiler used to sink
825%% get_tuple_element instructions in a way that would cause all
826%% versions of the accumulator to be kept until the end. The compiler
827%% now uses a heuristic to only sink get_tuple_element instructions if
828%% that would cause fewer values to be saved in the stack frame.
829slow_mapfoldl(F, Acc0, [Hd|Tail]) ->
830    Res1 = F(Hd, Acc0),
831    %% By saving the Res1 tuple, all intermediate accumulators will be
832    %% kept to the end.
833    Res2 = slow_mapfoldl(F, element(2, Res1), Tail),
834    {[element(1, Res1)|element(1, Res2)],element(2, Res2)};
835slow_mapfoldl(F, Acc, []) when is_function(F, 2) -> {[],Acc}.
836
837%% Here is an illustration how the compiler should compile mapfoldl/3
838%% to avoid keeping all intermediate accumulators. Note that
839%% slow_mapfoldl/3 and fast_mapfoldl/3 use the same amount of stack
840%% space.
841fast_mapfoldl(F, Acc0, [Hd|Tail]) ->
842    Res1 = F(Hd, Acc0),
843    R = element(1, Res1),
844    Res2 = fast_mapfoldl(F, element(2, Res1), Tail),
845    {[R|element(1, Res2)],element(2, Res2)};
846fast_mapfoldl(F, Acc, []) when is_function(F, 2) -> {[],Acc}.
847
848grab_bag(_Config) ->
849    {'EXIT',_} = (catch grab_bag_1()),
850    {'EXIT',_} = (catch grab_bag_2()),
851    {'EXIT',_} = (catch grab_bag_3()),
852    {'EXIT',_} = (catch grab_bag_4()),
853    {'EXIT',{function_clause,[{?MODULE,grab_bag_5,[a,17],_}|_]}} =
854        (catch grab_bag_5(a, 17)),
855    way = grab_bag_6(face),
856    no_match = grab_bag_6("ABC"),
857    no_match = grab_bag_6(any),
858    ok = grab_bag_7(),
859    [] = grab_bag_8(),
860    ok = grab_bag_9(),
861    whatever = grab_bag_10(ignore, whatever),
862    other = grab_bag_11(),
863    {'EXIT',_} = (catch grab_bag_12()),
864    {'EXIT',{{badmatch,[]},_}} = (catch grab_bag_13()),
865    timeout = grab_bag_14(),
866    ?MODULE = grab_bag_15(?MODULE),
867
868    error = grab_bag_16a(timeout_value),
869    {'EXIT',{timeout_value,_}} = (catch grab_bag_16a(whatever)),
870    {'EXIT',{timeout_value,_}} = (catch grab_bag_16b(whatever)),
871    timeout_value = grab_bag_16b(error),
872
873    fact = grab_bag_17(),
874
875    ok.
876
877grab_bag_1() ->
878    %% beam_kernel_to_ssa would crash when attempting to translate a make_fun
879    %% instruction without a destination variable.
880    (catch fun () -> 15 end)(true#{}).
881
882grab_bag_2() ->
883    %% is_guard_cg_safe/1 will be called with #cg_unreachable{}, which was
884    %% not handled.
885    27
886        or
887    try
888        try
889            x#{}
890        catch
891            _:_ ->
892                []
893        end
894    after
895        false
896    end.
897
898grab_bag_3() ->
899    case
900        fun (V0)
901              when
902                  %% The only thing left after optimizations would be
903                  %% a bs_add instruction not followed by succeeded,
904                  %% which would crash beam_ssa_codegen because there
905                  %% was no failure label available.
906                  binary_part(<<>>,
907                              <<V0:V0/unit:196>>) ->
908                []
909        end
910    of
911        <<>> ->
912            []
913    end.
914
915grab_bag_4() ->
916    %% beam_kernel_to_ssa would crash because there was a #cg_phi{}
917    %% instruction that was not referenced from any #cg_break{}.
918    case $f of
919        V0 ->
920            try
921                try fy of
922                    V0 ->
923                        fu
924                catch
925                    throw:$s ->
926                        fy
927                end
928            catch
929                error:#{#{[] + [] => []} := false} when [] ->
930                    fy
931            after
932                ok
933            end
934    end.
935
936grab_bag_5(A, B) when <<business:(node(power))>> ->
937    true.
938
939grab_bag_6(face) ->
940    way;
941grab_bag_6("ABC") when (node([]))#{size(door) => $k} ->
942    false;
943grab_bag_6(_) ->
944    no_match.
945
946grab_bag_7() ->
947    catch
948        case
949            case 1.6 of
950                %% The hd([] call will be translated to erlang:error(badarg).
951                %% This case exports two variables in Core Erlang (the
952                %% return value of the case and V). beam_kernel_to_ssa was not
953                %% prepared to handle a call to error/1 which is supposed to
954                %% export two variables.
955                <<0.5:(hd([])),V:false>> ->
956                    ok
957            end
958        of
959            _ ->
960                V
961        end,
962        ok.
963
964%% ssa_opt_sink would crash if sys_core_fold had not been run.
965grab_bag_8() ->
966    try
967        []
968    catch
969        _:_ ->
970            try
971                []
972            catch
973                _:any:_ ->
974                    a
975            end;
976        _:right ->
977            b
978    end.
979
980%% The ssa_opt_try optimization would leave a succeeded:body
981%% instruction followed by a #b_ret{} terminator, which would crash
982%% beam_ssa_pre_codegen.
983grab_bag_9() ->
984    catch
985        <<1 || 99, [] <- hour>> bsr false,
986        ok.
987
988grab_bag_10(_, V) ->
989    %% This function needs a stack frame in order to preserve V.
990    fun() -> ok end,
991    V.
992
993grab_bag_11() ->
994    try 0 of
995        false -> error;
996        true -> ok;
997        _ -> other
998    catch
999        _:_ ->
1000            catched
1001    end.
1002
1003grab_bag_12() ->
1004    %% beam_ssa_pre_codegen would try to place the created map in x1.
1005    %% That would not be safe because x0 is not initialized.
1006    check_process_code(1, (#{})#{key := teacher}),
1007    ok.
1008
1009grab_bag_13() ->
1010    %% If sys_core_fold was skipped, beam_ssa_beam would leave
1011    %% unreachable code with invalid phi nodes.
1012    case <<810:true>> = [] of
1013        <<709:false>> ->
1014            ok;
1015        whatever ->
1016            case 42 of
1017                175 ->
1018                    {ok,case "b" of
1019                            $X -> time
1020                        end}
1021            end
1022    end.
1023
1024grab_bag_14() ->
1025    %% If optimizations were turned off, beam_ssa_pre_codegen would
1026    %% sanitize the binary construction instruction, replacing it with
1027    %% a call to erlang:error/1, which is not allowed in a receive.
1028    receive
1029        #{<<42:(-1)>> := _} ->
1030            ok
1031    after 0 ->
1032            timeout
1033    end.
1034
1035grab_bag_15(V) ->
1036    %% Instead of:
1037    %%
1038    %%    move x0, y0
1039    %%    move y0, x0
1040    %%
1041    %% a swap instruction would be emitted by beam_ssa_codegen:
1042    %%
1043    %%    swap x0, y0
1044    %%
1045    case [] of
1046        [] -> V
1047    end:all(),
1048    V.
1049
1050grab_bag_16a(V) ->
1051    try
1052        catch 22,
1053    receive
1054    after bad ->
1055            not_reached
1056    end
1057    catch
1058        _:V ->
1059            error
1060    end.
1061
1062grab_bag_16b(V) ->
1063    try
1064        receive
1065        after get() ->
1066                ok
1067        end
1068    catch
1069        V:Reason ->
1070            Reason
1071    end.
1072
1073grab_bag_17() ->
1074    try "xwCl" of
1075        V when V ->
1076            <<[] || V>>;
1077        [_|_] ->
1078            %% Constant propagation in beam_ssa_codegen:prefer_xregs/2
1079            %% would produce get_hd and get_tl instructions with literal
1080            %% operands.
1081            fact
1082    catch
1083        _:_ ->
1084            []
1085    end.
1086
1087
1088coverage(_Config) ->
1089
1090    %% Cover beam_ssa_codegen:force_reg/2
1091    no_match = case true of
1092                   <<_:42>> -> true;
1093                   _ -> no_match
1094              end,
1095
1096    no_match = case [] of
1097                   <<$f:1.7>> -> ok;
1098                   _ -> no_match
1099               end,
1100    {'EXIT',{{badmatch,$T},_}} = (catch coverage_1()),
1101
1102    error = coverage_2(),
1103    ok = coverage_3(),
1104
1105    ok.
1106
1107coverage_1() ->
1108    <<area/signed-bitstring>> = $T.
1109
1110coverage_2() when << []:<<0/native>> >> -> ok;
1111coverage_2() -> error.
1112
1113coverage_3() ->
1114    %% Cover a line in beam_ssa_pre_codegen:need_frame_1/2.
1115    get(),
1116    ok.
1117
1118%% The identity function.
1119id(I) -> I.
1120