diff options
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/beam_block.erl | 35 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 36 | ||||
-rw-r--r-- | lib/compiler/src/cerl.erl | 6 | ||||
-rw-r--r-- | lib/compiler/src/core_lint.erl | 25 | ||||
-rw-r--r-- | lib/compiler/src/v3_codegen.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 78 |
6 files changed, 119 insertions, 65 deletions
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 9c6f835ab0..c45874597a 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -36,12 +36,13 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) -> %% Collect basic blocks and optimize them. Is2 = blockify(Is1), - Is3 = beam_utils:live_opt(Is2), - Is4 = opt_blocks(Is3), - Is5 = beam_utils:delete_live_annos(Is4), + Is3 = move_allocates(Is2), + Is4 = beam_utils:live_opt(Is3), + Is5 = opt_blocks(Is4), + Is6 = beam_utils:delete_live_annos(Is5), %% Optimize bit syntax. - {Is,Lc} = bsm_opt(Is5, Lc0), + {Is,Lc} = bsm_opt(Is6, Lc0), %% Done. {{function,Name,Arity,CLabel,Is},Lc} @@ -156,11 +157,7 @@ opt_blocks([I|Is]) -> opt_blocks([]) -> []. opt_block(Is0) -> - %% We explicitly move any allocate instruction upwards before optimising - %% moves, to avoid any potential problems with the calculation of live - %% registers. - Is1 = move_allocates(Is0), - Is = find_fixpoint(fun opt/1, Is1), + Is = find_fixpoint(fun opt/1, Is0), opt_alloc(Is). find_fixpoint(OptFun, Is0) -> @@ -170,11 +167,21 @@ find_fixpoint(OptFun, Is0) -> end. %% move_allocates(Is0) -> Is -%% Move allocates upwards in the instruction stream, in the hope of -%% getting more possibilities for optimizing away moves later. - -move_allocates(Is) -> - move_allocates_1(reverse(Is), []). +%% Move allocate instructions upwards in the instruction stream, in the +%% hope of getting more possibilities for optimizing away moves later. +%% +%% NOTE: Moving allocation instructions is only safe because it is done +%% immediately after code generation so that we KNOW that if {x,X} is +%% initialized, all x registers with lower numbers are also initialized. +%% That assumption may not be true after other optimizations, such as +%% the beam_utils:live_opt/1 optimization. + +move_allocates([{block,Bl0}|Is]) -> + Bl = move_allocates_1(reverse(Bl0), []), + [{block,Bl}|move_allocates(Is)]; +move_allocates([I|Is]) -> + [I|move_allocates(Is)]; +move_allocates([]) -> []. move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) -> {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0), diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 761d4ffec0..45cdf8a659 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -407,16 +407,23 @@ check_liveness(R, [{bif,Op,{f,Fail},Ss,D}|Is], St0) -> Other -> Other end; -check_liveness(R, [{gc_bif,Op,{f,Fail},_,Ss,D}|Is], St0) -> - case check_liveness_fail(R, Op, Ss, Fail, St0) of - {killed,St} = Killed -> - case member(R, Ss) of - true -> {used,St}; - false when R =:= D -> Killed; - false -> check_liveness(R, Is, St) - end; - Other -> - Other +check_liveness(R, [{gc_bif,Op,{f,Fail},Live,Ss,D}|Is], St0) -> + case R of + {x,X} when X >= Live -> + {killed,St0}; + {x,_} -> + {used,St0}; + _ -> + case check_liveness_fail(R, Op, Ss, Fail, St0) of + {killed,St}=Killed -> + case member(R, Ss) of + true -> {used,St}; + false when R =:= D -> Killed; + false -> check_liveness(R, Is, St) + end; + Other -> + Other + end end; check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) -> case member(R, Ss) of @@ -482,10 +489,13 @@ check_liveness(R, [{bs_context_to_binary,S}|Is], St) -> S -> {used,St}; _ -> check_liveness(R, Is, St) end; -check_liveness(R, [{loop_rec,{f,_},{x,0}}|Is], St) -> +check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) -> case R of - {x,_} -> {killed,St}; - _ -> check_liveness(R, Is, St) + {x,_} -> + {killed,St}; + _ -> + %% y register. Rarely happens. Be very conversative. + {unknown,St} end; check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) -> check_liveness_at(R, Fail, St); diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index d1fd9d40e2..4b74d60e9f 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -973,7 +973,7 @@ atom_name(Node) -> %% TODO: replace the use of the unofficial 'write_string/2'. --spec atom_lit(cerl()) -> string(). +-spec atom_lit(cerl()) -> nonempty_string(). atom_lit(Node) -> io_lib:write_string(atom_name(Node), $'). %' stupid Emacs. @@ -1079,7 +1079,7 @@ char_val(Node) -> %% %% @see c_char/1 --spec char_lit(c_literal()) -> string(). +-spec char_lit(c_literal()) -> nonempty_string(). char_lit(Node) -> io_lib:write_char(char_val(Node)). @@ -1178,7 +1178,7 @@ string_val(Node) -> %% %% @see c_string/1 --spec string_lit(c_literal()) -> string(). +-spec string_lit(c_literal()) -> nonempty_string(). string_lit(Node) -> io_lib:write_string(string_val(Node)). diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl index b633f568c9..b513a8965c 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. 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 @@ -65,7 +65,8 @@ | {'return_mismatch', fa()} | {'undefined_function', fa()} | {'duplicate_var', cerl:var_name(), fa()} | {'unbound_var', cerl:var_name(), fa()} - | {'undefined_function', fa(), fa()}. + | {'undefined_function', fa(), fa()} + | {'tail_segment_not_at_end', fa()}. -type error() :: {module(), err_desc()}. -type warning() :: {module(), term()}. @@ -116,7 +117,9 @@ format_error({duplicate_var,N,{F,A}}) -> format_error({unbound_var,N,{F,A}}) -> io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]); format_error({undefined_function,{F1,A1},{F2,A2}}) -> - io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]). + io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]); +format_error({tail_segment_not_at_end,{F,A}}) -> + io_lib:format("binary tail segment not at end in ~w/~w", [F,A]). -type ret() :: {'ok', [{module(), [warning(),...]}]} | {'error', [{module(), [error(),...]}], @@ -450,7 +453,8 @@ pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) -> pattern_list([H,T], Def, Ps, St); pattern(#c_tuple{es=Es}, Def, Ps, St) -> pattern_list(Es, Def, Ps, St); -pattern(#c_binary{segments=Ss}, Def, Ps, St) -> +pattern(#c_binary{segments=Ss}, Def, Ps, St0) -> + St = pat_bin_tail_check(Ss, St0), pat_bin(Ss, Def, Ps, St); pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) -> {Vvs,St1} = variable(V, Ps, St0), @@ -482,6 +486,19 @@ pat_segment(#c_bitstr{val=V,size=S,type=T}, Def0, Ps0, St0) -> pat_segment(_, Def, Ps, St) -> {Ps,Def,add_error({not_bs_pattern,St#lint.func}, St)}. +%% pat_bin_tail_check([Elem], State) -> State. +%% There must be at most one tail segment (a size-less segment of +%% type binary) and it must occur at the end. + +pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}], St) -> + %% Size-less field is OK at the end of the list of segments. + St; +pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}|_], St) -> + add_error({tail_segment_not_at_end,St#lint.func}, St); +pat_bin_tail_check([_|Ss], St) -> + pat_bin_tail_check(Ss, St); +pat_bin_tail_check([], St) -> St. + %% pat_bit_expr(SizePat, Type, Defined, State) -> State. %% Check the Size pattern, this is an input! Because of optimizations, %% we must allow any kind of constant and literal here. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 948937c438..77da6c8d00 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1523,7 +1523,9 @@ cg_binary_size_1([], Bits, Acc) -> [{1,_}|_] -> {bs_init_bits,cg_binary_bytes_to_bits(Sizes, [])}; [{8,_}|_] -> - {bs_init2,[E || {8,E} <- Sizes]} + {bs_init2,[E || {8,E} <- Sizes]}; + [] -> + {bs_init_bits,[]} end. cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) -> diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index f6bb45787c..2da24b2908 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -892,25 +892,22 @@ lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) -> 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), - {Tname,St2} = new_var_name(St1), - LA = lineno_anno(Line, St2), + LA = lineno_anno(Line, St1), LAnno = #a{anno=LA}, - HeadBinPattern = pattern(P,St2), - #c_binary{segments=Ps} = HeadBinPattern, - {EPs,St3} = emasculate_segments(Ps,St2), - Tail = #c_var{anno=LA,name=Tname}, - TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all}, - unit=#c_literal{val=1}, - type=#c_literal{val=binary}, - flags=#c_literal{val=[big,unsigned]}}, - Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]}, - EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]}, + 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, @@ -922,14 +919,14 @@ lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) -> op=#c_var{anno=LA,name={Name,1}}, args=[Tail]}]}, #iclause{anno=LAnno, - pats=[#c_binary{anno=LA, segments=[TailSegment]}],guard=[], + 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]}]}, - [],St10}; + [],St}; lc_tq(Line, E, [Fil0|Qs0], Mc, St0) -> %% Special case sequences guard tests. LA = lineno_anno(Line, St0), @@ -1037,26 +1034,24 @@ bc_tq1(Line, E, [{b_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), - {[Tail,AccVar],St2} = new_vars(LA, 2, St1), + {AccVar,St2} = new_var(LA, St1), LAnno = #a{anno=LA}, HeadBinPattern = pattern(P, St2), - #c_binary{segments=Ps} = HeadBinPattern, - {EPs,St3} = emasculate_segments(Ps, St2), - TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all}, - unit=#c_literal{val=1}, - type=#c_literal{val=binary}, - flags=#c_literal{val=[big,unsigned]}}, - Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]}, - EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]}, - {Arg,St4} = new_var(St3), + #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,St5} = lc_guard_tests(Gs, St4), %These are always flat! - {Bc,Bps,St6} = bc_tq1(Line, E, Qs1, AccVar, St5), - {Nc,Nps,St7} = expr(NewMore, St6), - {Gc,Gps,St8} = safe(G, St7), %Will be a function argument! + {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, @@ -1066,7 +1061,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> guard=[], body=Nps ++ [Nc]}, #iclause{anno=LAnno, - pats=[#c_binary{anno=LA,segments=[TailSegment]},AccVar], + pats=[#c_binary{anno=LA,segments=TailSegList},AccVar], guard=[], body=[AccVar]}], Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, @@ -1074,7 +1069,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> body=Gps ++ [#iapply{anno=LAnno, op=#c_var{anno=LA,name={Name,2}}, args=[Gc,AccExpr]}]}, - [],St8}; + [],St}; bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) -> %% Special case sequences guard tests. LA = lineno_anno(Line, St0), @@ -1120,6 +1115,29 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> %%Anno = Anno0#a{anno=[compiler_generated|A]}, {set_anno(E, Anno),Pre,St}. +append_tail_segment(Segs, St) -> + app_tail_seg(Segs, St, []). + +app_tail_seg([#c_bitstr{val=Var0,size=#c_literal{val=all}}=Seg0]=L, + St0, Acc) -> + case Var0 of + #c_var{name='_'} -> + {Var,St} = new_var(St0), + Seg = Seg0#c_bitstr{val=Var}, + {reverse(Acc, [Seg]),Var,St}; + #c_var{} -> + {reverse(Acc, L),Var0,St0} + end; +app_tail_seg([H|T], St, Acc) -> + app_tail_seg(T, St, [H|Acc]); +app_tail_seg([], St0, Acc) -> + {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]}}, + {reverse(Acc, [Tail]),Var,St}. + emasculate_segments(Segs, St) -> emasculate_segments(Segs, St, []). |