LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File 1010-Generalize-optimization-of-one-armed-cases.patch of Package erlang (Project home:Ledest:erlang:20)

From 3e1d141e4d22708788af1a14401f257f4153fbae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Wed, 9 Aug 2017 14:19:18 +0200
Subject: [PATCH] Generalize optimization of "one-armed" cases

A 'case' expression will force a stack frame (essentially in the same
way as a function call), unless it is at the end of a function.

In sys_core_fold there is an optimization that can optimize one-armed
cases such as:

  case Expr of
     Pat1 ->
       DoSomething;
     Pat2 ->
       erlang:error(bad)
  end,
  MoreCode.

Because only one arm of the 'case' can succeed, the code after the
case can be move into the successful arm:

  case Expr of
     Pat1 ->
       DoSomething,
       MoreCode;
     Pat2 ->
       erlang:error(bad)
  end.

Thus, the 'case' is at the end of the function and it will no longer
need a stack frame.

However, the optimization in sys_core_fold would not be applied if
there were more than one failing clause such as in this code:

  case Expr of
     Pat1 ->
       DoSomething,
       MoreCode;
     Pat2 ->
       erlang:error(bad);
     _ ->
       erlang:error(case_clause)
  end.

Generalize the optimization to handle any number of failing
clauses at the end of the case.

Reported-by: bugs.erlang.org/browse/ERL-452
---
 lib/compiler/src/sys_core_fold.erl    | 23 ++++++++++-------------
 lib/compiler/test/core_fold_SUITE.erl | 28 ++++++++++++++++++++++++++--
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index e0cd6da06..21ad2980e 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -2422,16 +2422,10 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner,
     Outer#c_let{vars=OuterVs,arg=Arg,
 		body=Inner#c_let{vars=InnerVs,arg=OuterBody,body=InnerBody}};
 move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
-		   #c_case{arg=Cexpr0,clauses=[Ca0,Cb0|Cs]}=Case, Sub0) ->
-    %% Test if there are no more clauses than Ca0 and Cb0, or if
-    %% Cb0 is guaranteed to match.
-    TwoClauses = Cs =:= [] orelse
-	case Cb0 of
-	    #c_clause{pats=[#c_var{}],guard=#c_literal{val=true}} -> true;
-	    _ -> false
-	end,
-    case {TwoClauses,is_failing_clause(Ca0),is_failing_clause(Cb0)} of
-	{true,false,true} ->
+		   #c_case{arg=Cexpr0,clauses=[Ca0|Cs0]}=Case, Sub0) ->
+    case not is_failing_clause(Ca0) andalso
+        are_all_failing_clauses(Cs0) of
+	true ->
 	    %% let <Lvars> = case <Case-expr> of
 	    %%                  <Cpats> -> <Clause-body>;
 	    %%                  <OtherCpats> -> erlang:error(...)
@@ -2467,8 +2461,8 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
 				  body=Lbody},
 
 		    Ca = Ca0#c_clause{pats=CaPats,guard=G,body=B},
-		    Cb = clause(Cb0, Cexpr, value, Sub0),
-		    Case#c_case{arg=Cexpr,clauses=[Ca,Cb]}
+		    Cs = [clause(C, Cexpr, value, Sub0) || C <- Cs0],
+		    Case#c_case{arg=Cexpr,clauses=[Ca|Cs]}
 	    catch
 		nomatch ->
 		    %% This is not a defeat. The code will eventually
@@ -2476,7 +2470,7 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
 		    %% optimizations done in this module.
 		    impossible
 	    end;
-	{_,_,_} -> impossible
+	false -> impossible
     end;
 move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
 		   #c_seq{arg=Sarg0,body=Sbody0}=Seq, Sub0) ->
@@ -2499,6 +2493,9 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
 				      body=Lbody}};
 move_let_into_expr(_Let, _Expr, _Sub) -> impossible.
 
+are_all_failing_clauses(Cs) ->
+    all(fun is_failing_clause/1, Cs).
+
 is_failing_clause(#c_clause{body=B}) ->
     will_fail(B).
 
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 0097e28d4..262967d03 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -26,7 +26,8 @@
 	 unused_multiple_values_error/1,unused_multiple_values/1,
 	 multiple_aliases/1,redundant_boolean_clauses/1,
 	 mixed_matching_clauses/1,unnecessary_building/1,
-	 no_no_file/1,configuration/1,supplies/1]).
+	 no_no_file/1,configuration/1,supplies/1,
+         redundant_stack_frame/1]).
 
 -export([foo/0,foo/1,foo/2,foo/3]).
 
@@ -45,7 +46,8 @@ groups() ->
        unused_multiple_values_error,unused_multiple_values,
        multiple_aliases,redundant_boolean_clauses,
        mixed_matching_clauses,unnecessary_building,
-       no_no_file,configuration,supplies]}].
+       no_no_file,configuration,supplies,
+       redundant_stack_frame]}].
 
 
 init_per_suite(Config) ->
@@ -527,4 +529,26 @@ supplies(_Config) ->
 
 do_supplies(#{1 := Value}) when byte_size(Value), byte_size(kg) -> working.
 
+redundant_stack_frame(_Config) ->
+    {1,2} = do_redundant_stack_frame(#{x=>1,y=>2}),
+    {'EXIT',{{badkey,_,x},_}} = (catch do_redundant_stack_frame(#{y=>2})),
+    {'EXIT',{{badkey,_,y},_}} = (catch do_redundant_stack_frame(#{x=>1})),
+    ok.
+
+do_redundant_stack_frame(Map) ->
+    %% There should not be a stack frame for this function.
+    X = case Map of
+            #{x := X0} ->
+                X0;
+            #{} ->
+                erlang:error({badkey, Map, x})
+        end,
+    Y = case Map of
+            #{y := Y0} ->
+                Y0;
+            #{} ->
+                erlang:error({badkey, Map, y})
+        end,
+    {X, Y}.
+
 id(I) -> I.
-- 
2.14.1