Skip to content

Commit

Permalink
Merge branch 'bjorn/compiler/match-fail/OTP-9842' into maint
Browse files Browse the repository at this point in the history
* bjorn/compiler/match-fail/OTP-9842:
  Add the beam_except pass to optimize exceptions
  Eliminate the match_fail primop in v3_kernel and later passes
  • Loading branch information
bjorng committed Jan 4, 2012
2 parents c9b489b + 726f6e4 commit ca5c9b9
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 81 deletions.
1 change: 1 addition & 0 deletions lib/compiler/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ MODULES = \
beam_dead \
beam_dict \
beam_disasm \
beam_except \
beam_flatten \
beam_jump \
beam_listing \
Expand Down
149 changes: 149 additions & 0 deletions lib/compiler/src/beam_except.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

-module(beam_except).
-export([module/2]).

%%% Rewrite certain calls to erlang:error/{1,2} to specialized
%%% instructions:
%%%
%%% erlang:error({badmatch,Value}) => badmatch Value
%%% erlang:error({case_clause,Value}) => case_end Value
%%% erlang:error({try_clause,Value}) => try_case_end Value
%%% erlang:error(if_clause) => if_end
%%% erlang:error(function_clause, Args) => jump FuncInfoLabel
%%%

-import(lists, [reverse/1]).

module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
Fs = [function(F) || F <- Fs0],
{ok,{Mod,Exp,Attr,Fs,Lc}}.

function({function,Name,Arity,CLabel,Is0}) ->
try
Is = function_1(Is0),
{function,Name,Arity,CLabel,Is}
catch
Class:Error ->
Stack = erlang:get_stacktrace(),
io:fwrite("Function: ~w/~w\n", [Name,Arity]),
erlang:raise(Class, Error, Stack)
end.

-record(st,
{lbl, %func_info label
loc %location for func_info
}).

function_1(Is0) ->
case Is0 of
[{label,Lbl},{line,Loc}|_] ->
St = #st{lbl=Lbl,loc=Loc},
translate(Is0, St, []);
[{label,_}|_] ->
%% No line numbers. The source must be a .S file.
%% There is no need to do anything.
Is0
end.

