diff options
author | Björn Gustavsson <[email protected]> | 2019-01-28 15:10:34 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2019-01-29 14:40:01 +0100 |
commit | 86f82183855d6b21912e0e125b096a28a52bbcf6 (patch) | |
tree | 1ea403263c64d4d4f3b5ff4a968bceaef36575e8 /lib/compiler/test | |
parent | 2f296e1a44a8abfd3f5d35aa9c111eecaac3156d (diff) | |
download | otp-86f82183855d6b21912e0e125b096a28a52bbcf6.tar.gz otp-86f82183855d6b21912e0e125b096a28a52bbcf6.tar.bz2 otp-86f82183855d6b21912e0e125b096a28a52bbcf6.zip |
Enhance optimization of function_clause exceptions
There is an optimization for reducing the number of instructions needed
to generate a `function_clause`. After the latest improvements of the
type optimization pass, that optimization is not always applied.
Here is an example:
-export([foo/3]).
foo(X, Y, Z) ->
bar(a, X, Y, Z).
bar(a, X, Y, Z) when is_tuple(X) ->
{X,Y,Z}.
Note that the compiler internally adds a clause to each function to
generate a `function_clause` exception. Thus:
bar(a, X, Y, Z) when is_tuple(X) ->
{X,Y,Z};
bar(A1, A2, A3, A4) ->
erlang:error(function_clause, [A1,A2,A3,A4]).
Optimizations will rewrite the code basically like this:
bar(_, X, Y, Z) when is_tuple(X) ->
{X,Y,Z};
bar(_, A2, A3, A4) ->
erlang:error(function_clause, [a,A2,A3,A4]).
Note the `a` as the first element of the list of arguments. It
will prevent the optimization of the `function_clause` exception.
The BEAM code for `bar/4` looks like this:
{function, bar, 4, 4}.
{label,3}.
{line,[{location,"t.erl",8}]}.
{func_info,{atom,t},{atom,bar},4}.
{label,4}.
{'%',{type_info,{x,0},{atom,a}}}.
{test,is_tuple,{f,5},[{x,1}]}.
{test_heap,4,4}.
{put_tuple2,{x,0},{list,[{x,1},{x,2},{x,3}]}}.
return.
{label,5}.
{test_heap,8,4}.
{put_list,{x,3},nil,{x,0}}.
{put_list,{x,2},{x,0},{x,0}}.
{put_list,{x,1},{x,0},{x,0}}.
{put_list,{atom,a},{x,0},{x,1}}.
{move,{atom,function_clause},{x,0}}.
{line,[{location,"t.erl",8}]}.
{call_ext,2,{extfunc,erlang,error,2}}.
The code after label 5 is the clause that generates the
`function_clause` exception.
This commit generalizes the optimization so that it can be applied for
this function:
{function, bar, 4, 4}.
{label,3}.
{line,[{location,"t.erl",8}]}.
{func_info,{atom,t},{atom,bar},4}.
{label,4}.
{'%',{type_info,{x,0},{atom,a}}}.
{test,is_tuple,{f,5},[{x,1}]}.
{test_heap,4,4}.
{put_tuple2,{x,0},{list,[{x,1},{x,2},{x,3}]}}.
return.
{label,5}.
{move,{atom,a},{x,0}}.
{jump,{f,3}}.
For this particular function, it would be safe to omit the
`move` instruction before the `{jump,{f,3}}` instruction, but
it would not be safe in general to omit `move` instructions.
Diffstat (limited to 'lib/compiler/test')
-rw-r--r-- | lib/compiler/test/beam_except_SUITE.erl | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index da61931136..49acad2bc5 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -88,8 +88,17 @@ coverage(_) -> {'EXIT',{{strange,Self},[{?MODULE,foo,[any],[File,{line,14}]}|_]}} = (catch foo(any)), + {ok,succeed,1,2} = foobar(succeed, 1, 2), + {'EXIT',{function_clause,[{?MODULE,foobar,[[fail],1,2], + [{file,"fake.erl"},{line,16}]}|_]}} = + (catch foobar([fail], 1, 2)), + {'EXIT',{function_clause,[{?MODULE,fake_function_clause,[{a,b},42.0],_}|_]}} = + (catch fake_function_clause({a,b})), + ok. +fake_function_clause(A) -> error(function_clause, [A,42.0]). + -file("fake.erl", 1). fc(a) -> %Line 2 ok; %Line 3 @@ -104,3 +113,6 @@ bar(X) -> %Line 8 %% Cover collection code for function_clause exceptions. foo(A) -> %Line 13 error({strange,self()}, [A]). %Line 14 +%% Cover beam_except:tag_literal/1. +foobar(A, B, C) when is_atom(A) -> %Line 16 + {ok,A,B,C}. %Line 17 |