From 34c8b06de52ac81e2938e93ed7a219d7611c16f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 6 Oct 2017 09:06:24 +0200
Subject: Optimize try/catch that ignores the return value

If a try/catch is used to ignore the potential exception caused by some
expression with a side effect such as:

   try
       side_effect()
   catch _Class:_Reason ->
        ok
   end,
   .
   .
   .

the generated code will be wasteful:

    try YReg TryLabel
    %% call side_effect() here
    try_end Yreg
    jump GoodLabel

  TryLabel:
    try_case YReg
    %% try_case has set up registers as follows:
    %% x(0) -> error | exit | throw
    %% x(1) -> reason
    %% x(2) -> raw stack trace data

  GoodLabel:
    %% This code does not use any x registers.

There will be two code paths that both end up at GoodLabel, and
the try_case instruction will set up registers that are never used.

We can do better by replacing try_case with try_end to obtain this
code:

    try YReg TryLabel
    %% call side_effect() here
    try_end Yreg
    jump GoodLabel

  TryLabel:
    try_end YReg

  GoodLabel:

The jump optimizer (beam_jump) can further optimize this code
like this:

    try YReg TryLabel
    %% call side_effect() here

  TryLabel:
    try_end YReg
---
 lib/compiler/src/beam_utils.erl | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

(limited to 'lib/compiler')

diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index a4c65397df..00f396c246 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -825,8 +825,14 @@ live_opt([{select,_,Src,Fail,List}=I|Is], Regs0, D, Acc) ->
     Regs1 = x_live([Src], Regs0),
     Regs = live_join_labels([Fail|List], D, Regs1),
     live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{try_case,_}=I|Is], _, D, Acc) ->
-    live_opt(Is, live_call(1), D, [I|Acc]);
+live_opt([{try_case,Y}=I|Is], Regs0, D, Acc) ->
+    Regs = live_call(1),
+    case Regs0 of
+        0 ->
+            live_opt(Is, Regs, D, [{try_end,Y}|Acc]);
+        _ ->
+            live_opt(Is, live_call(1), D, [I|Acc])
+    end;
 live_opt([{loop_rec,_Fail,_Dst}=I|Is], _, D, Acc) ->
     live_opt(Is, 0, D, [I|Acc]);
 live_opt([timeout=I|Is], _, D, Acc) ->
-- 
cgit v1.2.3


From 5fa3ac7e10f45ad433ac6a44f32f9b5e96b56fcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 6 Oct 2017 09:22:00 +0200
Subject: Do the receive optimization in the presence of try/catch

Improve the receive optimization to be able to handle code
such as this:

     Ref = make_ref(),
     try
         side_effect()
     catch _:_ ->
          ok
     end,
     receive
        %% Clauses that all match Ref.
        .
	.
	.
     end
---
 lib/compiler/src/beam_receive.erl | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'lib/compiler')

diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl
index 1403e1e05e..468460eedf 100644
--- a/lib/compiler/src/beam_receive.erl
+++ b/lib/compiler/src/beam_receive.erl
@@ -207,6 +207,8 @@ opt_update_regs({label,Lbl}, R, L) ->
 	    %% A catch label for a previously seen catch instruction is OK.
 	    {R,L}
     end;
+opt_update_regs({'try',_,{f,Lbl}}, R, L) ->
+    {R,gb_sets:add(Lbl, L)};
 opt_update_regs({try_end,_}, R, L) ->
     {R,L};
 opt_update_regs({line,_}, R, L) ->
-- 
cgit v1.2.3


From 119713b4d9ad6649aa5f1d0bf492e2f35a0ced01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 6 Oct 2017 09:25:53 +0200
Subject: Optimize a catch whose return value is ignored

Rewrite a catch expression like this:

   catch side_effect(),
   ...

to:

   try
      side_effect()
   catch
      _:_ ->
       ok
   end,
   ...

A try/catch is more efficient since no stack trace will be built
when an exception occurs.
---
 lib/compiler/src/sys_core_fold.erl | 9 +++++++++
 1 file changed, 9 insertions(+)

(limited to 'lib/compiler')

diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index f3f315935a..df880ff784 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -418,6 +418,15 @@ expr(#c_call{module=M0,name=N0}=Call0, Ctxt, Sub) ->
 expr(#c_primop{args=As0}=Prim, _, Sub) ->
     As1 = expr_list(As0, value, Sub),
     Prim#c_primop{args=As1};
+expr(#c_catch{anno=Anno,body=B}, effect, Sub) ->
+    %% When the return value of the 'catch' is ignored, we can replace it
+    %% with a try/catch to avoid building a stack trace when an exception
+    %% occurs.
+    Var = #c_var{name='catch_value'},
+    Evs = [#c_var{name='Class'},#c_var{name='Reason'},#c_var{name='Stk'}],
+    Try = #c_try{anno=Anno,arg=B,vars=[Var],body=Var,
+                 evars=Evs,handler=void()},
+    expr(Try, effect, Sub);
 expr(#c_catch{body=B0}=Catch, _, Sub) ->
     %% We can remove catch if the value is simple
     B1 = body(B0, value, Sub),
-- 
cgit v1.2.3