diff options
Diffstat (limited to 'lib/compiler/src/v3_core.erl')
-rw-r--r-- | lib/compiler/src/v3_core.erl | 408 |
1 files changed, 312 insertions, 96 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index ff7a48e002..daf1f63585 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -81,13 +81,13 @@ -export([module/2,format_error/1]). --import(lists, [reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3, - splitwith/2,keyfind/3,sort/1,droplast/1,last/1, +-import(lists, [any/2,reverse/1,reverse/2,map/2,member/2,foldl/3,foldr/3,mapfoldl/3, + splitwith/2,keydelete/3,keyfind/3,keymember/3,sort/1,droplast/1,last/1, duplicate/2]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,intersection/2,subtract/2]). -import(cerl, [ann_c_cons/3,ann_c_tuple/2,c_tuple/1, - ann_c_map/3]). + ann_c_map/3,cons_hd/1,cons_tl/1]). -include("core_parse.hrl"). @@ -120,7 +120,8 @@ -record(itry, {anno=#a{},args,vars,body,evars,handler}). -record(ifilter, {anno=#a{},arg}). -record(igen, {anno=#a{},acc_pat,acc_guard, - skip_pat,tail,tail_pat,arg}). + skip_pat,tail,tail_pat,arg, + refill={nomatch,ignore}}). -record(isimple, {anno=#a{},term :: cerl:cerl()}). -type iapply() :: #iapply{}. @@ -160,7 +161,8 @@ opts=[] :: [compile:option()], %Options. dialyzer=false :: boolean(), %Help dialyzer or not. ws=[] :: [warning()], %Warnings. - file=[{file,""}] %File. + file=[{file,""}], %File. + load_nif=false :: boolean() %true if calls erlang:load_nif/2 }). %% XXX: The following type declarations do not belong in this module @@ -171,12 +173,16 @@ -record(imodule, {name = [], exports = ordsets:new(), - nifs = sets:new([{version, 2}]), + nifs = none :: + 'none' | sets:set(), % Is a set if the attribute is + % present in the module. attrs = [], defs = [], file = [], opts = [], - ws = []}). + ws = [], + load_nif=false :: boolean() %true if calls erlang:load_nif/2 + }). -spec module([form()], [compile:option()]) -> {'ok',cerl:c_module(),[warning()]}. @@ -186,19 +192,28 @@ module(Forms0, Opts) -> Module = foldl(fun (F, Acc) -> form(F, Acc, Opts) end, #imodule{}, Forms), - #imodule{name=Mod,exports=Exp0,attrs=As0,defs=Kfs0,ws=Ws} = Module, + #imodule{name=Mod,exports=Exp0,attrs=As0, + defs=Kfs0,ws=Ws,load_nif=LoadNif,nifs=Nifs} = Module, Exp = case member(export_all, Opts) of true -> defined_functions(Forms); false -> Exp0 end, Cexp = [#c_var{name=FA} || {_,_}=FA <- Exp], + Kfs1 = reverse(Kfs0), + Kfs = if LoadNif and (Nifs =:= none) -> + insert_nif_start(Kfs1); + true -> + Kfs1 + end, As = reverse(As0), - Kfs = reverse(Kfs0), + {ok,#c_module{name=#c_literal{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}. -form({function,_,_,_,_}=F0, #imodule{defs=Defs}=Module, Opts) -> - {F,Ws} = function(F0, Module, Opts), - Module#imodule{defs=[F|Defs],ws=Ws}; +form({function,_,_,_,_}=F0, + #imodule{defs=Defs,load_nif=LoadNif0}=Module, + Opts) -> + {F,Ws,LoadNif} = function(F0, Module, Opts), + Module#imodule{defs=[F|Defs],ws=Ws,load_nif=LoadNif or LoadNif0}; form({attribute,_,module,Mod}, Module, _Opts) -> true = is_atom(Mod), Module#imodule{name=Mod}; @@ -211,7 +226,13 @@ form({attribute,_,export,Es}, #imodule{exports=Exp0}=Module, _Opts) -> Exp = ordsets:union(ordsets:from_list(Es), Exp0), Module#imodule{exports=Exp}; form({attribute,_,nifs,Ns}, #imodule{nifs=Nifs0}=Module, _Opts) -> - Nifs = sets:union(sets:from_list(Ns, [{version,2}]), Nifs0), + Nifs1 = case Nifs0 of + none -> + sets:new([{version, 2}]); + _ -> + Nifs0 + end, + Nifs = sets:union(sets:from_list(Ns, [{version,2}]), Nifs1), Module#imodule{nifs=Nifs}; form({attribute,_,_,_}=F, #imodule{attrs=As}=Module, _Opts) -> Module#imodule{attrs=[attribute(F)|As]}; @@ -236,7 +257,8 @@ defined_functions(Forms) -> %% io:format("~w/~w " ++ Format,[Name,Arity]++Terms), %% ok. -function({function,_,Name,Arity,Cs0}, Module, Opts) -> +function({function,_,Name,Arity,Cs0}, Module, Opts) + when is_integer(Arity), 0 =< Arity, Arity =< 255 -> #imodule{file=File, ws=Ws0, nifs=Nifs} = Module, try St0 = #core{vcount=0,function={Name,Arity},opts=Opts, @@ -248,9 +270,9 @@ function({function,_,Name,Arity,Cs0}, Module, Opts) -> %% ok = function_dump(Name, Arity, "ubody:~n~p~n",[B1]), {B2,St3} = cbody(B1, Nifs, St2), %% ok = function_dump(Name, Arity, "cbody:~n~p~n",[B2]), - {B3,#core{ws=Ws}} = lbody(B2, St3), + {B3,#core{ws=Ws,load_nif=LoadNif}} = lbody(B2, St3), %% ok = function_dump(Name, Arity, "lbody:~n~p~n",[B3]), - {{#c_var{name={Name,Arity}},B3},Ws} + {{#c_var{name={Name,Arity}},B3},Ws,LoadNif} catch Class:Error:Stack -> io:fwrite("Function: ~w/~w\n", [Name,Arity]), @@ -670,6 +692,9 @@ expr({lc,L,E,Qs0}, St0) -> lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1); expr({bc,L,E,Qs}, St) -> bc_tq(L, E, Qs, St); +expr({mc,L,E,Qs0}, St0) -> + {Qs1,St1} = preprocess_quals(L, Qs0, St0), + mc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1); expr({tuple,L,Es0}, St0) -> {Es1,Eps,St1} = safe_list(Es0, St0), A = record_anno(L, St1), @@ -859,6 +884,9 @@ expr({call,L,{remote,_,M0,F0},As0}, St0) -> name=#c_literal{val=match_fail}, args=[Tuple]}, {Fail,Aps,St1}; + {#c_literal{val=erlang},#c_literal{val=load_nif},[_,_]} -> + {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1}, + Aps,St1#core{load_nif=true}}; {_,_,_} -> {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1} end; @@ -877,7 +905,7 @@ expr({match,L,P0,E0}, St0) -> {{sequential_match,_,_,_}=P1,E1} -> %% Matching of an expression to more than one pattern. Example: %% - %% #rec{f=Val} = A = Expr + %% #{Key := Value} = #{key := Key} = Expr {E2,Eps1,St2} = safe(E1, St1), St3 = St2#core{wanted=St0#core.wanted}, @@ -897,8 +925,8 @@ expr({match,L,P0,E0}, St0) -> %% %% begin %% V = Expr, - %% A = V, - %% #rec{f=Val} = V + %% #{key := Key} = V, + %% #{Key := Value} = V %% end Block = blockify(L, P1, Var), {E3,Eps3,St5} = expr({block,L,Block}, St4), @@ -951,7 +979,9 @@ expr({op,L,Op,L0,R0}, St0) -> LineAnno = full_anno(L, St1), {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} module=#c_literal{anno=LineAnno,val=erlang}, - name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}. + name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}; +expr({ssa_check_when,L,WantedResult,Args,Tag,Clauses}, St) -> + {#c_opaque{anno=full_anno(L, St),val={ssa_check_when,WantedResult,Tag,Args,Clauses}}, [], St}. blockify(L0, {sequential_match,_L1,First,Then}, E) -> [{single_match,L0,First,E}|blockify(L0, Then, E)]; @@ -1528,7 +1558,7 @@ verify_suitable_fields([]) -> ok. %% Count the number of bits approximately needed to store Int. %% (We don't need an exact result for this purpose.) -count_bits(Int) -> +count_bits(Int) when is_integer(Int) -> count_bits_1(abs(Int), 64). count_bits_1(0, Bits) -> Bits; @@ -1568,37 +1598,31 @@ fun_tq(Cs0, L, St0, NameInfo) -> {Fun,[],St4}. %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}. -%% This TQ from Simon PJ pp 127-138. +%% This TQ from Simon PJ pp 127-138. lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno, acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + refill={RefillPat,RefillAction}, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lc", St0), LA = lineno_anno(Line, St1), - LAnno = #a{anno=LA}, F = #c_var{anno=LA,name={Name,1}}, Nc = #iapply{anno=GAnno,op=F,args=[Tail]}, {[FcVar,Var],St2} = new_vars(2, St1), Fc = bad_generator([FcVar], FcVar, Arg), - SkipClause = #iclause{anno=#a{anno=[skip_clause,compiler_generated|LA]}, - pats=[SkipPat],guard=[],body=[Nc]}, - TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]}, - {Cs,St4} = case {AccPat,SkipPat} of - {nomatch,nomatch} -> - {[TailClause],St2}; - {nomatch,_} -> - {[SkipClause,TailClause],St2}; - _ -> - {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), - AccClause = #iclause{anno=LAnno,pats=[AccPat],guard=AccGuard, - body=Lps ++ [Lc]}, - {[AccClause,SkipClause,TailClause],St3} - end, + SkipClause = make_clause([skip_clause,compiler_generated|LA], + SkipPat, [], [], [Nc]), + TailClause = make_clause(LA, TailPat, [], [], [Mc]), + {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), + AccClause = make_clause(LA, AccPat, [], AccGuard, Lps ++ [Lc]), + RefillClause = make_clause(LA, RefillPat, [], [], [RefillAction,Nc]), + Cs0 = [AccClause,SkipClause,TailClause,RefillClause], + Cs = [C || C <- Cs0, C =/= nomatch], Fun = #ifun{anno=GAnno,id=[],vars=[Var],clauses=Cs,fc=Fc}, {#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},defs=[{{Name,1},Fun}], body=Pre ++ [#iapply{anno=GAnno,op=F,args=[Arg]}]}, - [],St4}; + [],St3}; lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5); lc_tq(Line, E0, [], Mc0, St0) -> @@ -1633,6 +1657,7 @@ bc_tq(Line, Exp, Qs0, St0) -> bc_tq1(Line, E, [#igen{anno=GAnno, acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + refill={RefillPat,RefillAction}, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lbc", St0), LA = lineno_anno(Line, St1), @@ -1643,30 +1668,23 @@ bc_tq1(Line, E, [#igen{anno=GAnno, F = #c_var{anno=LA,name={Name,2}}, Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]}, Fc = bad_generator(FcVars, hd(FcVars), Arg), - SkipClause = #iclause{anno=#a{anno=[compiler_generated,skip_clause|LA]}, - pats=[SkipPat,IgnoreVar],guard=[],body=[Nc]}, - TailClause = #iclause{anno=LAnno,pats=[TailPat,IgnoreVar],guard=[], - body=[AccVar]}, - {Cs,St} = case {AccPat,SkipPat} of - {nomatch,nomatch} -> - {[TailClause],St4}; - {nomatch,_} -> - {[SkipClause,TailClause],St4}; - {_,_} -> - {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4), - Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], - AccClause = #iclause{anno=LAnno,pats=[AccPat,IgnoreVar], - guard=AccGuard,body=Body}, - {[AccClause,SkipClause,TailClause],St5} - end, - Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, + SkipClause = make_clause([compiler_generated,skip_clause|LA], + SkipPat, [IgnoreVar], [], [Nc]), + TailClause = make_clause(LA, TailPat, [IgnoreVar], [], [AccVar]), + {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4), + Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], + AccClause = make_clause(LA, AccPat, [IgnoreVar], AccGuard, Body), + RefillClause = make_clause(LA, RefillPat, [AccVar], [], [RefillAction,Nc]), + Cs0 = [AccClause,SkipClause,TailClause,RefillClause], + Cs = [C || C <- Cs0, C =/= nomatch], + Fun = #ifun{anno=GAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, %% Inlining would disable the size calculation optimization for %% bs_init_writable. {#iletrec{anno=LAnno#a{anno=[list_comprehension,no_inline|LA]}, defs=[{{Name,2},Fun}], body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]}, - [],St}; + [],St5}; bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5); bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> @@ -1703,6 +1721,20 @@ bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) -> Anno = Anno0#a{anno=[compiler_generated,single_use|A]}, {set_anno(E, Anno),Pre0++Pre,St}. +mc_tq(Line, {map_field_assoc,Lf,K,V}, Qs, Mc, St0) -> + E = {tuple,Lf,[K,V]}, + {Lc,Pre0,St1} = lc_tq(Line, E, Qs, Mc, St0), + {LcVar,St2} = new_var(St1), + Pre = Pre0 ++ [#iset{var=LcVar,arg=Lc}], + Call = #icall{module=#c_literal{val=maps}, + name=#c_literal{val=from_list}, + args=[LcVar]}, + {Call,Pre,St2}. + +make_clause(_Anno, nomatch, _PatExtra, _Guard, _Body) -> + nomatch; +make_clause(Anno, Pat, PatExtra, Guard, Body) -> + #iclause{anno=#a{anno=Anno},pats=[Pat|PatExtra],guard=Guard,body=Body}. %% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) -> %% {Case,[PreExpr],State}. @@ -1779,6 +1811,7 @@ preprocess_quals(_, [], St, Acc) -> is_generator({generate,_,_,_}) -> true; is_generator({b_generate,_,_,_}) -> true; +is_generator({m_generate,_,_,_}) -> true; is_generator(_) -> false. %% Retrieve the annotation from an Erlang AST form. @@ -1787,7 +1820,7 @@ is_generator(_) -> false. get_qual_anno(Abstract) -> element(2, Abstract). %% -%% Generators are abstracted as sextuplets: +%% Generators are abstracted as a record #igen{}: %% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr. %% - acc_guard is the list of guards immediately following the current %% generator in the qualifier list input. @@ -1797,6 +1830,8 @@ get_qual_anno(Abstract) -> element(2, Abstract). %% generator input. %% - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list %% and bit string generators. +%% - refill is a pair {RefillPat,RefillAction}, used to refill the iterator +%% argument (used by map generators). %% - arg is a pair {Pre,Arg} where Pre is the list of expressions to be %% inserted before the comprehension function and Arg is the expression %% that it should be passed. @@ -1811,22 +1846,13 @@ generator(Line, {generate,Lg,P0,E}, Gs, St0) -> {Head,St1} = list_gen_pattern(P0, Line, St0), {[Tail,Skip],St2} = new_vars(2, St1), {Cg,St3} = lc_guard_tests(Gs, St2), - {AccPat,SkipPat} = case Head of - #c_var{} -> - %% If the generator pattern is a variable, the - %% pattern from the accumulator clause can be - %% reused in the skip one. lc_tq and bc_tq1 takes - %% care of dismissing the latter in that case. - Cons = ann_c_cons(LA, Head, Tail), - {Cons,Cons}; - nomatch -> - %% If it never matches, there is no need for - %% an accumulator clause. - {nomatch,ann_c_cons(LA, Skip, Tail)}; - _ -> - {ann_c_cons(LA, Head, Tail), - ann_c_cons(LA, Skip, Tail)} - end, + AccPat = case Head of + nomatch -> + nomatch; + _ -> + ann_c_cons(LA, Head, Tail) + end, + SkipPat = ann_c_cons(LA, Skip, Tail), {Ce,Pre,St4} = safe(E, St3), Gen = #igen{anno=#a{anno=GA}, acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, @@ -1859,7 +1885,92 @@ generator(Line, {b_generate,Lg,P,E}, Gs, St0) -> tail_pat=#c_var{name='_'}, arg={Pre,Ce}}, {Gen,St1} - end. + end; +generator(Line, {m_generate,Lg,{map_field_exact,_,K0,V0},E}, Gs, St0) -> + %% Consider this example: + %% + %% [{K,V} || K := V <- L]. + %% + %% The following Core Erlang code will be generated: + %% + %% letrec + %% 'lc$^0'/1 = + %% fun (Iter0) -> + %% case Iter0 of + %% <{K,V,NextIter}> when 'true' -> + %% let <Tail> = + %% apply 'lc$^0'/1(NextIter) + %% in [{K,V}|Tail] + %% <{_K,_V,NextIter}> when 'true' -> + %% %% Skip clause; will be optimized away later + %% %% since there are no filters. + %% apply 'lc$^0'/1(NextIter) + %% <'none'> when 'true' -> + %% [] + %% <Iter> when 'true' -> + %% let NextIter = + %% call 'erts_internal':'mc_refill'(Iter) + %% in apply 'lc$^0'/1(NextIter) + %% <Bad> when 'true' -> + %% %% Generated by lc_tq/5. Never reached; + %% %% will be optimized away. + %% call 'erlang':'error'({'bad_generator',Bad}) + %% end + %% in let <Iter> = + %% case call 'erts_internal':'mc_iterator'(L) of + %% <[]> when 'true' -> + %% call 'erlang':'error' + %% ({'bad_generator',L}) + %% <Iter0> when 'true' -> + %% Iter0 + %% end + %% in apply 'lc$^0'/1(Iter0) + LA = lineno_anno(Line, St0), + GA = lineno_anno(Lg, St0), + {Pat,St1} = list_gen_pattern({cons,Lg,K0,V0}, Line, St0), + {[SkipK,SkipV,IterVar,OuterIterVar,_BadGenVar],St2} = new_vars(5, St1), + {Cg,St3} = lc_guard_tests(Gs, St2), + {Ce,Pre0,St4} = safe(E, St3), + AccPat = case Pat of + nomatch -> + nomatch; + _ -> + K = cons_hd(Pat), + V = cons_tl(Pat), + #c_tuple{es=[K,V,IterVar]} + end, + SkipPat = #c_tuple{es=[SkipK,SkipV,IterVar]}, + + Refill = {SkipK, + #iset{var=IterVar, + arg=#icall{anno=#a{anno=GA}, + module=#c_literal{val=erts_internal}, + name=#c_literal{val=mc_refill}, + args=[SkipK]}}}, + + InitIter = #icall{anno=#a{anno=GA}, + module=#c_literal{val=erts_internal}, + name=#c_literal{val=mc_iterator}, + args=[Ce]}, + + BadGenerator = bad_generator([#c_literal{val=[]}], Ce, + #c_literal{val=[],anno=GA}), + BeforeFc = #iclause{anno=#a{anno=GA}, + pats=[IterVar], + guard=[], + body=[IterVar]}, + Before = #iset{var=OuterIterVar, + arg=#icase{args=[InitIter], + clauses=[BadGenerator], + fc=BeforeFc}}, + + Pre = Pre0 ++ [Before], + Gen = #igen{anno=#a{anno=GA}, + acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, + tail=IterVar,tail_pat=#c_literal{anno=LA,val=none}, + refill=Refill, + arg={Pre,OuterIterVar}}, + {Gen,St4}. append_tail_segment(Segs, St0) -> {Var,St} = new_var(St0), @@ -2043,6 +2154,22 @@ pattern({bin,L,Ps}, St0) -> {Segments,St} = pat_bin(Ps, St0), {#ibinary{anno=#a{anno=lineno_anno(L, St)},segments=Segments},St}; pattern({match,_,P1,P2}, St) -> + %% Handle aliased patterns in a clause. Example: + %% + %% f({a,b} = {A,B}) -> . . . + %% + %% The `=` operator does not have any defined order in which the + %% two patterns are matched. Therefore, this example can safely be + %% rewritten like so: + %% + %% f({a=A,b=B}) -> . . . + %% + %% Aliased patterns that are illegal, such as: + %% + %% f(#{Key := Value} = {key := Key}) -> . . . + %% + %% have already been rejected by erl_lint. + %% {Cp1,St1} = pattern(P1, St), {Cp2,St2} = pattern(P2, St1), {pat_alias(Cp1, Cp2),St2}; @@ -2187,9 +2314,22 @@ pat_alias(P1, #c_var{}=Var) -> pat_alias(P1, #c_alias{pat=P2}=Alias) -> Alias#c_alias{pat=pat_alias(P1, P2)}; +pat_alias(#ibinary{segments=[]}=P, #ibinary{segments=[]}) -> + P; +pat_alias(#ibinary{segments=[_|_]=Segs1}=P, #ibinary{segments=[S0|Segs2]}) -> + %% Handle aliases of binary patterns in a clause. Example: + %% f(<<A:8,B:8>> = <<C:16>>) -> . . . + #ibitstr{anno=#a{anno=Anno}=A} = S0, + S = S0#ibitstr{anno=A#a{anno=[sequential_match|Anno]}}, + P#ibinary{segments=Segs1++[S|Segs2]}; +pat_alias(#ibinary{segments=[S0|Segs1]}=P, #ibinary{segments=[]}) -> + %% Example: f(<<_:0>> == <>>) -> . . . + #ibitstr{anno=#a{anno=Anno}=A} = S0, + S = S0#ibitstr{anno=A#a{anno=[sequential_match|Anno]}}, + P#ibinary{segments=[S|Segs1]}; + pat_alias(P1, P2) -> - %% Aliases between binaries are not allowed, so the only - %% legal patterns that remain are data patterns. + %% The only legal patterns that remain are data patterns. case cerl:is_data(P1) andalso cerl:is_data(P2) of false -> throw(nomatch); true -> ok @@ -2227,19 +2367,19 @@ string_to_conses(Line, Cs, Tail) -> make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ]. -new_fun_name(#core{function={F,A},fcount=I}=St) -> +new_fun_name(#core{function={F,A},fcount=I}=St) when is_integer(I) -> Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A) ++ "-fun-" ++ integer_to_list(I) ++ "-", {list_to_atom(Name),St#core{fcount=I+1}}. %% new_fun_name(Type, State) -> {FunName,State}. -new_fun_name(Type, #core{fcount=C}=St) -> +new_fun_name(Type, #core{fcount=C}=St) when is_integer(C) -> {list_to_atom(Type ++ "$^" ++ integer_to_list(C)),St#core{fcount=C+1}}. %% new_var_name(State) -> {VarName,State}. -new_var_name(#core{vcount=C}=St) -> +new_var_name(#core{vcount=C}=St) when is_integer(C) -> {C,St#core{vcount=C + 1}}. %% new_var(State) -> {{var,Name},State}. @@ -2436,7 +2576,7 @@ known_bind(#known{}=K, _) -> K. %% Update the known variables to only the set of variables that %% should be known when entering the fun. -known_in_fun(#known{base=[BaseKs|_],ks=Ks0,prev_ks=[PrevKs|_]}=K) -> +known_in_fun(#known{base=[BaseKs|_],ks=Ks0,prev_ks=[PrevKs|_]}=K, Name) -> %% Within a group of bodies that see the same bindings, calculate %% the known variables for a fun. Example: %% @@ -2449,9 +2589,20 @@ known_in_fun(#known{base=[BaseKs|_],ks=Ks0,prev_ks=[PrevKs|_]}=K) -> %% %% Thus, only `A` is known when entering the fun. - Ks = union(BaseKs, subtract(Ks0, PrevKs)), + Ks1 = union(BaseKs, subtract(Ks0, PrevKs)), + Ks = case Name of + unnamed -> Ks1; + {named,FName} -> union(Ks1, [FName]) + end, K#known{base=[],ks=Ks,prev_ks=[]}; -known_in_fun(#known{}=K) -> K. +known_in_fun(#known{ks=Ks0}=K, Name) -> + case Name of + unnamed -> + K; + {named,FName} -> + Ks = union(Ks0, [FName]), + K#known{ks=Ks} + end. %%% %%% End of abstract data type for known variables. @@ -2723,7 +2874,7 @@ uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}=Fun0, Ks0, St0) {named,FName} -> known_union(Ks0, subtract([FName], Avs)) end, Ks2 = known_union(Ks1, Avs), - KnownInFun = known_in_fun(Ks2), + KnownInFun = known_in_fun(Ks2, Name), {Cs3,St3} = ufun_clauses(Cs2, KnownInFun, St2), {Fc1,St4} = ufun_clause(Fc0, KnownInFun, St3), Used = subtract(intersection(used_in_any(Cs3), known_get(Ks1)), Avs), @@ -2779,6 +2930,8 @@ uexpr(#ibinary{anno=A,segments=Ss}, _, St) -> uexpr(#c_literal{}=Lit, _, St) -> Anno = get_anno(Lit), {set_anno(Lit, #a{us=[],anno=Anno}),St}; +uexpr(#c_opaque{}=Opaque, _, St) -> + {set_anno(Opaque, #a{us=[],anno=get_anno(Opaque)}),St}; uexpr(Simple, _, St) -> true = is_simple(Simple), %Sanity check! Vs = lit_vars(Simple), @@ -2791,9 +2944,9 @@ uexpr_list(Les0, Ks, St0) -> %% upattern(Pat, [KnownVar], State) -> %% {Pat,[GuardTest],[NewVar],[UsedVar],State}. -upattern(#c_var{name='_'}, _, St0) -> +upattern(#c_var{anno=Anno,name='_'}, _, St0) -> {New,St1} = new_var_name(St0), - {#c_var{name=New},[],[New],[],St1}; + {#c_var{anno=Anno,name=New},[],[New],[],St1}; upattern(#c_var{name=V}=Var, Ks, St0) -> case is_element(V, known_get(Ks)) of true -> @@ -2996,9 +3149,10 @@ ren_pat(#ibinary{segments=Es0}=P, Ks, {Isub,Osub0}, St0) -> {Es,_Isub,Osub,St} = ren_pat_bin(Es0, Ks, Isub, Osub0, St0), {P#ibinary{segments=Es},{Isub,Osub},St}; ren_pat(P, Ks0, {_,_}=Subs0, St0) -> + Anno = cerl:get_ann(P), Es0 = cerl:data_es(P), {Es,Subs,St} = ren_pats(Es0, Ks0, Subs0, St0), - {cerl:make_data(cerl:data_type(P), Es),Subs,St}. + {cerl:ann_make_data(Anno, cerl:data_type(P), Es),Subs,St}. ren_pat_bin([#ibitstr{val=Val0,size=Sz0}=E|Es0], Ks, Isub0, Osub0, St0) -> Sz = ren_get_subst(Sz0, Isub0), @@ -3042,6 +3196,9 @@ ren_is_subst(_V, []) -> no. %% from case/receive. In subblocks/clauses the AfterVars of the block %% are just the exported variables. +cbody(B0, none, St0) -> + {B1,_,_,St1} = cexpr(B0, [], St0), + {B1,St1}; cbody(B0, Nifs, St0) -> {B1,_,_,St1} = cexpr(B0, [], St0), B2 = case sets:is_element(St1#core.function,Nifs) of @@ -3255,6 +3412,8 @@ cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St0) -> false -> {#c_call{anno=Anno,module=Mod,name=Name,args=Args},[],A#a.us,St0} end; +cexpr(O=#c_opaque{}, _As, St) -> + {O,[],[],St}; cexpr(#iprimop{anno=A,name=Name,args=Args}, _As, St) -> {#c_primop{anno=A#a.anno,name=Name,args=Args},[],A#a.us,St}; cexpr(#iprotect{anno=A,body=Es}, _As, St0) -> @@ -3356,6 +3515,7 @@ skip_lowering(#c_call{}, _A) -> skip; skip_lowering(#c_cons{}, _A) -> skip; skip_lowering(#c_literal{}, _A) -> skip; skip_lowering(#c_map{}, _A) -> skip; +skip_lowering(#c_opaque{}, _A) -> skip; skip_lowering(#c_primop{}, _A) -> skip; skip_lowering(#c_tuple{}, _A) -> skip; skip_lowering(T, A) -> {T, A}. @@ -3615,14 +3775,26 @@ split_pats([P0|Ps0], St0) -> split_pats([], _) -> none. -split_pat(#c_binary{segments=Segs0}=Bin, St0) -> +split_pat(#c_binary{anno=Anno0,segments=Segs0}=Bin, St0) -> Vars = gb_sets:empty(), case split_bin_segments(Segs0, Vars, St0, []) of none -> none; - {TailVar,Wrap,Bef,Aft,St} -> - BefBin = Bin#c_binary{segments=Bef}, - {BefBin,{split,[TailVar],Wrap,Bin#c_binary{segments=Aft},nil},St} + {size_var,TailVar,Wrap,Bef,Aft,St1} -> + {BefBin,Anno,St} = size_var_before_bin(Bin, Bef, St1), + {BefBin,{split,[TailVar],Wrap,Bin#c_binary{anno=Anno,segments=Aft},nil},St}; + {sequential_match,Bef,Aft,St1} -> + Anno = keydelete(binary_var, 1, Anno0), + {BefBin,St} = + case keyfind(binary_var, 1, Anno0) of + false -> + {BinVar,StInt} = new_var(St1), + {#c_alias{var=BinVar,pat=Bin#c_binary{segments=Bef}},StInt}; + {binary_var,BinVar} -> + {Bin#c_binary{anno=Anno,segments=Bef},St1} + end, + Wrap = fun(Body) -> Body end, + {BefBin,{split,[BinVar],Wrap,Bin#c_binary{anno=Anno,segments=Aft},nil},St} end; split_pat(#c_map{es=Es}=Map, St) -> split_map_pat(Es, Map, St, []); @@ -3642,6 +3814,28 @@ split_pat(Data, St0) -> Es = cerl:data_es(Data), split_data(Es, Type, St0, []). +size_var_before_bin(#c_binary{anno=Anno0,segments=Segments}=Bin0, Bef, St0) -> + case any(fun(#c_bitstr{anno=Anno}) -> + member(sequential_match, Anno) + end, Segments) of + true -> + case keymember(binary_var, 1, Anno0) of + false -> + {BinVar,St1} = new_var(St0), + Bin = Bin0#c_binary{segments=Bef}, + P = #c_alias{var=BinVar,pat=Bin}, + Anno = [{binary_var,BinVar}|Anno0], + {P,Anno,St1}; + true -> + Anno = keydelete(binary_var, 1, Anno0), + Bin = Bin0#c_binary{anno=Anno,segments=Bef}, + {Bin,Anno0,St0} + end; + false -> + Bin = Bin0#c_binary{segments=Bef}, + {Bin,Anno0,St0} + end. + split_map_pat([#c_map_pair{key=Key,val=Val}=E0|Es], Map0, St0, Acc) -> case eval_map_key(Key, E0, Es, Map0, St0) of none -> @@ -3704,7 +3898,19 @@ split_data([E|Es0], Type, St0, Acc) -> end; split_data([], _, _, _) -> none. -split_bin_segments([#c_bitstr{val=Val,size=Size}=S0|Segs], Vars0, St0, Acc) -> +split_bin_segments([#c_bitstr{anno=Anno0}=S0|Segs], Vars, St, Acc) -> + case member(sequential_match, Anno0) of + true -> + Anno = Anno0 -- [sequential_match], + S = S0#c_bitstr{anno=Anno}, + {sequential_match,reverse(Acc),[S|Segs],St}; + false -> + split_bin_segments_1(S0, Segs, Vars, St, Acc) + end; +split_bin_segments(_, _, _, _) -> + none. + +split_bin_segments_1(#c_bitstr{val=Val,size=Size}=S0, Segs, Vars0, St0, Acc) -> Vars = case Val of #c_var{name=V} -> gb_sets:add(V, Vars0); _ -> Vars0 @@ -3721,7 +3927,7 @@ split_bin_segments([#c_bitstr{val=Val,size=Size}=S0|Segs], Vars0, St0, Acc) -> %% in the same pattern. {TailVar,Tail,St} = split_tail_seg(S0, Segs, St0), Wrap = fun(Body) -> Body end, - {TailVar,Wrap,reverse(Acc, [Tail]),[S0|Segs],St}; + {size_var,TailVar,Wrap,reverse(Acc, [Tail]),[S0|Segs],St}; false -> split_bin_segments(Segs, Vars, St0, [S0|Acc]) end; @@ -3733,10 +3939,8 @@ split_bin_segments([#c_bitstr{val=Val,size=Size}=S0|Segs], Vars0, St0, Acc) -> {SizeVar,St2} = new_var(St1), S = S0#c_bitstr{size=SizeVar}, {Wrap,St3} = split_wrap(SizeVar, Size, St2), - {TailVar,Wrap,reverse(Acc, [Tail]),[S|Segs],St3} - end; -split_bin_segments(_, _, _, _) -> - none. + {size_var,TailVar,Wrap,reverse(Acc, [Tail]),[S|Segs],St3} + end. split_tail_seg(#c_bitstr{anno=A}=S, Segs, St0) -> {TailVar,St} = new_var(St0), @@ -3890,6 +4094,18 @@ is_simple(_) -> false. is_simple_list(Es) -> lists:all(fun is_simple/1, Es). +insert_nif_start([VF={V,F=#c_fun{body=Body}}|Funs]) -> + case Body of + #c_seq{arg=#c_primop{name=#c_literal{val=nif_start}}} -> + [VF|insert_nif_start(Funs)]; + #c_case{} -> + NifStart = #c_primop{name=#c_literal{val=nif_start},args=[]}, + [{V,F#c_fun{body=#c_seq{arg=NifStart,body=Body}}} + |insert_nif_start(Funs)] + end; +insert_nif_start([]) -> + []. + %%% %%% Handling of warnings. %%% |