translate([{call_ext,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) ->
translate_1(Ar, I, Is, St, Acc);
translate([{call_ext_only,Ar,{extfunc,erlang,error,Ar}}=I|Is], St, Acc) ->
translate_1(Ar, I, Is, St, Acc);
translate([{call_ext_last,Ar,{extfunc,erlang,error,Ar},_}=I|Is], St, Acc) ->
translate_1(Ar, I, Is, St, Acc);
translate([I|Is], St, Acc) ->
translate(Is, St, [I|Acc]);
translate([], _, Acc) ->
reverse(Acc).

translate_1(Ar, I, Is, St, [{line,_}=Line|Acc1]=Acc0) ->
case dig_out(Ar, Acc1) of
no ->
translate(Is, St, [I|Acc0]);
{yes,function_clause,Acc2} ->
case {Line,St} of
{{line,Loc},#st{lbl=Fi,loc=Loc}} ->
Instr = {jump,{f,Fi}},
translate(Is, St, [Instr|Acc2]);
{_,_} ->
%% This must be "error(function_clause, Args)" in
%% the Erlang source code. Don't translate.
translate(Is, St, [I|Acc0])
end;
{yes,Instr,Acc2} ->
translate(Is, St, [Instr,Line|Acc2])
end.

dig_out(Ar, [{kill,_}|Is]) ->
dig_out(Ar, Is);
dig_out(1, [{block,Bl0}|Is]) ->
case dig_out_block(reverse(Bl0)) of
no -> no;
{yes,What,[]} ->
{yes,What,Is};
{yes,What,Bl} ->
{yes,What,[{block,Bl}|Is]}
end;
dig_out(2, [{block,Bl}|Is]) ->
case dig_out_block_fc(Bl) of
no -> no;
{yes,What} -> {yes,What,Is}
end;
dig_out(_, _) -> no.

dig_out_block([{set,[{x,0}],[{atom,if_clause}],move}]) ->
{yes,if_end,[]};
dig_out_block([{set,[{x,0}],[{literal,{Exc,Value}}],move}|Is]) ->
translate_exception(Exc, {literal,Value}, Is, 0);
dig_out_block([{set,[{x,0}],[Tuple],move},
{set,[],[Value],put},
{set,[],[{atom,Exc}],put},
{set,[Tuple],[],{put_tuple,2}}|Is]) ->
translate_exception(Exc, Value, Is, 3);
dig_out_block([{set,[],[Value],put},
{set,[],[{atom,Exc}],put},
{set,[{x,0}],[],{put_tuple,2}}|Is]) ->
translate_exception(Exc, Value, Is, 3);
dig_out_block(_) -> no.

translate_exception(badmatch, Val, Is, Words) ->
{yes,{badmatch,Val},fix_block(Is, Words)};
translate_exception(case_clause, Val, Is, Words) ->
{yes,{case_end,Val},fix_block(Is, Words)};
translate_exception(try_clause, Val, Is, Words) ->
{yes,{try_case_end,Val},fix_block(Is, Words)};
translate_exception(_, _, _, _) -> no.

fix_block(Is, 0) ->
reverse(Is);
fix_block(Is0, Words) ->
[{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is] = reverse(Is0),
[{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is].

dig_out_block_fc([{set,[],[],{alloc,Live,_}}|Bl]) ->
dig_out_fc(Bl, Live-1, nil);
dig_out_block_fc(_) -> no.

dig_out_fc([{set,[Dst],[{x,Reg},Dst0],put_list}|Is], Reg, Dst0) ->
dig_out_fc(Is, Reg-1, Dst);
dig_out_fc([{set,[{x,0}],[{atom,function_clause}],move}], -1, {x,1}) ->
{yes,function_clause};
dig_out_fc(_, _, _) -> no.
2 changes: 2 additions & 0 deletions lib/compiler/src/compile.erl
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ asm_passes() ->
[{unless,no_postopt,
[{pass,beam_block},
{iff,dblk,{listing,"block"}},
{unless,no_except,{pass,beam_except}},
{iff,dexcept,{listing,"except"}},
{unless,no_bopt,{pass,beam_bool}},
{iff,dbool,{listing,"bool"}},
{unless,no_topt,{pass,beam_type}},
Expand Down
1 change: 1 addition & 0 deletions lib/compiler/src/compiler.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
beam_dead,
beam_dict,
beam_disasm,
beam_except,
beam_flatten,
beam_jump,
beam_listing,
Expand Down
37 changes: 0 additions & 37 deletions lib/compiler/src/v3_codegen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@

%% Main codegen structure.
-record(cg, {lcount=1, %Label counter
finfo, %Function info label
bfail, %Fail label for BIFs
break, %Break label
recv, %Receive label
Expand Down Expand Up @@ -126,7 +125,6 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) ->
stk=[]}, 0, Vdb),
{B,_Aft,St} = cg_list(Les, 0, Vdb, Bef,
St3#cg{bfail=0,
finfo=Fi,
ultimate_failure=UltimateMatchFail,
is_top_block=true}),
{Name,Arity} = NameArity,
Expand All @@ -147,8 +145,6 @@ cg({match,M,Rs}, Le, Vdb, Bef, St) ->
match_cg(M, Rs, Le, Vdb, Bef, St);
cg({guard_match,M,Rs}, Le, Vdb, Bef, St) ->
guard_match_cg(M, Rs, Le, Vdb, Bef, St);
cg({match_fail,F}, Le, Vdb, Bef, St) ->
match_fail_cg(F, Le, Vdb, Bef, St);
cg({call,Func,As,Rs}, Le, Vdb, Bef, St) ->
call_cg(Func, As, Rs, Le, Vdb, Bef, St);
cg({enter,Func,As}, Le, Vdb, Bef, St) ->
Expand Down Expand Up @@ -294,39 +290,6 @@ match_cg({block,Es}, Le, _Fail, Bef, St) ->
Int = clear_dead(Bef, Le#l.i, Le#l.vdb),
block_cg(Es, Le, Int, St).

%% match_fail_cg(FailReason, Le, Vdb, StackReg, State) ->
%% {[Ainstr],StackReg,State}.
%% Generate code for the match_fail "call". N.B. there is no generic
%% case for when the fail value has been created elsewhere.

match_fail_cg({function_clause,As}, Le, Vdb, Bef, St) ->
%% Must have the args in {x,0}, {x,1},...
{Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
{Sis ++ [{jump,{f,St#cg.finfo}}],
Int#sr{reg=clear_regs(Int#sr.reg)},St};
match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) ->
R = cg_reg_arg(Term, Bef),
Int0 = clear_dead(Bef, Le#l.i, Vdb),
{Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
{Sis ++ [line(Le),{badmatch,R}],
Int#sr{reg=clear_regs(Int0#sr.reg)},St};
match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) ->
R = cg_reg_arg(Reason, Bef),
Int0 = clear_dead(Bef, Le#l.i, Vdb),
{Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
{Sis++[line(Le),{case_end,R}],
Int#sr{reg=clear_regs(Bef#sr.reg)},St};
match_fail_cg(if_clause, Le, Vdb, Bef, St) ->
Int0 = clear_dead(Bef, Le#l.i, Vdb),
{Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
{Sis++[line(Le),if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St};
match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) ->
R = cg_reg_arg(Reason, Bef),
Int0 = clear_dead(Bef, Le#l.i, Vdb),
{Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
{Sis ++ [line(Le),{try_case_end,R}],
Int#sr{reg=clear_regs(Int0#sr.reg)},St}.

%% bsm_rename_ctx([Clause], Var) -> [Clause]
%% We know from an annotation that the register for a binary can
%% be reused for the match context because the two are not truly
Expand Down
22 changes: 13 additions & 9 deletions lib/compiler/src/v3_kernel.erl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2,
keymember/3,keyfind/3]).
-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
-import(cerl, [c_tuple/1]).

-include("core_parse.hrl").
-include("v3_kernel.hrl").
Expand Down Expand Up @@ -422,10 +423,11 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
end;
expr(#c_primop{anno=A,name=#c_literal{val=match_fail},args=Cargs0}, Sub, St0) ->
Cargs = translate_match_fail(Cargs0, Sub, A, St0),
%% This special case will disappear.
{Kargs,Ap,St} = atomic_list(Cargs, Sub, St0),
Ar = length(Cargs),
Call = #k_call{anno=A,op=#k_internal{name=match_fail,arity=Ar},args=Kargs},
Call = #k_call{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
name=#k_atom{val=error},
arity=Ar},args=Kargs},
{Call,Ap,St};
expr(#c_primop{anno=A,name=#c_literal{val=N},args=Cargs}, Sub, St0) ->
{Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
Expand Down Expand Up @@ -455,14 +457,14 @@ expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}.
translate_match_fail(Args, Sub, Anno, St) ->
case Args of
[#c_tuple{es=[#c_literal{val=function_clause}|As]}] ->
translate_match_fail_1(Anno, Args, As, Sub, St);
translate_match_fail_1(Anno, As, Sub, St);
[#c_literal{val=Tuple}] when is_tuple(Tuple) ->
%% The inliner may have created a literal out of
%% the original #c_tuple{}.
case tuple_to_list(Tuple) of
[function_clause|As0] ->
As = [#c_literal{val=E} || E <- As0],
translate_match_fail_1(Anno, Args, As, Sub, St);
translate_match_fail_1(Anno, As, Sub, St);
_ ->
Args
end;
Expand All @@ -471,7 +473,7 @@ translate_match_fail(Args, Sub, Anno, St) ->
Args
end.

translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) ->
translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
AnnoFunc = case keyfind(function_name, 1, Anno) of
false ->
none; %Force rewrite.
Expand All @@ -481,10 +483,10 @@ translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) ->
case {AnnoFunc,FF} of
{Same,Same} ->
%% Still in the correct function.
Args;
translate_fc(As);
{{F,_},F} ->
%% Still in the correct function.
Args;
translate_fc(As);
_ ->
%% Wrong function or no function_name annotation.
%%
Expand All @@ -493,9 +495,12 @@ translate_match_fail_1(Anno, Args, As, Sub, #kern{ff=FF}) ->
%% the current function). match_fail(function_clause) will
%% only work at the top level of the function it was originally
%% defined in, so we will need to rewrite it to a case_clause.
[#c_tuple{es=[#c_literal{val=case_clause},#c_tuple{es=As}]}]
[c_tuple([#c_literal{val=case_clause},c_tuple(As)])]
end.

translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].

%% call_type(Module, Function, Arity) -> call | bif | apply | error.
%% Classify the call.
call_type(#c_literal{val=M}, #c_literal{val=F}, Ar) when is_atom(M), is_atom(F) ->
Expand Down Expand Up @@ -1494,7 +1499,6 @@ iletrec_funs_gen(Fs, FreeVs, St) ->
%% is_exit_expr(Kexpr) -> boolean().
%% Test whether Kexpr always exits and never returns.

is_exit_expr(#k_call{op=#k_internal{name=match_fail,arity=1}}) -> true;
is_exit_expr(#k_receive_next{}) -> true;
is_exit_expr(_) -> false.

Expand Down
34 changes: 2 additions & 32 deletions lib/compiler/src/v3_life.erl
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,8 @@ function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) ->
end.

%% body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
%% Handle a body, need special cases for transforming match_fails.
%% We KNOW that they only occur last in a body.

body(#k_seq{arg=#k_put{anno=Pa,arg=Arg,ret=[R]},
body=#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},
args=[R]}},
I, Vdb0) ->
Vdb1 = use_vars(Pa#k.us, I, Vdb0), %All used here
{[match_fail(Arg, I, Pa#k.a ++ Ea#k.a)],I,Vdb1};
body(#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},args=[Arg]},
I, Vdb0) ->
Vdb1 = use_vars(Ea#k.us, I, Vdb0),
{[match_fail(Arg, I, Ea#k.a)],I,Vdb1};
%% Handle a body.

body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
%%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
A = get_kanno(Ke),
Expand Down Expand Up @@ -353,25 +342,6 @@ guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Ctxt, Vdb0) ->
i=I,vdb=use_vars((get_kanno(Kg))#k.us, I+2, Vdb1),
a=A#k.a}.

%% match_fail(FailValue, I, Anno) -> Expr.
%% Generate the correct match_fail instruction. N.B. there is no
%% generic case for when the fail value has been created elsewhere.

match_fail(#k_literal{anno=Anno,val={Atom,Val}}, I, A) when is_atom(Atom) ->
match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom},#k_literal{val=Val}]}, I, A);
match_fail(#k_literal{anno=Anno,val={Atom}}, I, A) when is_atom(Atom) ->
match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom}]}, I, A);
match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) ->
#l{ke={match_fail,{function_clause,literal_list(As, [])}},i=I,a=A};
match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) ->
#l{ke={match_fail,{badmatch,literal(Val, [])}},i=I,a=A};
match_fail(#k_tuple{es=[#k_atom{val=case_clause},Val]}, I, A) ->
#l{ke={match_fail,{case_clause,literal(Val, [])}},i=I,a=A};
match_fail(#k_atom{val=if_clause}, I, A) ->
#l{ke={match_fail,if_clause},i=I,a=A};
match_fail(#k_tuple{es=[#k_atom{val=try_clause},Val]}, I, A) ->
#l{ke={match_fail,{try_clause,literal(Val, [])}},i=I,a=A}.

%% type(Ktype) -> Type.

type(k_literal) -> literal;
Expand Down
2 changes: 2 additions & 0 deletions lib/compiler/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ MODULES= \
apply_SUITE \
beam_validator_SUITE \
beam_disasm_SUITE \
beam_expect_SUITE \
bs_bincomp_SUITE \
bs_bit_binaries_SUITE \
bs_construct_SUITE \
Expand Down Expand Up @@ -39,6 +40,7 @@ MODULES= \
NO_OPT= \
andor \
apply \
beam_expect \
bs_construct \
bs_match \
bs_utf \
Expand Down
Loading

0 comments on commit ca5c9b9

Please sign in to comment.