diff options
Diffstat (limited to 'lib/compiler/src')
| -rw-r--r-- | lib/compiler/src/beam_bsm.erl | 1 | ||||
| -rw-r--r-- | lib/compiler/src/beam_disasm.erl | 6 | ||||
| -rw-r--r-- | lib/compiler/src/beam_validator.erl | 5 | ||||
| -rw-r--r-- | lib/compiler/src/compile.erl | 7 | ||||
| -rw-r--r-- | lib/compiler/src/core_lib.erl | 4 | ||||
| -rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 143 | ||||
| -rw-r--r-- | lib/compiler/src/v3_codegen.erl | 138 | ||||
| -rw-r--r-- | lib/compiler/src/v3_core.erl | 510 | ||||
| -rw-r--r-- | lib/compiler/src/v3_kernel.erl | 105 | ||||
| -rw-r--r-- | lib/compiler/src/v3_kernel.hrl | 4 | ||||
| -rw-r--r-- | lib/compiler/src/v3_kernel_pp.erl | 17 | ||||
| -rw-r--r-- | lib/compiler/src/v3_life.erl | 14 | 
12 files changed, 513 insertions, 441 deletions
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl index fdfcb08125..d54c2a9fde 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -209,6 +209,7 @@ btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->  btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->      btb_call(Arity+2, apply, Regs, Is, D);  btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) -> +    btb_ensure_not_used([{x,Live}], I, Regs),      btb_call(Live, I, Regs, Is, D);  btb_reaches_match_2([{make_fun2,_,_,_,Live}|Is], Regs, D) ->      btb_call(Live, make_fun2, Regs, Is, D); diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index e0d0d0fd1d..57fdf95677 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -1134,7 +1134,7 @@ resolve_inst({line,[Index]},_,_,_) ->      {line,resolve_arg(Index)};  %% -%% R17A. +%% 17.0  %%  resolve_inst({put_map_assoc,Args},_,_,_) ->      [FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args, @@ -1150,6 +1150,10 @@ resolve_inst({is_map,Args0},_,_,_) ->      [FLbl|Args] = resolve_args(Args0),      {test, is_map, FLbl, Args}; +resolve_inst({has_map_field,Args0},_,_,_) -> +    [FLbl|Args] = resolve_args(Args0), +    {test,has_map_field,FLbl,Args}; +  resolve_inst({get_map_element,Args},_,_,_) ->      [FLbl,Src,Key,Dst] = resolve_args(Args),      {get_map_element,FLbl,Src,Key,Dst}; diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 97f84da08f..2486d486ed 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -574,6 +574,7 @@ valfun_4({apply,Live}, Vst) ->  valfun_4({apply_last,Live,_}, Vst) ->      tail_call(apply, Live+2, Vst);  valfun_4({call_fun,Live}, Vst) -> +    validate_src([{x,Live}], Vst),      call('fun', Live+1, Vst);  valfun_4({call,Live,Func}, Vst) ->      call(Func, Live, Vst); @@ -881,7 +882,7 @@ valfun_4(_, _) ->  verify_put_map(Fail, Src, Dst, Live, List, Vst0) ->      verify_live(Live, Vst0),      verify_y_init(Vst0), -    [assert_term(Term, Vst0) || Term <- List], +    foreach(fun (Term) -> assert_term(Term, Vst0) end, List),      assert_term(Src, Vst0),      Vst1 = heap_alloc(0, Vst0),      Vst2 = branch_state(Fail, Vst1), @@ -908,7 +909,7 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->      branch_state(Fail, Vst).  %% -%% Special state handling for setelement/3 and the set_tuple_element/3 instruction. +%% Special state handling for setelement/3 and set_tuple_element/3 instructions.  %% A possibility for garbage collection must not occur between setelement/3 and  %% set_tuple_element/3.  %% diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 0bb4de6f17..e79fe41f9b 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -623,9 +623,11 @@ core_passes() ->         [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},  	{iff,doldinline,{listing,"oldinline"}},  	?pass(core_fold_module), +	{iff,dcorefold,{listing,"corefold"}},  	{core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},  	{iff,dinline,{listing,"inline"}}, -	{core_fold_after_inlining,fun test_core_inliner/1,fun core_fold_module_after_inlining/1}, +        {core_fold_after_inlining,fun test_any_inliner/1, +         fun core_fold_module_after_inlining/1},  	?pass(core_transforms)]},         {iff,dcopt,{listing,"copt"}},         {iff,'to_core',{done,"core"}}]} @@ -1171,6 +1173,9 @@ test_core_inliner(#compile{options=Opts}) ->  		end, Opts)      end. +test_any_inliner(St) -> +    test_old_inliner(St) orelse test_core_inliner(St). +  core_old_inliner(#compile{code=Code0,options=Opts}=St) ->      {ok,Code} = sys_core_inline:module(Code0, Opts),      {ok,St#compile{code=Code}}. diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index f506901099..ed181e3baa 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -105,8 +105,8 @@ vu_expr(V, #c_cons{hd=H,tl=T}) ->      vu_expr(V, H) orelse vu_expr(V, T);  vu_expr(V, #c_tuple{es=Es}) ->      vu_expr_list(V, Es); -vu_expr(V, #c_map{es=Es}) -> -    vu_expr_list(V, Es); +vu_expr(V, #c_map{var=M,es=Es}) -> +    vu_expr(V, M) orelse vu_expr_list(V, Es);  vu_expr(V, #c_map_pair{key=Key,val=Val}) ->      vu_expr_list(V, [Key,Val]);  vu_expr(V, #c_binary{segments=Ss}) -> diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 1cdbac5693..e2b9213891 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -305,6 +305,10 @@ expr(#c_let{}=Let, Ctxt, Sub) ->  	    %% Now recursively re-process the new expression.  	    expr(Expr, Ctxt, sub_new_preserve_types(Sub))      end; +expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) -> +    %% This is named fun in an 'effect' context. Warn and ignore. +    add_warning(Letrec, useless_building), +    void();  expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) ->      Fs1 = map(fun ({Name,Fb}) ->  		      {Name,expr(Fb, {letrec,Ctxt}, Sub)} @@ -349,7 +353,12 @@ expr(#c_case{}=Case0, Ctxt, Sub) ->  	    Case = Case1#c_case{arg=Arg2,clauses=Cs2},  	    warn_no_clause_match(Case1, Case),  	    Expr = eval_case(Case, Sub), -	    bsm_an(Expr); +            case move_case_into_arg(Case, Sub) of +                impossible -> +                    bsm_an(Expr); +                Other -> +                    expr(Other, Ctxt, sub_new_preserve_types(Sub)) +            end;  	Other ->  	    expr(Other, Ctxt, Sub)      end; @@ -598,6 +607,14 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz},  	error:_ ->  	    throw(impossible)      end; +eval_binary_1([#c_bitstr{val=#c_literal{},size=#c_literal{}, +			 unit=#c_literal{},type=#c_literal{}, +			 flags=#c_cons{}=Flags}=Bitstr|Ss], Acc0) -> +    case cerl:fold_literal(Flags) of +	#c_literal{} = Flags1 -> +	    eval_binary_1([Bitstr#c_bitstr{flags=Flags1}|Ss], Acc0); +	_ -> throw(impossible) +    end;  eval_binary_1([], Acc) -> Acc;  eval_binary_1(_, _) -> throw(impossible). @@ -1536,9 +1553,17 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->      {Ps,{_,Osub}} = mapfoldl(fun map_pair_pattern/2, {Isub,Osub0}, Ps0),      {Ps,Osub}. -map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair, {Isub,Osub0}) -> -    {K,Osub1} = pattern(K0, Isub, Osub0), -    {V,Osub} = pattern(V0, Isub, Osub1), +map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) -> +    {K,Osub1} = case cerl:type(K0) of +	binary -> +	    K1 = eval_binary(K0), +	    case cerl:type(K1) of +		literal -> {K1,Osub0}; +		_ -> pattern(K0,Isub,Osub0) +	    end; +	_ -> pattern(K0,Isub,Osub0) +    end, +    {V,Osub} = pattern(V0,Isub,Osub1),      {Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.  bin_pattern_list(Ps0, Isub, Osub0) -> @@ -1920,14 +1945,45 @@ opt_bool_case_guard(Arg, [#c_clause{pats=[#c_literal{val=false}]}=Fc,Tc]) ->  %%  last clause is guaranteed to match so if there is only one clause  %%  with a pattern containing only variables then rewrite to a let. -eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0,body=B}]}, Sub) -> +eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0, +					   guard=#c_literal{val=true}, +					   body=B}]}=Case, Sub) ->      Es = case cerl:is_c_values(E) of  	     true -> cerl:values_es(E);  	     false -> [E]  	 end, -    {true,Bs} = cerl_clauses:match_list(Ps0, Es), -    {Ps,As} = unzip(Bs), -    expr(#c_let{vars=Ps,arg=core_lib:make_values(As),body=B}, sub_new(Sub)); +    %% Consider: +    %% +    %%   case SomeSideEffect() of +    %%      X=Y -> ... +    %%   end +    %% +    %% We must not rewrite it to: +    %% +    %%   let <X,Y> = <SomeSideEffect(),SomeSideEffect()> in ... +    %% +    %% because SomeSideEffect() would be evaluated twice. +    %% +    %% Instead we must evaluate the case expression in an outer let +    %% like this: +    %% +    %%   let NewVar = SomeSideEffect() in +    %%       let <X,Y> = <NewVar,NewVar> in ... +    %% +    Vs = make_vars([], length(Es)), +    case cerl_clauses:match_list(Ps0, Vs) of +	{false,_} -> +	    %% This can only happen if the Core Erlang code is +	    %% handwritten or generated by another code generator +	    %% than v3_core. Assuming that the Core Erlang program +	    %% is correct, the clause will always match at run-time. +	    Case; +	{true,Bs} -> +	    {Ps,As} = unzip(Bs), +	    InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B), +	    Let = cerl:c_let(Vs, E, InnerLet), +	    expr(Let, sub_new(Sub)) +    end;  eval_case(Case, _) -> Case.  %% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}. @@ -2555,6 +2611,77 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->  	      value, Sub)      end. +move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, +                                      body=InnerArg0}=Outer, +                           clauses=InnerClauses}=Inner, Sub) -> +    %% +    %% case let <OuterVars> = <OuterArg> in <InnerArg> of +    %%     <InnerClauses> +    %% end +    %% +    %%       ==> +    %% +    %% let <OuterVars> = <OuterArg> +    %% in case <InnerArg> of <InnerClauses> end +    %% +    ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), +    {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0), +    InnerArg = body(InnerArg0, ScopeSub), +    Outer#c_let{vars=OuterVars,arg=OuterArg, +                body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}}; +move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg, +                                       clauses=[OuterCa0,OuterCb]}=Outer, +                           clauses=InnerClauses}=Inner0, Sub) -> +    case is_failing_clause(OuterCb) of +        true -> +            #c_clause{pats=OuterPats0,guard=OuterGuard0, +                      body=InnerArg0} = OuterCa0, +            %% +            %% case case <OuterArg> of +            %%          <OuterPats> when <OuterGuard> -> <InnerArg> +            %%          <OuterCb> +            %%          ... +            %%      end of +            %%     <InnerClauses> +            %% end +            %% +            %%       ==> +            %% +            %% case <OuterArg> of +            %%     <OuterPats> when <OuterGuard> -> +            %%         case <InnerArg> of <InnerClauses> end +            %%     <OuterCb> +            %% end +            %% +            ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), +            {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0), +            OuterGuard = guard(OuterGuard0, ScopeSub), +            InnerArg = body(InnerArg0, ScopeSub), +            Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses}, +            OuterCa = OuterCa0#c_clause{pats=OuterPats,guard=OuterGuard, +                                        body=Inner}, +            Outer#c_case{arg=OuterArg, +                         clauses=[OuterCa,OuterCb]}; +        false -> +            impossible +    end; +move_case_into_arg(#c_case{arg=#c_seq{arg=OuterArg,body=InnerArg}=Outer, +                           clauses=InnerClauses}=Inner, _Sub) -> +    %% +    %% case do <OuterArg> <InnerArg> of +    %%     <InnerClauses> +    %% end +    %% +    %%       ==> +    %% +    %% do <OuterArg> +    %%    case <InnerArg> of <InerClauses> end +    %% +    Outer#c_seq{arg=OuterArg, +                body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}}; +move_case_into_arg(_, _) -> +    impossible. +  %% In guards only, rewrite a case in a let argument like  %%  %%    let <Var> = case <> of diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index c8735a76e8..4d155c0fd0 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -459,7 +459,7 @@ basic_block([Le|Les], Acc) ->  %% sets that may garbage collect are not allowed in basic blocks.  collect_block({set,_,{binary,_}})    -> no_block; -collect_block({set,_,{map,_,_}})     -> no_block; +collect_block({set,_,{map,_,_,_}})   -> no_block;  collect_block({set,_,_})             -> include;  collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};  collect_block({call,Func,As,_Rs})   -> {block_end,As++func_vars(Func)}; @@ -928,7 +928,7 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->  select_map(Scs, V, Tf, Vf, Bef, St0) ->      Reg = fetch_var(V, Bef),      {Is,Aft,St1} = -	match_fmf(fun(#l{ke={val_clause,{map,Es},B},i=I,vdb=Vdb}, Fail, St1) -> +	match_fmf(fun(#l{ke={val_clause,{map,_,Es},B},i=I,vdb=Vdb}, Fail, St1) ->  			  select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1)  		  end, Vf, St0, Scs),      {[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}. @@ -1488,55 +1488,35 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,      %% Now generate the complete code for constructing the binary.      Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),      {Sis++Code,Aft,St}; -set_cg([{var,R}], {map,SrcMap,Es0}, Le, Vdb, Bef, +set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,         #cg{in_catch=InCatch,bfail=Bfail}=St) -> +      Fail = {f,Bfail},      {Sis,Int0} =  	case InCatch of  	    true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);  	    false -> {[],Bef}  	end, +    SrcReg = cg_reg_arg(Map,Int0),      Line = line(Le#l.a), -    SrcReg = case SrcMap of -		 {var,SrcVar} -> fetch_var(SrcVar, Int0); -		 _ -> SrcMap -	     end, -    {Assoc,Exact} = -	try -	    cg_map_pairs(Es0) -	catch -	    throw:badarg -> -		{[],[{{float,0.0},{atom,badarg}}, -		     {{integer,0},{atom,badarg}}]} -	end, -    F = fun ({K,{var,V}}) -> [K,fetch_var(V, Int0)]; -	    ({K,E}) -> [K,E] -	end, -    AssocList = flatmap(F, Assoc), -    ExactList = flatmap(F, Exact), -    Live0 = max_reg(Bef#sr.reg), -    Int1 = clear_dead(Int0, Le#l.i, Vdb), -    Aft = Bef#sr{reg=put_reg(R, Int1#sr.reg)}, -    Target = fetch_reg(R, Aft#sr.reg), -    Code = [Line] ++ -	case {AssocList,ExactList} of -	    {[_|_],[]} -> -		[{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}]; -	    {[_|_],[_|_]} -> -		Live = case Target of -			   {x,TargetX} when TargetX =:= Live0 -> -			       Live0 + 1; -			   _ -> -			       Live0 -		       end, -		[{put_map_assoc,Fail,SrcReg,Target,Live0,{list,AssocList}}, -		 {put_map_exact,Fail,Target,Target,Live,{list,ExactList}}]; -	    {[],[_|_]} -> -		[{put_map_exact,Fail,SrcReg,Target,Live0,{list,ExactList}}]; -	    {[],[]} -> -		[{put_map_assoc,Fail,SrcReg,Target,Live0,{list,[]}}] -	end, -    {Sis++Code,Aft,St}; + +    %% The instruction needs to store keys in term sorted order +    %% All keys has to be unique here +    Pairs = map_pair_strip_and_termsort(Es), + +    %% fetch registers for values to be put into the map +    List = flatmap(fun({K,V}) -> [K,cg_reg_arg(V,Int0)] end, Pairs), + +    Live = max_reg(Bef#sr.reg), +    Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)}, +    Aft = clear_dead(Int1, Le#l.i, Vdb), +    Target = fetch_reg(R, Int1#sr.reg), + +    I = case Op of +	assoc -> put_map_assoc; +	exact -> put_map_exact +    end, +    {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St};  set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->      %% Find a place for the return register first.      Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)}, @@ -1549,70 +1529,12 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->  	  end,      {Ais,clear_dead(Int, Le#l.i, Vdb),St}. -%% cg_map_pairs(MapPairs) -> {Assoc,Exact} -%%  Assoc = Exact = [{K,V}] -%% -%%  Remove multiple assignments to the same key, and return -%%  one list key-value list with all keys that may or may not exist -%%  (Assoc), and one with keys that must exist (Exact). -%% - -cg_map_pairs(Es0) -> -    Es = cg_map_pairs_1(Es0, 0), -    R0 = sofs:relation(Es), -    R1 = sofs:relation_to_family(R0), -    R2 = sofs:to_external(R1), - -    %% R2 is now [{KeyValue,[{Order,Op,OriginalKey,Value}]}] -    R3 = [begin -	      %% The value for the last pair determines the value. -	      {_,_,_,V} = lists:last(Vs), -	      {Op,{_,SortOrder}=K} = map_pair_op_and_key(Vs), -	      {Op,{SortOrder,K,V}} -	  end || {_,Vs} <- R2], - -    %% R3 is now [{Op,{Key,Value}}] -    R = termsort(R3), - -    %% R4 is now sorted with all alloc first in the list, followed by -    %% all exact. -    {Assoc,Exact} = lists:partition(fun({Op,_}) -> Op =:= assoc end, R), -    {[{K,V} || {_,{_,K,V}} <- Assoc], -     [{K,V} || {_,{_,K,V}} <- Exact]}. - -cg_map_pairs_1([{map_pair_assoc,{_,Kv}=K,V}|T], Order) -> -    [{Kv,{Order,assoc,K,V}}|cg_map_pairs_1(T, Order+1)]; -cg_map_pairs_1([{map_pair_exact,{_,Kv}=K,V}|T], Order) -> -    [{Kv,{Order,exact,K,V}}|cg_map_pairs_1(T, Order+1)]; -cg_map_pairs_1([], _) -> []. - -%% map_pair_op_and_key({_,Op,K,_}) -> {Operator,Key} -%%  Determine the operator and key to use. Throw a 'badarg' -%%  exception if there are contradictory exact updates. - -map_pair_op_and_key(L) -> -    case [K || {_,exact,K,_} <- L] of -	[K] -> -	    %% There is a single ':=' operator. Use that key. -	    {exact,K}; -	[K|T] -> -	    %% There is more than one ':=' operator. All of them -	    %% must have the same key. -	    case lists:all(fun(E) -> E =:= K end, T) of -		true -> -		    {exact,K}; -		false -> -		    %% Some keys are different, e.g. 1 and 1.0. -		    throw(badarg) -	    end; -	[] -> -	    %% Only '=>' operators. Use the first key in the list. -	    [{_,assoc,K,_}|_] = L, -	    {assoc,K} -    end. - -termsort(Ls) -> -    lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Ls). +map_pair_strip_and_termsort(Es) -> +    %% format in +    %%    [{map_pair,K,V}] +    %% where K is for example {integer, 1} and we want to sort on 1. +    Ls = [{K,V}||{_,K,V}<-Es], +    lists:sort(fun({{_,A},_},{{_,B},_}) -> erts_internal:cmp_term(A,B) < 0 end, Ls).  %%%  %%% Code generation for constructing binaries. @@ -2085,7 +2007,7 @@ load_vars(Vs, Regs) ->      foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).  %% put_reg(Val, Regs) -> Regs. -%% find_reg(Val, Regs) -> ok{r{R}} | error. +%% find_reg(Val, Regs) -> {ok,r{R}} | error.  %% fetch_reg(Val, Regs) -> r{R}.  %%  Functions to interface the registers. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index e30bfa729c..3435a46ca9 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -74,7 +74,7 @@  -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,foreach/2]). +		splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]).  -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_cons_skel/3,ann_c_tuple/2,c_tuple/1]). @@ -101,6 +101,8 @@  -record(ireceive2, {anno=#a{},clauses,timeout,action}).  -record(iset,      {anno=#a{},var,arg}).  -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}).  -type iapply()    :: #iapply{}.  -type ibinary()   :: #ibinary{}. @@ -117,10 +119,13 @@  -type ireceive2() :: #ireceive2{}.  -type iset()      :: #iset{}.  -type itry()      :: #itry{}. +-type ifilter()   :: #ifilter{}. +-type igen()      :: #igen{}.  -type i() :: iapply()   | ibinary()   | icall()     | icase()  | icatch()             | iclause()  | ifun()      | iletrec()   | imatch() | iprimop() -           | iprotect() | ireceive1() | ireceive2() | iset()   | itry(). +           | iprotect() | ireceive1() | ireceive2() | iset()   | itry() +           | ifilter()  | igen().  -type warning() :: {file:filename(), [{integer(), module(), term()}]}. @@ -226,13 +231,13 @@ guard(Gs0, St0) ->  			Gt1 = guard_tests(Gt0),  			L = element(2, Gt1),  			{op,L,'or',Gt1,Rhs} -		end, guard_tests(last(Gs0)), first(Gs0)), +		end, guard_tests(last(Gs0)), droplast(Gs0)),      {Gs,St} = gexpr_top(Gs1, St0#core{in_guard=true}),      {Gs,St#core{in_guard=false}}.  guard_tests(Gs) ->      L = element(2, hd(Gs)), -    {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}. +    {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), droplast(Gs))}.  %% gexpr_top(Expr, State) -> {Cexpr,State}.  %%  Generate an internal core expression of a guard test.  Explicitly @@ -479,8 +484,9 @@ expr({cons,L,H0,T0}, St0) ->      {T1,Tps,St2} = safe(T0, St1),      A = lineno_anno(L, St2),      {ann_c_cons(A, H1, T1),Hps ++ Tps,St2}; -expr({lc,L,E,Qs}, St) -> -    lc_tq(L, E, Qs, #c_literal{anno=lineno_anno(L, St),val=[]}, St); +expr({lc,L,E,Qs0}, St0) -> +    {Qs1,St1} = preprocess_quals(L, 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, {nil,L}, St);  expr({tuple,L,Es0}, St0) -> @@ -513,7 +519,7 @@ expr({bin,L,Es0}, St0) ->      end;  expr({block,_,Es0}, St0) ->      %% Inline the block directly. -    {Es1,St1} = exprs(first(Es0), St0), +    {Es1,St1} = exprs(droplast(Es0), St0),      {E1,Eps,St2} = expr(last(Es0), St1),      {E1,Es1 ++ Eps,St2};  expr({'if',L,Cs0}, St0) -> @@ -647,7 +653,7 @@ expr({match,L,P0,E0}, St0) ->  	Other when not is_atom(Other) ->  	    {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps,St4}      end; -expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) -> +expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) ->      %% Optimise '++' here because of the list comprehension algorithm.      %%      %% To avoid achieving quadratic complexity if there is a chain of @@ -655,7 +661,8 @@ expr({op,_,'++',{lc,Llc,E,Qs},More}, St0) ->      %% evaluation of More now. Evaluating More here could also reduce the      %% number variables in the environment for letrec.      {Mc,Mps,St1} = safe(More, St0), -    {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St1), +    {Qs,St2} = preprocess_quals(Llc, Qs0, St1), +    {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2),      {Y,Mps++Yps,St};  expr({op,L,'andalso',E1,E2}, St0) ->      {#c_var{name=V0},St} = new_var(L, St0), @@ -889,133 +896,45 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) ->  %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.  %%  This TQ from Simon PJ pp 127-138.   -%%  This gets a bit messy as we must transform all directly here.  We -%%  recognise guard tests and try to fold them together and join to a -%%  preceding generators, this should give us better and more compact -%%  code. -lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) -> -    {Gs,Qs1} =  splitwith(fun is_guard_test/1, Qs0), +lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, +                      skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, +                      arg={Pre,Arg}}|Qs], Mc, St0) ->      {Name,St1} = new_fun_name("lc", St0), -    {Head,St2} = new_var(St1), -    {Tname,St3} = new_var_name(St2), -    LA = lineno_anno(Line, St3), -    LAnno = #a{anno=LA}, -    Tail = #c_var{anno=LA,name=Tname}, -    {Arg,St4} = new_var(St3), -    {Nc,[],St5} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St4), -    {Guardc,St6} = lc_guard_tests(Gs, St5),	%These are always flat! -    {Lc,Lps,St7} = lc_tq(Line, E, Qs1, Nc, St6), -    {Pc,St8} = list_gen_pattern(P, Line, St7), -    {Gc,Gps,St9} = safe(G, St8),		%Will be a function argument! -    Fc = function_clause([Arg], LA, {Name,1}), - -    %% Avoid constructing a default clause if the list comprehension -    %% only has a variable as generator and there are no guard -    %% tests. In other words, if the comprehension is equivalent to -    %% lists:map/2. -    Cs0 = case {Guardc, Pc} of -	      {[], #c_var{}} -> -		  [#iclause{anno=LAnno, -			    pats=[#c_literal{anno=LA,val=[]}],guard=[], -			    body=[Mc]}]; -	      _ -> -		  [#iclause{anno=#a{anno=[compiler_generated|LA]}, -			    pats=[ann_c_cons(LA, Head, Tail)], -			    guard=[], -			    body=[Nc]}, -		   #iclause{anno=LAnno, -			    pats=[#c_literal{anno=LA,val=[]}],guard=[], -			    body=[Mc]}] -	  end, -    Cs = case Pc of -	     nomatch -> Cs0; -	     _ -> -		 [#iclause{anno=LAnno, -			   pats=[ann_c_cons(LA, Pc, Tail)], -			   guard=Guardc, -			   body=Lps ++ [Lc]}|Cs0] -	 end, -    Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc}, -    {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}], -	      body=Gps ++ [#iapply{anno=LAnno, -				   op=#c_var{anno=LA,name={Name,1}}, -				   args=[Gc]}]}, -     [],St9}; -lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) -> -    {Gs,Qs1} =  splitwith(fun is_guard_test/1, Qs0), -    {Name,St1} = new_fun_name("blc", St0),      LA = lineno_anno(Line, St1),      LAnno = #a{anno=LA}, -    HeadBinPattern = pattern(P, St1), -    #c_binary{segments=Ps0} = HeadBinPattern, -    {Ps,Tail,St2} = append_tail_segment(Ps0, St1), -    {EPs,St3} = emasculate_segments(Ps, St2), -    Pattern = HeadBinPattern#c_binary{segments=Ps}, -    EPattern = HeadBinPattern#c_binary{segments=EPs}, -    {Arg,St4} = new_var(St3), -    {Guardc,St5} = lc_guard_tests(Gs, St4),	%These are always flat! -    Tname = Tail#c_var.name, -    {Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5), -    {Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6), -    {Gc,Gps,St10} = safe(G, St7),		%Will be a function argument! -    Fc = function_clause([Arg], LA, {Name,1}), -    {TailSegList,_,St} = append_tail_segment([], St10), -    Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]}, -		   pats=[Pattern], -		   guard=Guardc, -		   body=Bps ++ [Bc]}, -	  #iclause{anno=#a{anno=[compiler_generated|LA]}, -		   pats=[EPattern], -		   guard=[], -		   body=[#iapply{anno=LAnno, -				 op=#c_var{anno=LA,name={Name,1}}, -				 args=[Tail]}]}, -	  #iclause{anno=LAnno, -		   pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[], -		   body=[Mc]}], -    Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc}, -    {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}], -	      body=Gps ++ [#iapply{anno=LAnno, -				   op=#c_var{anno=LA,name={Name,1}}, -				   args=[Gc]}]}, -     [],St}; -lc_tq(Line, E, [Fil0|Qs0], Mc, St0) -> -    %% Special case sequences guard tests. -    LA = lineno_anno(element(2, Fil0), St0), -    LAnno = #a{anno=LA}, -    case is_guard_test(Fil0) of -	true -> -	    {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0), -	    {Lc,Lps,St1} = lc_tq(Line, E, Qs1, Mc, St0), -	    {Gs,St2} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat! -	    {#icase{anno=LAnno, -		    args=[], -		    clauses=[#iclause{anno=LAnno,pats=[], -				      guard=Gs,body=Lps ++ [Lc]}], -		    fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, -				pats=[],guard=[],body=[Mc]}}, -	     [],St2}; -	false -> -	    {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0), -	    {Fpat,St2} = new_var(St1), -	    Fc = fail_clause([Fpat], LA, -			     c_tuple([#c_literal{val=case_clause},Fpat])), -	    %% Do a novars little optimisation here. -	    {Filc,Fps,St3} = novars(Fil0, St2), -	    {#icase{anno=LAnno, -		    args=[Filc], -		    clauses=[#iclause{anno=LAnno, -				      pats=[#c_literal{anno=LA,val=true}], -				      guard=[], -				      body=Lps ++ [Lc]}, -			     #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, -				      pats=[#c_literal{anno=LA,val=false}], -				      guard=[], -				      body=[Mc]}], -		    fc=Fc}, -	     Fps,St3} -    end; +    F = #c_var{anno=LA,name={Name,1}}, +    Nc = #iapply{anno=GAnno,op=F,args=[Tail]}, +    {Var,St2} = new_var(St1), +    Fc = function_clause([Var], LA, {Name,1}), +    TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]}, +    Cs0 = case {AccPat,AccGuard} of +              {SkipPat,[]} -> +                  %% Skip and accumulator patterns are the same and there is +                  %% no guard, no need to generate a skip clause. +                  [TailClause]; +              _ -> +                  [#iclause{anno=#a{anno=[compiler_generated|LA]}, +                            pats=[SkipPat],guard=[],body=[Nc]}, +                   TailClause] +          end, +    {Cs,St4} = case AccPat of +                   nomatch -> +                       %% The accumulator pattern never matches, no need +                       %% for an accumulator clause. +                       {Cs0,St2}; +                   _ -> +                       {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), +                       {[#iclause{anno=LAnno,pats=[AccPat],guard=AccGuard, +                                  body=Lps ++ [Lc]}|Cs0], +                        St3} +               end, +    Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc}, +    {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}], +              body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]}, +     [],St4}; +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) ->      {H1,Hps,St1} = safe(E0, St0),      {T1,Tps,St} = force_safe(Mc0, St1), @@ -1025,143 +944,60 @@ lc_tq(Line, E0, [], Mc0, St0) ->  %% bc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.  %%  This TQ from Gustafsson ERLANG'05.   -%%  This gets a bit messy as we must transform all directly here.  We -%%  recognise guard tests and try to fold them together and join to a -%%  preceding generators, this should give us better and more compact -%%  code.  %%  More could be transformed before calling bc_tq. -bc_tq(Line, Exp, Qualifiers, _, St0) -> +bc_tq(Line, Exp, Qs0, _, St0) ->      {BinVar,St1} = new_var(St0), -    {Sz,SzPre,St2} = bc_initial_size(Exp, Qualifiers, St1), -    {E,BcPre,St} = bc_tq1(Line, Exp, Qualifiers, BinVar, St2), +    {Sz,SzPre,St2} = bc_initial_size(Exp, Qs0, St1), +    {Qs,St3} = preprocess_quals(Line, Qs0, St2), +    {E,BcPre,St} = bc_tq1(Line, Exp, Qs, BinVar, St3),      Pre = SzPre ++  	[#iset{var=BinVar,  	       arg=#iprimop{name=#c_literal{val=bs_init_writable},  			    args=[Sz]}}] ++ BcPre,      {E,Pre,St}. -bc_tq1(Line, E, [{generate,Lg,P,G}|Qs0], AccExpr, St0) -> -    {Gs,Qs1} =  splitwith(fun is_guard_test/1, Qs0), -    {Name,St1} = new_fun_name("lbc", St0), -    LA = lineno_anno(Line, St1), -    {[Head,Tail,AccVar],St2} = new_vars(LA, 3, St1), -    LAnno = #a{anno=LA}, -    {Arg,St3} = new_var(St2), -    NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name}, -				       {var,Lg,AccVar#c_var.name}]}, -    {Guardc,St4} = lc_guard_tests(Gs, St3),	%These are always flat! -    {Lc,Lps,St5} = bc_tq1(Line, E, Qs1, AccVar, St4), -    {Nc,Nps,St6} = expr(NewMore, St5), -    {Pc,St7} = list_gen_pattern(P, Line, St6), -    {Gc,Gps,St8} = safe(G, St7),		%Will be a function argument! -    Fc = function_clause([Arg,AccVar], LA, {Name,2}), -    Cs0 = case {Guardc, Pc} of -	      {[], #c_var{}} -> -		  [#iclause{anno=LAnno, -			    pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[], -			    body=[AccVar]}]; -	      _ -> -		  [#iclause{anno=#a{anno=[compiler_generated|LA]}, -			    pats=[ann_c_cons(LA, Head, Tail),AccVar], -			    guard=[], -			    body=Nps ++ [Nc]}, -		   #iclause{anno=LAnno, -			    pats=[#c_literal{anno=LA,val=[]},AccVar],guard=[], -			    body=[AccVar]}] -	  end, -    Cs = case Pc of -	     nomatch -> Cs0; -	     _ -> -		 Body = Lps ++ Nps ++ [#iset{var=AccVar,arg=Lc},Nc], -		 [#iclause{anno=LAnno, -			   pats=[ann_c_cons(LA,Pc,Tail),AccVar], -			   guard=Guardc, -			   body=Body}|Cs0] -	 end, -    Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, -    {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}], -	      body=Gps ++ [#iapply{anno=LAnno, -				   op=#c_var{anno=LA,name={Name,2}}, -				   args=[Gc,AccExpr]}]}, -     [],St8}; -bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> -    {Gs,Qs1} =  splitwith(fun is_guard_test/1, Qs0), +bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, +                       skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, +                       arg={Pre,Arg}}|Qs], Mc, St0) ->      {Name,St1} = new_fun_name("lbc", St0),      LA = lineno_anno(Line, St1), -    {AccVar,St2} = new_var(LA, St1), -    LAnno = #a{anno=LA}, -    HeadBinPattern = pattern(P, St2), -    #c_binary{segments=Ps0} = HeadBinPattern, -    {Ps,Tail,St3} = append_tail_segment(Ps0, St2), -    {EPs,St4} = emasculate_segments(Ps, St3), -    Pattern = HeadBinPattern#c_binary{segments=Ps}, -    EPattern = HeadBinPattern#c_binary{segments=EPs}, -    {Arg,St5} = new_var(St4), -    NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name}, -				       {var,Lg,AccVar#c_var.name}]}, -    {Guardc,St6} = lc_guard_tests(Gs, St5),	%These are always flat! -    {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6), -    {Nc,Nps,St8} = expr(NewMore, St7), -    {Gc,Gps,St9} = safe(G, St8),	 %Will be a function argument! -    Fc = function_clause([Arg,AccVar], LA, {Name,2}), -    Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc], -    {TailSegList,_,St} = append_tail_segment([], St9), -    Cs = [#iclause{anno=LAnno, -		   pats=[Pattern,AccVar], -		   guard=Guardc, -		   body=Body}, -	  #iclause{anno=#a{anno=[compiler_generated|LA]}, -		   pats=[EPattern,AccVar], -		   guard=[], -		   body=Nps ++ [Nc]}, -	  #iclause{anno=LAnno, -		   pats=[#c_binary{anno=LA,segments=TailSegList},AccVar], -		   guard=[], -		   body=[AccVar]}], -    Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, -    {#iletrec{anno=LAnno,defs=[{{Name,2},Fun}], -	      body=Gps ++ [#iapply{anno=LAnno, -				   op=#c_var{anno=LA,name={Name,2}}, -				   args=[Gc,AccExpr]}]}, -     [],St}; -bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) -> -    %% Special case sequences guard tests. -    LA = lineno_anno(element(2, Fil0), St0),      LAnno = #a{anno=LA}, -    case is_guard_test(Fil0) of -	true -> -	    {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0), -	    {Bc,Bps,St1} = bc_tq1(Line, E, Qs1, AccVar, St0), -	    {Gs,St} = lc_guard_tests([Fil0|Gs0], St1), %These are always flat! -	    {#icase{anno=LAnno, -		    args=[], -		    clauses=[#iclause{anno=LAnno, -				      pats=[], -				      guard=Gs,body=Bps ++ [Bc]}], -		    fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, -				pats=[],guard=[],body=[AccVar]}}, -	     [],St}; -	false -> -	    {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0), -	    {Fpat,St2} = new_var(St1), -	    Fc = fail_clause([Fpat], LA, -			     c_tuple([#c_literal{val=case_clause},Fpat])), -	    %% Do a novars little optimisation here. -	    {Filc,Fps,St} = novars(Fil0, St2), -	    {#icase{anno=LAnno, -		    args=[Filc], -		    clauses=[#iclause{anno=LAnno, -				      pats=[#c_literal{anno=LA,val=true}], -				      guard=[], -				      body=Bps ++ [Bc]}, -			     #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, -				      pats=[#c_literal{anno=LA,val=false}], -				      guard=[], -				      body=[AccVar]}], -		    fc=Fc}, -	     Fps,St} -    end; +    {Vars=[_,AccVar],St2} = new_vars(LA, 2, St1), +    F = #c_var{anno=LA,name={Name,2}}, +    Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]}, +    Fc = function_clause(Vars, LA, {Name,2}), +    TailClause = #iclause{anno=LAnno,pats=[TailPat,AccVar],guard=[], +                          body=[AccVar]}, +    Cs0 = case {AccPat,AccGuard} of +              {SkipPat,[]} -> +                  %% Skip and accumulator patterns are the same and there is +                  %% no guard, no need to generate a skip clause. +                  [TailClause]; +              _ -> +                  [#iclause{anno=#a{anno=[compiler_generated|LA]}, +                            pats=[SkipPat,AccVar],guard=[],body=[Nc]}, +                   TailClause] +          end, +    {Cs,St4} = case AccPat of +                   nomatch -> +                       %% The accumulator pattern never matches, no need +                       %% for an accumulator clause. +                       {Cs0,St2}; +                   _ -> +                       {Bc,Bps,St3} = bc_tq1(Line, E, Qs, AccVar, St2), +                       Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], +                       {[#iclause{anno=LAnno, +                                  pats=[AccPat,AccVar],guard=AccGuard, +                                  body=Body}|Cs0], +                        St3} +               end, +    Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, +    {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}], +              body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]}, +     [],St4}; +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) ->      {E,Pre,St} = expr({bin,Bl,[{bin_element,Bl,  				{var,Bl,AccVar#c_var.name}, @@ -1169,16 +1005,154 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->  				[binary,{unit,1}]}|Elements]}, St0),      #a{anno=A} = Anno0 = get_anno(E),      Anno = Anno0#a{anno=[compiler_generated,single_use|A]}, -    %%Anno = Anno0#a{anno=[compiler_generated|A]},      {set_anno(E, Anno),Pre,St}. +%% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) -> +%%     {Case,[PreExpr],State}. +%%  Transform an intermediate comprehension filter to its intermediate case +%%  representation. + +filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg={Pre,Arg}}, +          Mc, St0, Qs, TqFun) -> +    %% The filter is an expression, it is compiled to a case of degree 1 with +    %% 3 clauses, one accumulating, one skipping and the final one throwing +    %% {case_clause,Value} where Value is the result of the filter and is not a +    %% boolean. +    {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0), +    {FailPat,St2} = new_var(St1), +    Fc = fail_clause([FailPat], LA, +                     c_tuple([#c_literal{val=case_clause},FailPat])), +    {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[Arg], +            clauses=[#iclause{anno=LAnno, +                              pats=[#c_literal{val=true}],guard=[], +                              body=Lps ++ [Lc]}, +                     #iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, +                              pats=[#c_literal{val=false}],guard=[], +                              body=[Mc]}], +            fc=Fc}, +     Pre,St2}; +filter_tq(Line, E, #ifilter{anno=#a{anno=LA}=LAnno,arg=Guard}, +          Mc, St0, Qs, TqFun) when is_list(Guard) -> +    %% Otherwise it is a guard, compiled to a case of degree 0 with 2 clauses, +    %% the first matches if the guard succeeds and the comprehension continues +    %% or the second one is selected and the current element is skipped. +    {Lc,Lps,St1} = TqFun(Line, E, Qs, Mc, St0), +    {#icase{anno=LAnno#a{anno=[list_comprehension|LA]},args=[], +            clauses=[#iclause{anno=LAnno,pats=[],guard=Guard,body=Lps ++ [Lc]}], +            fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, +                        pats=[],guard=[],body=[Mc]}}, +     [],St1}. + +%% preprocess_quals(Line, [Qualifier], State) -> {[Qualifier'],State}. +%%  Preprocess a list of Erlang qualifiers into its intermediate representation, +%%  represented as a list of #igen{} and #ifilter{} records. We recognise guard +%%  tests and try to fold them together and join to a preceding generators, this +%%  should give us better and more compact code. + +preprocess_quals(Line, Qs, St) -> +    preprocess_quals(Line, Qs, St, []). + +preprocess_quals(Line, [Q|Qs0], St0, Acc) -> +    case is_generator(Q) of +        true -> +            {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0), +            {Gen,St} = generator(Line, Q, Gs, St0), +            preprocess_quals(Line, Qs, St, [Gen|Acc]); +        false -> +            LAnno = #a{anno=lineno_anno(get_anno(Q), St0)}, +            case is_guard_test(Q) of +                true -> +                    %% When a filter is a guard test, its argument in the +                    %% #ifilter{} record is a list as returned by +                    %% lc_guard_tests/2. +                    {Gs,Qs} = splitwith(fun is_guard_test/1, Qs0), +                    {Cg,St} = lc_guard_tests([Q|Gs], St0), +                    Filter = #ifilter{anno=LAnno,arg=Cg}, +                    preprocess_quals(Line, Qs, St, [Filter|Acc]); +                false -> +                    %% Otherwise, it is a pair {Pre,Arg} as in a generator +                    %% input. +                    {Ce,Pre,St} = novars(Q, St0), +                    Filter = #ifilter{anno=LAnno,arg={Pre,Ce}}, +                    preprocess_quals(Line, Qs0, St, [Filter|Acc]) +            end +    end; +preprocess_quals(_, [], St, Acc) -> +    {reverse(Acc),St}. + +is_generator({generate,_,_,_}) -> true; +is_generator({b_generate,_,_,_}) -> true; +is_generator(_) -> false. + +%% +%% Generators are abstracted as sextuplets: +%%  - 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. +%%  - skip_pat is the skip pattern, e.g. <<X,_:X,Tail/bitstring>> for +%%    <<X,1:X>> <= Expr. +%%  - tail is the variable used in AccPat and SkipPat bound to the rest of the +%%    generator input. +%%  - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list +%%    and bit string 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. +%% + +%% generator(Line, Generator, Guard, State) -> {Generator',State}. +%%  Transform a given generator into its #igen{} representation. + +generator(Line, {generate,Lg,P0,E}, Gs, St0) -> +    LA = lineno_anno(Line, St0), +    GA = lineno_anno(Lg, 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, +    {Ce,Pre,St4} = safe(E, St3), +    Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, +                tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}}, +    {Gen,St4}; +generator(Line, {b_generate,Lg,P,E}, Gs, St0) -> +    LA = lineno_anno(Line, St0), +    GA = lineno_anno(Lg, St0), +    Cp = #c_binary{segments=Segs} = pattern(P, St0), +    %% The function append_tail_segment/2 keeps variable patterns as-is, making +    %% it possible to have the same skip clause removal as with list generators. +    {AccSegs,Tail,TailSeg,St1} = append_tail_segment(Segs, St0), +    AccPat = Cp#c_binary{segments=AccSegs}, +    {Cg,St2} = lc_guard_tests(Gs, St1), +    {SkipSegs,St3} = emasculate_segments(AccSegs, St2), +    SkipPat = Cp#c_binary{segments=SkipSegs}, +    {Ce,Pre,St4} = safe(E, St3), +    Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, +                tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]}, +                arg={Pre,Ce}}, +    {Gen,St4}. +  append_tail_segment(Segs, St0) ->      {Var,St} = new_var(St0),      Tail = #c_bitstr{val=Var,size=#c_literal{val=all},  		     unit=#c_literal{val=1},  		     type=#c_literal{val=binary},  		     flags=#c_literal{val=[unsigned,big]}}, -    {Segs++[Tail],Var,St}. +    {Segs++[Tail],Var,Tail,St}.  emasculate_segments(Segs, St) ->      emasculate_segments(Segs, St, []). @@ -1189,7 +1163,7 @@ emasculate_segments([B|Rest], St0, Acc) ->      {Var,St1} = new_var(St0),      emasculate_segments(Rest, St1, [B#c_bitstr{val=Var}|Acc]);  emasculate_segments([], St, Acc) -> -    {lists:reverse(Acc),St}. +    {reverse(Acc),St}.  lc_guard_tests([], St) -> {[],St};  lc_guard_tests(Gs0, St0) -> @@ -1588,15 +1562,6 @@ pat_alias_list(_, _) -> throw(nomatch).  pattern_list(Ps, St) -> [pattern(P, St) || P <- Ps]. -%% first([A]) -> [A]. -%% last([A]) -> A. - -first([_]) -> []; -first([H|T]) -> [H|first(T)]. - -last([L]) -> L; -last([_|T]) -> last(T). -  %% make_vars([Name]) -> [{Var,Name}].  make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ]. @@ -1679,13 +1644,13 @@ uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->  uguard([], [], _, St) -> {[],St};  uguard(Pg, [], Ks, St) ->      %% No guard, so fold together equality tests. -    uguard(first(Pg), [last(Pg)], Ks, St); +    uguard(droplast(Pg), [last(Pg)], Ks, St);  uguard(Pg, Gs0, Ks, St0) ->      %% Gs0 must contain at least one element here.      {Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->  			      {L,St2} = new_var(St1),  			      {R,St3} = new_var(St2), -			      {[#iset{var=L,arg=T}] ++ first(Gs1) ++ +			      {[#iset{var=L,arg=T}] ++ droplast(Gs1) ++  			       [#iset{var=R,arg=last(Gs1)},  				#icall{anno=#a{}, %Must have an #a{}  				       module=#c_literal{val=erlang}, @@ -2088,7 +2053,8 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,              RecVar = #c_var{name={Name,length(Ps)}},              Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body},              CFun1 = CFun0#c_fun{body=Let}, -            Letrec = #c_letrec{defs=[{RecVar,CFun1}], +            Letrec = #c_letrec{anno=A0#a.anno, +			       defs=[{RecVar,CFun1}],                                 body=RecVar},              {Letrec,[],Us1,St1}      end; diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 9a2b1605ad..bc5ca0314a 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -81,7 +81,7 @@  -export([module/2,format_error/1]).  -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,member/2, -		keymember/3,keyfind/3,partition/2]). +		keymember/3,keyfind/3,partition/2,droplast/1,last/1]).  -import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).  -import(cerl, [c_tuple/1]). @@ -274,8 +274,7 @@ expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->      {#k_tuple{anno=A,es=Kes},Ep,St1};  expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) ->      {Var,[],St1} = expr(Var0, Sub, St0), -    {Kes,Ep,St2} = map_pairs(Ces, Sub, St1), -    {#k_map{anno=A,var=Var,es=Kes},Ep,St2}; +    map_split_pairs(A, Var, Ces, Sub, St1);  expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->      try atomic_bin(Cv, Sub, St0) of  	{Kv,Ep,St1} -> @@ -351,7 +350,7 @@ expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->      {Kvs,Pv,St2} = match_vars(Ka, St1),		%Must have variables here!      {Km,St3} = kmatch(Kvs, Ccs, Sub, St2),      Match = flatten_seq(build_match(Kvs, Km)), -    {last(Match),Pa ++ Pv ++ first(Match),St3}; +    {last(Match),Pa ++ Pv ++ droplast(Match),St3};  expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->      {Ke,Pe,St1} = atomic(Ce, Sub, St0),		%Force this to be atomic!      {Rvar,St2} = new_var(St1), @@ -497,15 +496,71 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->  translate_fc(Args) ->      [#c_literal{val=function_clause},make_list(Args)]. -%% FIXME: Not completed -map_pairs(Es, Sub, St) -> -    foldr(fun -	    (#c_map_pair{op=#c_literal{val=Op},key=K0,val=V0}, {Kes,Esp,St0}) when -                                           Op =:= assoc; Op =:= exact -> %% assert Op -		{K,[],St1} = expr(K0, Sub, St0), -		{V,Ep,St2} = atomic(V0, Sub, St1), -		{[#k_map_pair{op=Op,key=K,val=V}|Kes],Ep ++ Esp,St2} -	end, {[],[],St}, Es). +    %{Kes,Ep,St2} = map_pairs(Ces, Sub, St1), +map_split_pairs(A, Var, Ces, Sub, St0) -> +    %% two steps +    %% 1. force variables +    %% 2. remove multiples +    Pairs0 = [{Op,K,V} || #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces], +    {Pairs,Esp,St1} = foldr(fun +	    ({Op,K0,V0}, {Ops,Espi,Sti0}) when Op =:= assoc; Op =:= exact -> +		{K,[],Sti1} = expr(K0, Sub, Sti0), +		{V,Ep,Sti2} = atomic(V0, Sub, Sti1), +		{[{Op,K,V}|Ops],Ep ++ Espi,Sti2} +	end, {[],[],St0}, Pairs0), + +    case map_group_pairs(Pairs) of +	{Assoc,[]} -> +	    Kes = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], +	    {#k_map{anno=A,op=assoc,var=Var,es=Kes},Esp,St1}; +	{[],Exact} -> +	    Kes = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], +	    {#k_map{anno=A,op=exact,var=Var,es=Kes},Esp,St1}; +	{Assoc,Exact} -> +	    Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], +	    {Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1), +	    Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], +	    {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Em ++ Esp,St2} + +    end. + +%% Group map by Assoc operations and Exact operations + +map_group_pairs(Es) -> +    Groups = dict:to_list(map_group_pairs(Es,dict:new())), +    partition(fun({_,{Op,_,_}}) -> Op =:= assoc end, Groups). + +map_group_pairs([{assoc,K,V}|Es0],Used0) -> +    Used1 = case map_key_is_used(K,Used0) of +	{ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); +	{ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); +	_                 -> map_key_set_used(K,{assoc,K,V},Used0) +    end, +    map_group_pairs(Es0,Used1); +map_group_pairs([{exact,K,V}|Es0],Used0) -> +    Used1 = case map_key_is_used(K,Used0) of +	{ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); +	{ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); +	_                 -> map_key_set_used(K,{exact,K,V},Used0) +    end, +    map_group_pairs(Es0,Used1); +map_group_pairs([],Used) -> +    Used. + +map_key_set_used(K,How,Used) -> +    dict:store(map_key_clean(K),How,Used). + +map_key_is_used(K,Used) -> +    dict:find(map_key_clean(K),Used). + +%% Be explicit instead of using set_kanno(K,[]) +map_key_clean(#k_literal{val=V}) -> {k_literal,V}; +map_key_clean(#k_int{val=V})     -> {k_int,V}; +map_key_clean(#k_float{val=V})   -> {k_float,V}; +map_key_clean(#k_atom{val=V})    -> {k_atom,V}; +map_key_clean(#k_nil{})          -> k_nil; +map_key_clean(#k_var{name=V})    -> {k_var,V}. +  %% call_type(Module, Function, Arity) -> call | bif | apply | error.  %%  Classify the call. @@ -664,11 +719,11 @@ pattern(#c_tuple{anno=A,es=Ces}, Isub, Osub0, St0) ->      {#k_tuple{anno=A,es=Kes},Osub1,St1};  pattern(#c_map{anno=A,es=Ces}, Isub, Osub0, St0) ->      {Kes,Osub1,St1} = pattern_list(Ces, Isub, Osub0, St0), -    {#k_map{anno=A,es=Kes},Osub1,St1}; +    {#k_map{anno=A,op=exact,es=Kes},Osub1,St1};  pattern(#c_map_pair{op=#c_literal{val=exact},anno=A,key=Ck,val=Cv},Isub, Osub0, St0) ->      {Kk,Osub1,St1} = pattern(Ck, Isub, Osub0, St0),      {Kv,Osub2,St2} = pattern(Cv, Isub, Osub1, St1), -    {#k_map_pair{anno=A,op=exact,key=Kk,val=Kv},Osub2,St2}; +    {#k_map_pair{anno=A,key=Kk,val=Kv},Osub2,St2};  pattern(#c_binary{anno=A,segments=Cv}, Isub, Osub0, St0) ->      {Kv,Osub1,St1} = pattern_bin(Cv, Isub, Osub0, St0),      {#k_binary{anno=A,segs=Kv},Osub1,St1}; @@ -847,15 +902,6 @@ foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->      foldr2(Fun, Acc1, L1, L2);  foldr2(_, Acc, [], []) -> Acc. -%% first([A]) -> [A]. -%% last([A]) -> A. - -last([L]) -> L; -last([_|T]) -> last(T). - -first([_]) -> []; -first([H|T]) -> [H|first(T)]. -  %% This code implements the algorithm for an optimizing compiler for  %% pattern matching given "The Implementation of Functional  %% Programming Languages" by Simon Peyton Jones. The code is much @@ -1365,9 +1411,8 @@ new_clauses(Cs0, U, St) ->  				     [S,N|As];  				 #k_bin_int{next=N} ->  				     [N|As]; -				 #k_map{es=Es} -> -				     Vals = [V || -						#k_map_pair{op=exact,val=V} <- Es], +				 #k_map{op=exact,es=Es} -> +				     Vals = [V || #k_map_pair{val=V} <- Es],  				     Vals ++ As;  				 _Other ->  				     As @@ -1467,9 +1512,9 @@ arg_val(Arg, C) ->  		_ ->  		    {set_kanno(S, []),U,T,Fs}  	    end; -	#k_map{es=Es} -> +	#k_map{op=exact,es=Es} ->  	    Keys = [begin -			#k_map_pair{op=exact,key=#k_literal{val=Key}} = Pair, +			#k_map_pair{key=#k_literal{val=Key}} = Pair,  			Key  		    end || Pair <- Es],  	    %% multiple keys may have the same name @@ -1885,7 +1930,7 @@ pat_vars(#k_tuple{es=Es}) ->      pat_list_vars(Es);  pat_vars(#k_map{es=Es}) ->      pat_list_vars(Es); -pat_vars(#k_map_pair{op=exact,val=V}) -> +pat_vars(#k_map_pair{val=V}) ->      pat_vars(V).  pat_list_vars(Ps) -> diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index c7886a070d..ab66445f73 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -38,8 +38,8 @@  -record(k_nil, {anno=[]}).  -record(k_tuple, {anno=[],es}). --record(k_map, {anno=[],var,es}). --record(k_map_pair, {anno=[],op,key,val}). +-record(k_map, {anno=[],var,op,es}). +-record(k_map_pair, {anno=[],key,val}).  -record(k_cons, {anno=[],hd,tl}).  -record(k_binary, {anno=[],segs}).  -record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}). diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index edbd3f74f8..639c6737e2 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -110,15 +110,18 @@ format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) ->       " | ",format_1(Var, Ctxt),       $},$~      ]; -format_1(#k_map{es=Es}, Ctxt) -> -    [$~,${, +format_1(#k_map{op=assoc,es=Es}, Ctxt) -> +    ["~{",       format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), -     $},$~ +     "}~" +    ]; +format_1(#k_map{op=exact,es=Es}, Ctxt) -> +    ["::{", +     format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), +     "}::"      ]; -format_1(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) -> -    ["~<",format(K, Ctxt),",",format(V, Ctxt),">"]; -format_1(#k_map_pair{op=exact,key=K,val=V}, Ctxt) -> -    ["::<",format(K, Ctxt),",",format(V, Ctxt),">"]; +format_1(#k_map_pair{key=K,val=V}, Ctxt) -> +    ["<",format(K, Ctxt),",",format(V, Ctxt),">"];  format_1(#k_binary{segs=S}, Ctxt) ->      ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];  format_1(#k_bin_seg{next=Next}=S, Ctxt) -> diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index ae928e955c..c4f54a7970 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -367,12 +367,10 @@ literal(#k_bin_end{}, Ctxt) ->      {bin_end,Ctxt};  literal(#k_tuple{es=Es}, Ctxt) ->      {tuple,literal_list(Es, Ctxt)}; -literal(#k_map{var=Var,es=Es}, Ctxt) -> -    {map,literal(Var, Ctxt),literal_list(Es, Ctxt)}; -literal(#k_map_pair{op=assoc,key=K,val=V}, Ctxt) -> -    {map_pair_assoc,literal(K, Ctxt),literal(V, Ctxt)}; -literal(#k_map_pair{op=exact,key=K,val=V}, Ctxt) -> -    {map_pair_exact,literal(K, Ctxt),literal(V, Ctxt)}; +literal(#k_map{op=Op,var=Var,es=Es}, Ctxt) -> +    {map,Op,literal(Var, Ctxt),literal_list(Es, Ctxt)}; +literal(#k_map_pair{key=K,val=V}, Ctxt) -> +    {map_pair,literal(K, Ctxt),literal(V, Ctxt)};  literal(#k_literal{val=V}, _Ctxt) ->      {literal,V}. @@ -402,8 +400,8 @@ literal2(#k_bin_end{}, Ctxt) ->      {bin_end,Ctxt};  literal2(#k_tuple{es=Es}, Ctxt) ->      {tuple,literal_list2(Es, Ctxt)}; -literal2(#k_map{es=Es}, Ctxt) -> -    {map,literal_list2(Es, Ctxt)}; +literal2(#k_map{op=Op,es=Es}, Ctxt) -> +    {map,Op,literal_list2(Es, Ctxt)};  literal2(#k_map_pair{key=K,val=V}, Ctxt) ->      {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}.  | 
