From 282fb67730e1bafe722e7149a35e272398f88b34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Wed, 4 Apr 2018 15:14:54 +0200
Subject: Fix unsafe optimization of record test

beam_record would make an unsafe optimization for the
not_used_p/4 function added to beam_utils_SUITE in this
commit. The bug is in beam_utils, which would falsely
report that {x,4} was unused when it in fact was used.

The bug was in the function not_used/1. The purpose of
not_used/1 is to return a 'not_used' result unless the
actual result is 'used'. Unfortunately it was not
implemented in that way. It would let a 'transparent'
result slip through, which the caller in this case would
convert to 'killed' (because the register was killed on
all other paths).

Reported-by: Richard Carlsson
---
 lib/compiler/src/beam_utils.erl        |  5 ++---
 lib/compiler/test/beam_utils_SUITE.erl | 24 ++++++++++++++++++++++--
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 047cd5a569..1ddad30328 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -655,9 +655,8 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) ->
 	    {Res,St#live{res=gb_trees:insert(Lbl, Res, St#live.res)}}
     end.
 
-not_used({exit_not_used,St}) -> {not_used,St};
-not_used({killed,St}) -> {not_used,St};
-not_used({_,_}=Res) -> Res.
+not_used({used,_}=Res) -> Res;
+not_used({_,St}) -> {not_used,St}.
 
 check_liveness_ret(R, R, St) -> {used,St};
 check_liveness_ret(_, _, St) -> {killed,St}.
diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl
index 7686e69b63..b2a5cada3d 100644
--- a/lib/compiler/test/beam_utils_SUITE.erl
+++ b/lib/compiler/test/beam_utils_SUITE.erl
@@ -25,7 +25,7 @@
 	 is_not_killed/1,is_not_used_at/1,
 	 select/1,y_catch/1,otp_8949_b/1,liveopt/1,coverage/1,
          y_registers/1,user_predef/1,scan_f/1,cafu/1,
-         receive_label/1,read_size_file_version/1]).
+         receive_label/1,read_size_file_version/1,not_used/1]).
 -export([id/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -51,7 +51,8 @@ groups() ->
        user_predef,
        scan_f,
        cafu,
-       read_size_file_version
+       read_size_file_version,
+       not_used
       ]}].
 
 init_per_suite(Config) ->
@@ -507,5 +508,24 @@ do_read_size_file_version(E) ->
             {ok,MaxFiles}
     end.
 
+-record(s, { a, b }).
+-record(k, { v }).
+
+not_used(_Config) ->
+    [] = not_used_p(any, #s{b=true}, #k{}, ignored),
+    #k{v=42} = not_used_p(any, #s{b=false}, #k{v=42}, ignored),
+    #k{v=42} = not_used_p(any, #s{b=bad}, #k{v=42}, ignored),
+    ok.
+
+not_used_p(_C, S, K, L) when is_record(K, k) ->
+    if ((S#s.b) and
+         (S#s.b)) ->
+            [];
+       true ->
+            id(L),
+            id(K#k.v),
+            id(K)
+    end.
+
 %% The identity function.
 id(I) -> I.
-- 
cgit v1.2.3