diff options
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/beam_bsm.erl | 13 | ||||
-rw-r--r-- | lib/compiler/src/beam_jump.erl | 15 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 9 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 14 | ||||
-rw-r--r-- | lib/compiler/src/sys_core_bsm.erl | 62 |
5 files changed, 91 insertions, 22 deletions
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl index abc6e96c85..1c8e0e9854 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -310,18 +310,7 @@ btb_reaches_match_2([{test,bs_start_match2,{f,F},Live,[Bin,_],Ctx}|Is], end; btb_reaches_match_2([{test,_,{f,F},Ss}=I|Is], Regs, D0) -> btb_ensure_not_used(Ss, I, Regs), - D1 = btb_follow_branch(F, Regs, D0), - D = case Is of - [{bs_context_to_binary,_}|_] -> - %% bs_context_to_binary following a test instruction - %% probably needs the current position to be saved as - %% the new start position, but we can't be sure. - %% Therefore, conservatively disable the optimization - %% (instead of forcing a saving of the position). - D1#btb{must_save=true,must_not_save=true}; - _ -> - D1 - end, + D = btb_follow_branch(F, Regs, D0), btb_reaches_match_1(Is, Regs, D); btb_reaches_match_2([{test,_,{f,F},_,Ss,_}=I|Is], Regs, D0) -> btb_ensure_not_used(Ss, I, Regs), diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 9eee56d604..22974da398 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -128,7 +128,7 @@ %%% on the program state. %%% --import(lists, [reverse/1,reverse/2,foldl/3]). +-import(lists, [dropwhile/2,reverse/1,reverse/2,foldl/3]). -type instruction() :: beam_utils:instruction(). @@ -411,14 +411,19 @@ opt_useless_loads([{test,_,{f,L},_}=I|Is], L, St) -> opt_useless_loads(Is, _L, St) -> {Is,St}. -opt_useless_block_loads([{set,[Dst],_,_}=I|Is], L, Index) -> - BlockJump = [{block,Is},{jump,{f,L}}], +opt_useless_block_loads([{set,[Dst],_,_}=I|Is0], L, Index) -> + BlockJump = [{block,Is0},{jump,{f,L}}], case beam_utils:is_killed(Dst, BlockJump, Index) of true -> - %% The register is killed and not used, we can remove the load + %% The register is killed and not used, we can remove the load. + %% Remove any `put` instructions in case we just + %% removed a `put_tuple` instruction. + Is = dropwhile(fun({set,_,_,put}) -> true; + (_) -> false + end, Is0), opt_useless_block_loads(Is, L, Index); false -> - [I|opt_useless_block_loads(Is, L, Index)] + [I|opt_useless_block_loads(Is0, L, Index)] end; opt_useless_block_loads([I|Is], L, Index) -> [I|opt_useless_block_loads(Is, L, Index)]; diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 5580d2f123..6b2ab5a2a4 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -745,8 +745,11 @@ check_liveness_block_2(R, {gc_bif,Op,{f,Lbl}}, Ss, St) -> check_liveness_block_3(R, Lbl, {Op,length(Ss)}, St); check_liveness_block_2(R, {bif,Op,{f,Lbl}}, Ss, St) -> Arity = length(Ss), + + %% Note that is_function/2 is a type test but is not safe. case erl_internal:comp_op(Op, Arity) orelse - erl_internal:new_type_test(Op, Arity) of + (erl_internal:new_type_test(Op, Arity) andalso + erl_bifs:is_safe(erlang, Op, Arity)) of true -> {killed,St}; false -> @@ -1115,6 +1118,10 @@ defs([{bs_init,{f,L},_,Live,_,Dst}=I|Is], Regs0, D) -> end, Regs = def_regs([Dst], Regs1), [I|defs(Is, Regs, update_regs(L, Regs, D))]; +defs([{test,bs_start_match2,{f,L},Live,_,Dst}=I|Is], _Regs, D) -> + Regs0 = init_def_regs(Live), + Regs = def_regs([Dst], Regs0), + [I|defs(Is, Regs, update_regs(L, Regs0, D))]; defs([{bs_put,{f,L},_,_}=I|Is], Regs, D) -> [I|defs(Is, Regs, update_regs(L, Regs, D))]; defs([build_stacktrace=I|Is], _Regs, D) -> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 562d57a6ef..6510571441 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -931,11 +931,17 @@ parse_module(_Code, St0) -> end. do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) -> + SourceName0 = proplists:get_value(source, Opts, File), + SourceName = case member(deterministic, Opts) of + true -> filename:basename(SourceName0); + false -> SourceName0 + end, R = epp:parse_file(File, - [{includes,[".",Dir|inc_paths(Opts)]}, - {macros,pre_defs(Opts)}, - {default_encoding,DefEncoding}, - extra]), + [{includes,[".",Dir|inc_paths(Opts)]}, + {source_name, SourceName}, + {macros,pre_defs(Opts)}, + {default_encoding,DefEncoding}, + extra]), case R of {ok,Forms,Extra} -> Encoding = proplists:get_value(encoding, Extra), diff --git a/lib/compiler/src/sys_core_bsm.erl b/lib/compiler/src/sys_core_bsm.erl index d7b26c3a56..62657933ee 100644 --- a/lib/compiler/src/sys_core_bsm.erl +++ b/lib/compiler/src/sys_core_bsm.erl @@ -44,6 +44,14 @@ function([{#c_var{name={F,Arity}}=Name,B0}|Fs], FsAcc, Ws0) -> {B,Ws} -> function(Fs, [{Name,B}|FsAcc], Ws) catch + throw:unsafe_bs_context_to_binary -> + %% Unsafe bs_context_to_binary (in the sense that the + %% contents of the binary will probably be wrong). + %% Disable binary optimizations for the entire function. + %% We don't generate an INFO message, because this happens + %% very infrequently and it would be hard to explain in + %% a comprehensible way in an INFO message. + function(Fs, [{Name,B0}|FsAcc], Ws0); Class:Error:Stack -> io:fwrite("Function: ~w/~w\n", [F,Arity]), erlang:raise(Class, Error, Stack) @@ -116,12 +124,66 @@ move_from_col(Pos, L) -> [Col|First] ++ Rest. bsm_do_an([#c_var{name=Vname}=V0|Vs0], Cs0, Case) -> + bsm_inner_context_to_binary(Cs0), Cs = bsm_do_an_var(Vname, Cs0), V = bsm_annotate_for_reuse(V0), Vs = core_lib:make_values([V|Vs0]), Case#c_case{arg=Vs,clauses=Cs}; bsm_do_an(_Vs, _Cs, Case) -> Case. +bsm_inner_context_to_binary([#c_clause{body=B}|Cs]) -> + %% Consider: + %% + %% foo(<<Length, Data/binary>>) -> %Line 1 + %% case {Data, Length} of %Line 2 + %% {_, 0} -> Data; %Line 3 + %% {<<...>>, 4} -> ... %Line 4 + %% end. + %% + %% No sub binary will be created for Data in line 1. The match + %% context will be passed on to the `case` in line 2. In line 3, + %% this pass inserts a `bs_context_to_binary` instruction to + %% convert the match context representing Data to a binary before + %% returning it. The problem is that the binary created will be + %% the original binary (including the matched out Length field), + %% not the tail of the binary as it is supposed to be. + %% + %% Here follows a heuristic to disable the binary optimizations + %% for the entire function if this code kind of code is found. + + case cerl_trees:free_variables(B) of + [] -> + %% Since there are no free variables in the body of + %% this clause, there can't be any troublesome + %% bs_context_to_binary instructions. + bsm_inner_context_to_binary(Cs); + [_|_]=Free -> + %% One of the free variables could refer to a match context + %% created by the outer binary match. + F = fun(#c_primop{name=#c_literal{val=bs_context_to_binary}, + args=[#c_var{name=V}]}, _) -> + case member(V, Free) of + true -> + %% This bs_context_to_binary instruction will + %% make a binary of the match context from an + %% outer binary match. It is very likely that + %% the contents of the binary will be wrong + %% (the original binary as opposed to only + %% the tail binary). + throw(unsafe_bs_context_to_binary); + false -> + %% Safe. This bs_context_to_binary instruction + %% will make a binary from a match context + %% defined in the body of the clause. + ok + end; + (_, _) -> + ok + end, + cerl_trees:fold(F, ok, B) + end; +bsm_inner_context_to_binary([]) -> ok. + bsm_do_an_var(V, [#c_clause{pats=[P|_],guard=G,body=B0}=C0|Cs]) -> case P of #c_var{name=VarName} -> |