aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src')
-rw-r--r--lib/compiler/src/Makefile4
-rw-r--r--lib/compiler/src/beam_a.erl2
-rw-r--r--lib/compiler/src/beam_asm.erl15
-rw-r--r--lib/compiler/src/beam_block.erl599
-rw-r--r--lib/compiler/src/beam_bool.erl60
-rw-r--r--lib/compiler/src/beam_bs.erl278
-rw-r--r--lib/compiler/src/beam_bsm.erl38
-rw-r--r--lib/compiler/src/beam_clean.erl24
-rw-r--r--lib/compiler/src/beam_dead.erl70
-rw-r--r--lib/compiler/src/beam_dict.erl13
-rw-r--r--lib/compiler/src/beam_disasm.erl7
-rw-r--r--lib/compiler/src/beam_disasm.hrl8
-rw-r--r--lib/compiler/src/beam_except.erl10
-rw-r--r--lib/compiler/src/beam_flatten.erl3
-rw-r--r--lib/compiler/src/beam_jump.erl74
-rw-r--r--lib/compiler/src/beam_listing.erl2
-rw-r--r--lib/compiler/src/beam_peep.erl49
-rw-r--r--lib/compiler/src/beam_receive.erl5
-rw-r--r--lib/compiler/src/beam_reorder.erl148
-rw-r--r--lib/compiler/src/beam_split.erl11
-rw-r--r--lib/compiler/src/beam_trim.erl2
-rw-r--r--lib/compiler/src/beam_type.erl224
-rw-r--r--lib/compiler/src/beam_utils.erl147
-rw-r--r--lib/compiler/src/beam_validator.erl214
-rw-r--r--lib/compiler/src/beam_z.erl14
-rw-r--r--lib/compiler/src/cerl.erl58
-rw-r--r--lib/compiler/src/cerl_clauses.erl2
-rw-r--r--lib/compiler/src/cerl_inline.erl2
-rw-r--r--lib/compiler/src/cerl_trees.erl245
-rw-r--r--lib/compiler/src/compile.erl134
-rw-r--r--lib/compiler/src/compiler.app.src4
-rw-r--r--lib/compiler/src/compiler.appup.src2
-rw-r--r--lib/compiler/src/core_lib.erl40
-rw-r--r--lib/compiler/src/core_lint.erl4
-rw-r--r--lib/compiler/src/core_parse.hrl2
-rw-r--r--lib/compiler/src/core_parse.yrl63
-rw-r--r--lib/compiler/src/core_pp.erl170
-rw-r--r--lib/compiler/src/core_scan.erl2
-rw-r--r--lib/compiler/src/erl_bifs.erl2
-rwxr-xr-xlib/compiler/src/genop.tab12
-rw-r--r--lib/compiler/src/rec_env.erl188
-rw-r--r--lib/compiler/src/sys_core_dsetel.erl38
-rw-r--r--lib/compiler/src/sys_core_fold.erl302
-rw-r--r--lib/compiler/src/sys_core_inline.erl2
-rw-r--r--lib/compiler/src/sys_pre_attributes.erl2
-rw-r--r--lib/compiler/src/sys_pre_expand.erl179
-rw-r--r--lib/compiler/src/v3_codegen.erl183
-rw-r--r--lib/compiler/src/v3_core.erl169
-rw-r--r--lib/compiler/src/v3_kernel.erl33
-rw-r--r--lib/compiler/src/v3_kernel.hrl2
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl2
-rw-r--r--lib/compiler/src/v3_life.erl2
-rw-r--r--lib/compiler/src/v3_life.hrl2
53 files changed, 2391 insertions, 1476 deletions
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 299b2892fc..518c89d044 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2013. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ MODULES = \
beam_asm \
beam_block \
beam_bool \
+ beam_bs \
beam_bsm \
beam_clean \
beam_dead \
@@ -62,6 +63,7 @@ MODULES = \
beam_opcodes \
beam_peep \
beam_receive \
+ beam_reorder \
beam_split \
beam_trim \
beam_type \
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl
index f0f2ee08c2..91e6d80da3 100644
--- a/lib/compiler/src/beam_a.erl
+++ b/lib/compiler/src/beam_a.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index a3201b0f4a..f6ca7a0afb 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,11 +30,12 @@
module(Code, Abst, SourceFile, Opts) ->
{ok,assemble(Code, Abst, SourceFile, Opts)}.
-assemble({Mod,Exp,Attr0,Asm0,NumLabels}, Abst, SourceFile, Opts) ->
+assemble({Mod,Exp0,Attr0,Asm0,NumLabels}, Abst, SourceFile, Opts) ->
{1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
{0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0),
NumFuncs = length(Asm0),
{Asm,Attr} = on_load(Asm0, Attr0),
+ Exp = cerl_sets:from_list(Exp0),
{Code,Dict2} = assemble_1(Asm, Exp, Dict1, []),
build_file(Code, Attr, Dict2, NumLabels, NumFuncs, Abst, SourceFile, Opts).
@@ -61,7 +62,7 @@ insert_on_load_instruction(Is0, Entry) ->
Bef ++ [El,on_load|Is].
assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->
- Dict1 = case member({Name,Arity}, Exp) of
+ Dict1 = case cerl_sets:is_element({Name,Arity}, Exp) of
true ->
beam_dict:export(Name, Arity, Entry, Dict0);
false ->
@@ -224,10 +225,12 @@ flatten_imports(Imps) ->
list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)).
build_attributes(Opts, SourceFile, Attr, MD5) ->
+ Misc0 = case SourceFile of
+ [] -> [];
+ [_|_] -> [{source,SourceFile}]
+ end,
Misc = case member(slim, Opts) of
- false ->
- {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
- [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}];
+ false -> Misc0;
true -> []
end,
Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 2def3de7f3..6a35191f6e 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,14 +23,13 @@
-module(beam_block).
-export([module/2]).
--import(lists, [mapfoldl/3,reverse/1,reverse/2,foldl/3,member/2]).
--define(MAXREG, 1024).
+-import(lists, [reverse/1,reverse/2,foldl/3,member/2]).
-module({Mod,Exp,Attr,Fs0,Lc0}, _Opt) ->
- {Fs,Lc} = mapfoldl(fun function/2, Lc0, Fs0),
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = [function(F) || F <- Fs0],
{ok,{Mod,Exp,Attr,Fs,Lc}}.
-function({function,Name,Arity,CLabel,Is0}, Lc0) ->
+function({function,Name,Arity,CLabel,Is0}) ->
try
%% Collect basic blocks and optimize them.
Is1 = blockify(Is0),
@@ -40,11 +39,8 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) ->
Is5 = opt_blocks(Is4),
Is6 = beam_utils:delete_live_annos(Is5),
- %% Optimize bit syntax.
- {Is,Lc} = bsm_opt(Is6, Lc0),
-
%% Done.
- {{function,Name,Arity,CLabel,Is},Lc}
+ {function,Name,Arity,CLabel,Is6}
catch
Class:Error ->
Stack = erlang:get_stacktrace(),
@@ -62,56 +58,15 @@ blockify(Is) ->
blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
%% Useless instruction sequence.
blockify(Is, Acc);
-blockify([{test,is_atom,{f,Fail},[Reg]}=I|
- [{select,select_val,Reg,{f,Fail},
- [{atom,false},{f,_}=BrFalse,
- {atom,true}=AtomTrue,{f,_}=BrTrue]}|Is]=Is0],
- [{block,Bl}|_]=Acc) ->
- case is_last_bool(Bl, Reg) of
- false ->
- blockify(Is0, [I|Acc]);
- true ->
- %% The last instruction is a boolean operator/guard BIF that can't fail.
- %% We can convert the three-way branch to a two-way branch (eliminating
- %% the reference to the failure label).
- blockify(Is, [{jump,BrTrue},
- {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
- end;
-blockify([{test,is_atom,{f,Fail},[Reg]}=I|
- [{select,select_val,Reg,{f,Fail},
- [{atom,true}=AtomTrue,{f,_}=BrTrue,
- {atom,false},{f,_}=BrFalse]}|Is]=Is0],
- [{block,Bl}|_]=Acc) ->
- case is_last_bool(Bl, Reg) of
- false ->
- blockify(Is0, [I|Acc]);
- true ->
- blockify(Is, [{jump,BrTrue},
- {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
- end;
blockify([I|Is0]=IsAll, Acc) ->
- case is_bs_put(I) of
- true ->
- {BsPuts0,Is} = collect_bs_puts(IsAll),
- BsPuts = opt_bs_puts(BsPuts0),
- blockify(Is, reverse(BsPuts, Acc));
- false ->
- case collect(I) of
- error -> blockify(Is0, [I|Acc]);
- Instr when is_tuple(Instr) ->
- {Block,Is} = collect_block(IsAll),
- blockify(Is, [{block,Block}|Acc])
- end
+ case collect(I) of
+ error -> blockify(Is0, [I|Acc]);
+ Instr when is_tuple(Instr) ->
+ {Block,Is} = collect_block(IsAll),
+ blockify(Is, [{block,Block}|Acc])
end;
blockify([], Acc) -> reverse(Acc).
-is_last_bool([{set,[Reg],As,{bif,N,_}}], Reg) ->
- Ar = length(As),
- erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar)
- orelse erl_internal:bool_op(N, Ar);
-is_last_bool([_|Is], Reg) -> is_last_bool(Is, Reg);
-is_last_bool([], _) -> false.
-
collect_block(Is) ->
collect_block(Is, []).
@@ -126,7 +81,9 @@ collect_block([I|Is]=Is0, Acc) ->
case collect(I) of
error -> {reverse(Acc),Is0};
Instr -> collect_block(Is, [Instr|Acc])
- end.
+ end;
+collect_block([], Acc) ->
+ {reverse(Acc),[]}.
collect({allocate,N,R}) -> {set,[],[],{alloc,R,{nozero,N,0,[]}}};
collect({allocate_zero,N,R}) -> {set,[],[],{alloc,R,{zero,N,0,[]}}};
@@ -146,10 +103,10 @@ collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
collect(remove_message) -> {set,[],[],remove_message};
collect({put_map,F,Op,S,D,R,{list,Puts}}) ->
{set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}};
-collect({get_map_elements,F,S,{list,Gets}}) ->
- {Ss,Ds} = beam_utils:split_even(Gets),
- {set,Ds,[S|Ss],{get_map_elements,F}};
-collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
+collect({'catch'=Op,R,L}) ->
+ {set,[R],[],{try_catch,Op,L}};
+collect({'try'=Op,R,L}) ->
+ {set,[R],[],{try_catch,Op,L}};
collect(fclearerror) -> {set,[],[],fclearerror};
collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror};
collect({fmove,S,D}) -> {set,[D],[S],fmove};
@@ -183,7 +140,9 @@ opt_blocks([I|Is]) ->
opt_blocks([]) -> [].
opt_block(Is0) ->
- Is = find_fixpoint(fun opt/1, Is0),
+ Is = find_fixpoint(fun(Is) ->
+ opt_tuple_element(opt(Is))
+ end, Is0),
opt_alloc(Is).
find_fixpoint(OptFun, Is0) ->
@@ -193,14 +152,43 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocate instructions upwards in the instruction stream, in the
-%% hope of getting more possibilities for optimizing away moves later.
+%% Move allocate instructions upwards in the instruction stream
+%% (within the same block), in the hope of getting more possibilities
+%% for optimizing away moves later.
+%%
+%% For example, we can transform the following instructions:
+%%
+%% get_tuple_element x(1) Element => x(2)
+%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live
+%%
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 2 %% x(0) and x(1) are live
+%% get_tuple_element x(1) Element => x(2)
+%%
+%% NOTE: Since the beam_reorder pass has been run, it is no longer
+%% safe to assume that if x(N) is initialized, then all lower-numbered
+%% x registers are also initialized.
+%%
+%% For example, in general it is not safe to transform the following
+%% instructions:
+%%
+%% get_tuple_element x(0) Element => x(1)
+%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live
%%
-%% 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.
+%% to the following instructions:
+%%
+%% allocate_zero StackSize 3
+%% get_tuple_element x(0) Element => x(1)
+%%
+%% The transformation is safe if and only if x(1) has been
+%% initialized previously. Unfortunately, beam_reorder may have moved
+%% a get_tuple_element instruction so that x(1) is not always
+%% initialized when this code is reached. To find whether or not x(1)
+%% is initialized, we would need to analyze all code preceding these
+%% two instructions (across branches). Since we currently don't have
+%% any practical mechanism for doing that, we will have to
+%% conservatively assume that the transformation is unsafe.
move_allocates([{block,Bl0}|Is]) ->
Bl = move_allocates_1(reverse(Bl0), []),
@@ -209,38 +197,26 @@ 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),
- move_allocates_1(Is, Acc);
+move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) ->
+ case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of
+ {false,_} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,not_possible} ->
+ move_allocates_1(Is, [I|Acc0]);
+ {true,Live} when is_integer(Live) ->
+ A = {set,[],[],{alloc,Live,Info}},
+ move_allocates_1(Is, [A,I|Acc])
+ end;
move_allocates_1([I|Is], Acc) ->
move_allocates_1(Is, [I|Acc]);
-move_allocates_1([], Is) -> Is.
-
-move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) ->
- Live = Live0, % Assertion.
- Alloc = {alloc,Live,combine_alloc(Info0, Info)},
- move_allocates_2(Alloc, Is, Acc);
-move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) ->
- case alloc_may_pass(I) of
- false ->
- {Is0,[{set,[],[],Alloc0}|Acc]};
- true ->
- Alloc = {alloc,alloc_live_regs(I, Live),Info},
- move_allocates_2(Alloc, Is, [I|Acc])
- end;
-move_allocates_2(Alloc, [], Acc) ->
- {[],[{set,[],[],Alloc}|Acc]}.
+move_allocates_1([], Acc) -> Acc.
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
-alloc_may_pass({set,_,_,{get_map_elements,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
-combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
- {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
%% opt([Instruction]) -> [Instruction]
%% Optimize the instruction stream inside a basic block.
@@ -251,7 +227,7 @@ opt([{set,_,_,{line,_}}=Line1,
{set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
opt([Line2,I2,Line1,I1|Is]);
-opt([{set,Ds0,Ss,Op}|Is0]) ->
+opt([{set,Ds0,Ss,Op}|Is0]) ->
{Ds,Is} = opt_moves(Ds0, Is0),
[{set,Ds,Ss,Op}|opt(Is)];
opt([{'%live',_,_}=I|Is]) ->
@@ -277,78 +253,167 @@ opt_moves([X0,Y0], Is0) ->
not_possible -> {[X,Y0],Is2};
{X,_} -> {[X,Y0],Is2};
{Y,Is} -> {[X,Y],Is}
- end;
-opt_moves(Ds, Is) ->
- %% multiple destinations -> pass through
- {Ds,Is}.
-
+ end.
%% opt_move(Dest, [Instruction]) -> {UpdatedDest,[Instruction]} | not_possible
%% If there is a {move,Dest,FinalDest} instruction
%% in the instruction stream, remove the move instruction
%% and let FinalDest be the destination.
-%%
-%% For this optimization to be safe, we must be sure that
-%% Dest will not be referenced in any other by other instructions
-%% in the rest of the instruction stream. Not even the indirect
-%% reference by an instruction that may allocate (such as
-%% test_heap/2 or a GC Bif) is allowed.
opt_move(Dest, Is) ->
- opt_move_1(Dest, Is, ?MAXREG, []).
-
-opt_move_1(R, [{set,_,_,{alloc,Live,_}}|_]=Is, SafeRegs, Acc) when Live < SafeRegs ->
- %% Downgrade number of safe regs and rescan the instruction, as it most probably
- %% is a gc_bif instruction.
- opt_move_1(R, Is, Live, Acc);
-opt_move_1(R, [{set,[{x,X}=D],[R],move}|Is], SafeRegs, Acc) ->
- case X < SafeRegs andalso beam_utils:is_killed_block(R, Is) of
- true -> opt_move_2(D, Acc, Is);
- false -> not_possible
+ opt_move_1(Dest, Is, []).
+
+opt_move_1(R, [{set,[D],[R],move}|Is0], Acc) ->
+ %% Provided that the source register is killed by instructions
+ %% that follow, the optimization is safe.
+ case eliminate_use_of_from_reg(Is0, R, D, []) of
+ {yes,Is} -> opt_move_rev(D, Acc, Is);
+ no -> not_possible
end;
-opt_move_1(R, [{set,[D],[R],move}|Is], _SafeRegs, Acc) ->
- case beam_utils:is_killed_block(R, Is) of
- true -> opt_move_2(D, Acc, Is);
- false -> not_possible
+opt_move_1(_R, [{set,_,_,{alloc,_,_}}|_], _) ->
+ %% The optimization is either not possible or not safe.
+ %%
+ %% If R is an X register killed by allocation, the optimization is
+ %% not safe. On the other hand, if the X register is killed, there
+ %% will not follow a 'move' instruction with this X register as
+ %% the source.
+ %%
+ %% If R is a Y register, the optimization is still not safe
+ %% because the new target register is an X register that cannot
+ %% safely pass the alloc instruction.
+ not_possible;
+opt_move_1(R, [{set,_,_,_}=I|Is], Acc) ->
+ %% If the source register is either killed or used by this
+ %% instruction, the optimimization is not possible.
+ case is_killed_or_used(R, I) of
+ true -> not_possible;
+ false -> opt_move_1(R, Is, [I|Acc])
end;
-opt_move_1(R, [I|Is], SafeRegs, Acc) ->
- case is_transparent(R, I) of
- false -> not_possible;
- true -> opt_move_1(R, Is, SafeRegs, [I|Acc])
- end.
+opt_move_1(_, _, _) ->
+ not_possible.
+
+%% opt_tuple_element([Instruction]) -> [Instruction]
+%% If possible, move get_tuple_element instructions forward
+%% in the instruction stream to a move instruction, eliminating
+%% the move instruction. Example:
+%%
+%% get_tuple_element Tuple Pos Dst1
+%% ...
+%% move Dst1 Dst2
+%%
+%% This code may be possible to rewrite to:
+%%
+%% %%(Moved get_tuple_element instruction)
+%% ...
+%% get_tuple_element Tuple Pos Dst2
+%%
-%% Reverse the instructions, while checking that there are no instructions that
-%% would interfere with using the new destination register chosen.
+opt_tuple_element([{set,[D],[S],{get_tuple_element,_}}=I|Is0]) ->
+ case opt_tuple_element_1(Is0, I, {S,D}, []) of
+ no ->
+ [I|opt_tuple_element(Is0)];
+ {yes,Is} ->
+ opt_tuple_element(Is)
+ end;
+opt_tuple_element([I|Is]) ->
+ [I|opt_tuple_element(Is)];
+opt_tuple_element([]) -> [].
+
+opt_tuple_element_1([{set,_,_,{alloc,_,_}}|_], _, _, _) ->
+ no;
+opt_tuple_element_1([{set,_,_,{try_catch,_,_}}|_], _, _, _) ->
+ no;
+opt_tuple_element_1([{set,[D],[S],move}|Is0], I0, {_,S}, Acc) ->
+ case eliminate_use_of_from_reg(Is0, S, D, []) of
+ no ->
+ no;
+ {yes,Is} ->
+ {set,[S],Ss,Op} = I0,
+ I = {set,[D],Ss,Op},
+ {yes,reverse(Acc, [I|Is])}
+ end;
+opt_tuple_element_1([{set,Ds,Ss,_}=I|Is], MovedI, {S,D}=Regs, Acc) ->
+ case member(S, Ds) orelse member(D, Ss) of
+ true ->
+ no;
+ false ->
+ opt_tuple_element_1(Is, MovedI, Regs, [I|Acc])
+ end;
+opt_tuple_element_1(_, _, _, _) -> no.
-opt_move_2(D, [I|Is], Acc) ->
- case is_transparent(D, I) of
- false -> not_possible;
- true -> opt_move_2(D, Is, [I|Acc])
+%% Reverse the instructions, while checking that there are no
+%% instructions that would interfere with using the new destination
+%% register (D).
+
+opt_move_rev(D, [I|Is], Acc) ->
+ case is_killed_or_used(D, I) of
+ true -> not_possible;
+ false -> opt_move_rev(D, Is, [I|Acc])
+ end;
+opt_move_rev(D, [], Acc) -> {D,Acc}.
+
+%% is_killed_or_used(Register, {set,_,_,_}) -> bool()
+%% Test whether the register is used by the instruction.
+
+is_killed_or_used(R, {set,Ss,Ds,_}) ->
+ member(R, Ds) orelse member(R, Ss).
+
+%% eliminate_use_of_from_reg([Instruction], FromRegister, ToRegister, Acc) ->
+%% {yes,Is} | no
+%% Eliminate any use of FromRegister in the instruction sequence
+%% by replacing uses of FromRegister with ToRegister. If FromRegister
+%% is referenced by an allocation instruction, return 'no' to indicate
+%% that FromRegister is still used and that the optimization is not
+%% possible.
+
+eliminate_use_of_from_reg([{set,_,_,{alloc,Live,_}}|_]=Is0, {x,X}, _, Acc) ->
+ if
+ X < Live ->
+ no;
+ true ->
+ {yes,reverse(Acc, Is0)}
end;
-opt_move_2(D, [], Acc) -> {D,Acc}.
-
-%% is_transparent(Register, Instruction) -> true | false
-%% Returns true if Instruction does not in any way references Register
-%% (even indirectly by an allocation instruction).
-%% Returns false if Instruction does reference Register, or we are
-%% not sure.
-
-is_transparent({x,X}, {set,_,_,{alloc,Live,_}}) when X < Live ->
- false;
-is_transparent(R, {set,Ds,Ss,_Op}) ->
- case member(R, Ds) of
- true -> false;
- false -> not member(R, Ss)
+eliminate_use_of_from_reg([{set,Ds,Ss0,Op}=I0|Is], From, To, Acc) ->
+ I = case member(From, Ss0) of
+ true ->
+ Ss = [case S of
+ From -> To;
+ _ -> S
+ end || S <- Ss0],
+ {set,Ds,Ss,Op};
+ false ->
+ I0
+ end,
+ case member(From, Ds) of
+ true ->
+ {yes,reverse(Acc, [I|Is])};
+ false ->
+ eliminate_use_of_from_reg(Is, From, To, [I|Acc])
end;
-is_transparent(_, _) -> false.
+eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
+ case beam_utils:is_killed_block(From, [I]) of
+ true ->
+ {yes,reverse(Acc, Is)};
+ false ->
+ no
+ end.
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
+opt_alloc([{set,[],[],{alloc,Live0,Info0}},
+ {set,[],[],{alloc,Live,Info}}|Is]) ->
+ Live = Live0, %Assertion.
+ Alloc = combine_alloc(Info0, Info),
+ I = {set,[],[],{alloc,Live,Alloc}},
+ opt_alloc([I|Is]);
opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
- [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|opt(Is)];
+ [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
opt_alloc([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
@@ -397,13 +462,14 @@ count_ones(Bits, Acc) ->
alloc_live_regs({set,Ds,Ss,_}, Regs0) ->
Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
- live_regs(Rset).
-
-live_regs(Regs) ->
- live_regs_1(0, Regs).
+ live_regs(0, Rset).
-live_regs_1(N, 0) -> N;
-live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+live_regs(N, 0) ->
+ N;
+live_regs(N, Regs) when Regs band 1 =:= 1 ->
+ live_regs(N+1, Regs bsr 1);
+live_regs(_, _) ->
+ not_possible.
x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
@@ -412,234 +478,3 @@ x_dead([], Regs) -> Regs.
x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N));
x_live([_|Rs], Regs) -> x_live(Rs, Regs);
x_live([], Regs) -> Regs.
-
-%%%
-%%% Evaluation of constant bit fields.
-%%%
-
-is_bs_put({bs_put,_,{bs_put_integer,_,_},_}) -> true;
-is_bs_put({bs_put,_,{bs_put_float,_,_},_}) -> true;
-is_bs_put(_) -> false.
-
-collect_bs_puts(Is) ->
- collect_bs_puts_1(Is, []).
-
-collect_bs_puts_1([I|Is]=Is0, Acc) ->
- case is_bs_put(I) of
- false -> {reverse(Acc),Is0};
- true -> collect_bs_puts_1(Is, [I|Acc])
- end.
-
-opt_bs_puts(Is) ->
- opt_bs_1(Is, []).
-
-opt_bs_1([{bs_put,Fail,
- {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) ->
- try eval_put_float(Src, Sz, Flags0) of
- <<Int:Sz>> ->
- Flags = force_big(Flags0),
- I = {bs_put,Fail,{bs_put_integer,1,Flags},
- [{integer,Sz},{integer,Int}]},
- opt_bs_1([I|Is], Acc)
- catch
- error:_ ->
- opt_bs_1(Is, [I0|Acc])
- end;
-opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{integer,_}]}|_]=IsAll,
- Acc0) ->
- {Is,Acc} = bs_collect_string(IsAll, Acc0),
- opt_bs_1(Is, Acc);
-opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0],
- Acc) when Sz > 8 ->
- case field_endian(F) of
- big ->
- %% We can do this optimization for any field size without risk
- %% for code explosion.
- case bs_split_int(N, Sz, Fail, Is0) of
- no_split -> opt_bs_1(Is0, [I|Acc]);
- Is -> opt_bs_1(Is, Acc)
- end;
- little when Sz < 128 ->
- %% We only try to optimize relatively small fields, to avoid
- %% an explosion in code size.
- <<Int:Sz>> = <<N:Sz/little>>,
- Flags = force_big(F),
- Is = [{bs_put,Fail,{bs_put_integer,1,Flags},
- [{integer,Sz},{integer,Int}]}|Is0],
- opt_bs_1(Is, Acc);
- _ -> %native or too wide little field
- opt_bs_1(Is0, [I|Acc])
- end;
-opt_bs_1([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 ->
- opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc);
-opt_bs_1([I|Is], Acc) ->
- opt_bs_1(Is, [I|Acc]);
-opt_bs_1([], Acc) -> reverse(Acc).
-
-eval_put_float(Src, Sz, Flags) when Sz =< 256 -> %Only evaluate if Sz is reasonable.
- Val = value(Src),
- case field_endian(Flags) of
- little -> <<Val:Sz/little-float-unit:1>>;
- big -> <<Val:Sz/big-float-unit:1>>
- %% native intentionally not handled here - we can't optimize it.
- end.
-
-value({integer,I}) -> I;
-value({float,F}) -> F.
-
-bs_collect_string(Is, [{bs_put,_,{bs_put_string,Len,{string,Str}},[]}|Acc]) ->
- bs_coll_str_1(Is, Len, reverse(Str), Acc);
-bs_collect_string(Is, Acc) ->
- bs_coll_str_1(Is, 0, [], Acc).
-
-bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{integer,V}]}|Is],
- Len, StrAcc, IsAcc) when U*Sz =:= 8 ->
- Byte = V band 16#FF,
- bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc);
-bs_coll_str_1(Is, Len, StrAcc, IsAcc) ->
- {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}.
-
-field_endian({field_flags,F}) -> field_endian_1(F).
-
-field_endian_1([big=E|_]) -> E;
-field_endian_1([little=E|_]) -> E;
-field_endian_1([native=E|_]) -> E;
-field_endian_1([_|Fs]) -> field_endian_1(Fs).
-
-force_big({field_flags,F}) ->
- {field_flags,force_big_1(F)}.
-
-force_big_1([big|_]=Fs) -> Fs;
-force_big_1([little|Fs]) -> [big|Fs];
-force_big_1([F|Fs]) -> [F|force_big_1(Fs)].
-
-bs_split_int(0, Sz, _, _) when Sz > 64 ->
- %% We don't want to split in this case because the
- %% string will consist of only zeroes.
- no_split;
-bs_split_int(-1, Sz, _, _) when Sz > 64 ->
- %% We don't want to split in this case because the
- %% string will consist of only 255 bytes.
- no_split;
-bs_split_int(N, Sz, Fail, Acc) ->
- FirstByteSz = case Sz rem 8 of
- 0 -> 8;
- Rem -> Rem
- end,
- bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
-
-bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 ->
- I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
- [{integer,Sz},{integer,-1}]},
- [I|Acc];
-bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 ->
- I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
- [{integer,Sz},{integer,0}]},
- [I|Acc];
-bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 ->
- Mask = (1 bsl ByteSz) - 1,
- I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
- [{integer,ByteSz},{integer,N band Mask}]},
- bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
-bs_split_int_1(_, _, _, _, Acc) -> Acc.
-
-
-%%%
-%%% Optimization of new bit syntax matching: get rid
-%%% of redundant bs_restore2/2 instructions across select_val
-%%% instructions, as well as a few other simple peep-hole optimizations.
-%%%
-
-bsm_opt(Is0, Lc0) ->
- {Is1,D0,Lc} = bsm_scan(Is0, [], Lc0, []),
- Is2 = case D0 of
- [] ->
- Is1;
- _ ->
- D = gb_trees:from_orddict(orddict:from_list(D0)),
- bsm_reroute(Is1, D, none, [])
- end,
- Is = beam_clean:bs_clean_saves(Is2),
- {bsm_opt_2(Is, []),Lc}.
-
-bsm_scan([{label,L}=Lbl,{bs_restore2,_,Save}=R|Is], D0, Lc, Acc0) ->
- D = [{{L,Save},Lc}|D0],
- Acc = [{label,Lc},R,Lbl|Acc0],
- bsm_scan(Is, D, Lc+1, Acc);
-bsm_scan([I|Is], D, Lc, Acc) ->
- bsm_scan(Is, D, Lc, [I|Acc]);
-bsm_scan([], D, Lc, Acc) ->
- {reverse(Acc),D,Lc}.
-
-bsm_reroute([{bs_save2,Reg,Save}=I|Is], D, _, Acc) ->
- bsm_reroute(Is, D, {Reg,Save}, [I|Acc]);
-bsm_reroute([{bs_restore2,Reg,Save}=I|Is], D, _, Acc) ->
- bsm_reroute(Is, D, {Reg,Save}, [I|Acc]);
-bsm_reroute([{label,_}=I|Is], D, S, Acc) ->
- bsm_reroute(Is, D, S, [I|Acc]);
-bsm_reroute([{select,select_val,Reg,F0,Lbls0}|Is], D, {_,Save}=S, Acc0) ->
- [F|Lbls] = bsm_subst_labels([F0|Lbls0], Save, D),
- Acc = [{select,select_val,Reg,F,Lbls}|Acc0],
- bsm_reroute(Is, D, S, Acc);
-bsm_reroute([{test,TestOp,F0,TestArgs}=I|Is], D, {_,Save}=S, Acc0) ->
- F = bsm_subst_label(F0, Save, D),
- Acc = [{test,TestOp,F,TestArgs}|Acc0],
- case bsm_not_bs_test(I) of
- true ->
- %% The test instruction will not update the bit offset for the
- %% binary being matched. Therefore the save position can be kept.
- bsm_reroute(Is, D, S, Acc);
- false ->
- %% The test instruction might update the bit offset. Kill our
- %% remembered Save position.
- bsm_reroute(Is, D, none, Acc)
- end;
-bsm_reroute([{test,TestOp,F0,Live,TestArgs,Dst}|Is], D, {_,Save}, Acc0) ->
- F = bsm_subst_label(F0, Save, D),
- Acc = [{test,TestOp,F,Live,TestArgs,Dst}|Acc0],
- %% The test instruction will update the bit offset. Kill our
- %% remembered Save position.
- bsm_reroute(Is, D, none, Acc);
-bsm_reroute([{block,[{set,[],[],{alloc,_,_}}]}=Bl,
- {bs_context_to_binary,_}=I|Is], D, S, Acc) ->
- %% To help further bit syntax optimizations.
- bsm_reroute([I,Bl|Is], D, S, Acc);
-bsm_reroute([I|Is], D, _, Acc) ->
- bsm_reroute(Is, D, none, [I|Acc]);
-bsm_reroute([], _, _, Acc) -> reverse(Acc).
-
-bsm_opt_2([{test,bs_test_tail2,F,[Ctx,Bits]}|Is],
- [{test,bs_skip_bits2,F,[Ctx,{integer,I},Unit,_Flags]}|Acc]) ->
- bsm_opt_2(Is, [{test,bs_test_tail2,F,[Ctx,Bits+I*Unit]}|Acc]);
-bsm_opt_2([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,_]}|Is],
- [{test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,Flags]}|Acc]) ->
- bsm_opt_2(Is, [{test,bs_skip_bits2,F,
- [Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
-bsm_opt_2([I|Is], Acc) ->
- bsm_opt_2(Is, [I|Acc]);
-bsm_opt_2([], Acc) -> reverse(Acc).
-
-%% bsm_not_bs_test({test,Name,_,Operands}) -> true|false.
-%% Test whether is the test is a "safe", i.e. does not move the
-%% bit offset for a binary.
-%%
-%% 'true' means that the test is safe, 'false' that we don't know or
-%% that the test moves the offset (e.g. bs_get_integer2).
-
-bsm_not_bs_test({test,bs_test_tail2,_,[_,_]}) -> true;
-bsm_not_bs_test(Test) -> beam_utils:is_pure_test(Test).
-
-bsm_subst_labels(Fs, Save, D) ->
- bsm_subst_labels_1(Fs, Save, D, []).
-
-bsm_subst_labels_1([F|Fs], Save, D, Acc) ->
- bsm_subst_labels_1(Fs, Save, D, [bsm_subst_label(F, Save, D)|Acc]);
-bsm_subst_labels_1([], _, _, Acc) ->
- reverse(Acc).
-
-bsm_subst_label({f,Lbl0}=F, Save, D) ->
- case gb_trees:lookup({Lbl0,Save}, D) of
- {value,Lbl} -> {f,Lbl};
- none -> F
- end;
-bsm_subst_label(Other, _, _) -> Other.
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 14b6381230..99e4ccb1e9 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,8 +25,6 @@
-import(lists, [reverse/1,reverse/2,foldl/3,mapfoldl/3,map/2]).
--define(MAXREG, 1024).
-
-record(st,
{next, %Next label number.
ll %Live regs at labels.
@@ -142,11 +140,6 @@ bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
throw:not_boolean_expr ->
failed;
- %% The block contains a 'move' instruction that could
- %% not be handled.
- throw:move ->
- failed;
-
%% The optimization is not safe. (A register
%% used by the instructions following the
%% optimized code is either not assigned a
@@ -215,37 +208,14 @@ ensure_opt_safe(Bl, NewCode, OldIs, Fail, PrecedingCode, St) ->
false -> throw(all_registers_not_killed);
true -> ok
end,
- Same = assigned_same_value(Bl, NewCode),
MustBeUnused = ordsets:subtract(ordsets:union(NotSet, NewDst),
- ordsets:union(MustBeKilled, Same)),
+ MustBeKilled),
case none_used(MustBeUnused, OldIs, Fail, St) of
false -> throw(registers_used);
true -> ok
end,
ok.
-%% assigned_same_value(OldCode, NewCodeReversed) -> [DestinationRegs]
-%% Return an ordset with a list of all y registers that are always
-%% assigned the same value in the old and new code. Currently, we
-%% are very conservative in that we only consider identical move
-%% instructions in the same order.
-%%
-assigned_same_value(Old, New) ->
- case reverse(New) of
- [{block,Bl}|_] ->
- assigned_same_value(Old, Bl, []);
- _ ->
- ordsets:new()
- end.
-
-assigned_same_value([{set,[{y,_}=D],[S],move}|T1],
- [{set,[{y,_}=D],[S],move}|T2], Acc) ->
- assigned_same_value(T1, T2, [D|Acc]);
-assigned_same_value(_, _, Acc) ->
- ordsets:from_list(Acc).
-
-update_fail_label([{set,_,_,move}=I|Is], Fail, Acc) ->
- update_fail_label(Is, Fail, [I|Acc]);
update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
update_fail_label([{set,Ds,As,{alloc,Regs,{gc_bif,N,{f,_}}}}|Is], Fail, Acc) ->
@@ -268,9 +238,9 @@ extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]) ->
end;
extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}.
-extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
+extend_block_1([{set,[{x,_}],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
extend_block_1(Is, Fail, [I|Acc]);
-extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
+extend_block_1([{set,[{x,_}],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
case safe_bool_op(Bif, length(As)) of
false -> {Acc,reverse(Is0)};
true -> extend_block_1(Is, Fail, [I|Acc])
@@ -314,8 +284,6 @@ split_block_1(Is, Fail, ProhibitFailLabel) ->
end
end.
-split_block_2([{set,_,_,move}=I|Is], Fail, Acc) ->
- split_block_2(Is, Fail, [I|Acc]);
split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
split_block_2(Is, Fail, [I|Acc]);
split_block_2([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}=I|Is], Fail, Acc) ->
@@ -343,8 +311,8 @@ dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
dst_regs(Is, [D|Acc]);
dst_regs([{set,[D],_,{alloc,_,{gc_bif,_,{f,_}}}}|Is], Acc) ->
dst_regs(Is, [D|Acc]);
-dst_regs([{set,[D],_,move}|Is], Acc) ->
- dst_regs(Is, [D|Acc]);
+dst_regs([{protected,_,Bl,_}|Is], Acc) ->
+ dst_regs(Bl, dst_regs(Is, Acc));
dst_regs([_|Is], Acc) ->
dst_regs(Is, Acc);
dst_regs([], Acc) -> ordsets:from_list(Acc).
@@ -411,13 +379,6 @@ bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) ->
_Res ->
throw(not_boolean_expr)
end;
-bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) ->
- case {Src,Dst} of
- {{tmp,_},_} -> throw(move);
- {_,{tmp,_}} -> throw(move);
- _ -> ok
- end,
- bopt_tree(Is, Forest, [Move|Pre]);
bopt_tree([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
Ar = length(As),
case safe_bool_op(N, Ar) of
@@ -589,10 +550,6 @@ free_variables(Is) ->
E = gb_sets:empty(),
free_vars_1(Is, E, E, E).
-free_vars_1([{set,Ds,As,move}|Is], F0, N0, A) ->
- F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
- N = gb_sets:union(N0, var_list(Ds)),
- free_vars_1(Is, F, N, A);
free_vars_1([{set,Ds,As,{bif,_,_}}|Is], F0, N0, A) ->
F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
N = gb_sets:union(N0, var_list(Ds)),
@@ -632,8 +589,6 @@ free_vars_regs(X) -> [{x,X-1}|free_vars_regs(X-1)].
rename_regs(Is, Regs) ->
rename_regs(Is, Regs, []).
-rename_regs([{set,_,_,move}=I|Is], Regs, Acc) ->
- rename_regs(Is, Regs, [I|Acc]);
rename_regs([{set,[Dst0],Ss0,{alloc,_,Info}}|Is], Regs0, Acc) ->
Live = live_regs(Regs0),
Ss = rename_sources(Ss0, Regs0),
@@ -737,8 +692,7 @@ ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
Sub1 = gb_trees:update(R, NewReg, Sub0),
Sub = gb_trees:insert(NewReg, NewReg, Sub1),
Ssa#ssa{sub=Sub}
- end;
-ssa_assign(_, Ssa) -> Ssa.
+ end.
ssa_sub_list(List, Sub) ->
[ssa_sub(E, Sub) || E <- List].
diff --git a/lib/compiler/src/beam_bs.erl b/lib/compiler/src/beam_bs.erl
new file mode 100644
index 0000000000..2aed98d4e7
--- /dev/null
+++ b/lib/compiler/src/beam_bs.erl
@@ -0,0 +1,278 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Purpose : Partitions assembly instructions into basic blocks and
+%% optimizes them.
+
+-module(beam_bs).
+
+-export([module/2]).
+-import(lists, [mapfoldl/3,reverse/1]).
+
+module({Mod,Exp,Attr,Fs0,Lc0}, _Opt) ->
+ {Fs,Lc} = mapfoldl(fun function/2, Lc0, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}, Lc0) ->
+ try
+ Is1 = bs_put_opt(Is0),
+ {Is,Lc} = bsm_opt(Is1, Lc0),
+ {{function,Name,Arity,CLabel,Is},Lc}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+%%%
+%%% Evaluation of constant bit fields.
+%%%
+
+bs_put_opt([{bs_put,_,_,_}=I|Is0]) ->
+ {BsPuts0,Is} = collect_bs_puts(Is0, [I]),
+ BsPuts = opt_bs_puts(BsPuts0),
+ BsPuts ++ bs_put_opt(Is);
+bs_put_opt([I|Is]) ->
+ [I|bs_put_opt(Is)];
+bs_put_opt([]) -> [].
+
+collect_bs_puts([{bs_put,_,_,_}=I|Is], Acc) ->
+ collect_bs_puts(Is, [I|Acc]);
+collect_bs_puts([_|_]=Is, Acc) ->
+ {reverse(Acc),Is}.
+
+opt_bs_puts(Is) ->
+ opt_bs_1(Is, []).
+
+opt_bs_1([{bs_put,Fail,
+ {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) ->
+ try eval_put_float(Src, Sz, Flags0) of
+ <<Int:Sz>> ->
+ Flags = force_big(Flags0),
+ I = {bs_put,Fail,{bs_put_integer,1,Flags},
+ [{integer,Sz},{integer,Int}]},
+ opt_bs_1([I|Is], Acc)
+ catch
+ error:_ ->
+ opt_bs_1(Is, [I0|Acc])
+ end;
+opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{integer,_}]}|_]=IsAll,
+ Acc0) ->
+ {Is,Acc} = bs_collect_string(IsAll, Acc0),
+ opt_bs_1(Is, Acc);
+opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0],
+ Acc) when Sz > 8 ->
+ case field_endian(F) of
+ big ->
+ %% We can do this optimization for any field size without
+ %% risk for code explosion.
+ case bs_split_int(N, Sz, Fail, Is0) of
+ no_split -> opt_bs_1(Is0, [I|Acc]);
+ Is -> opt_bs_1(Is, Acc)
+ end;
+ little when Sz < 128 ->
+ %% We only try to optimize relatively small fields, to
+ %% avoid an explosion in code size.
+ <<Int:Sz>> = <<N:Sz/little>>,
+ Flags = force_big(F),
+ Is = [{bs_put,Fail,{bs_put_integer,1,Flags},
+ [{integer,Sz},{integer,Int}]}|Is0],
+ opt_bs_1(Is, Acc);
+ _ -> %native or too wide little field
+ opt_bs_1(Is0, [I|Acc])
+ end;
+opt_bs_1([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 ->
+ opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc);
+opt_bs_1([I|Is], Acc) ->
+ opt_bs_1(Is, [I|Acc]);
+opt_bs_1([], Acc) -> reverse(Acc).
+
+eval_put_float(Src, Sz, Flags) when Sz =< 256 ->
+ %%Only evaluate if Sz is reasonable.
+ Val = value(Src),
+ case field_endian(Flags) of
+ little -> <<Val:Sz/little-float-unit:1>>;
+ big -> <<Val:Sz/big-float-unit:1>>
+ %% native intentionally not handled here - we can't optimize
+ %% it.
+ end.
+
+value({integer,I}) -> I;
+value({float,F}) -> F.
+
+bs_collect_string(Is, [{bs_put,_,{bs_put_string,Len,{string,Str}},[]}|Acc]) ->
+ bs_coll_str_1(Is, Len, reverse(Str), Acc);
+bs_collect_string(Is, Acc) ->
+ bs_coll_str_1(Is, 0, [], Acc).
+
+bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{integer,V}]}|Is],
+ Len, StrAcc, IsAcc) when U*Sz =:= 8 ->
+ Byte = V band 16#FF,
+ bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc);
+bs_coll_str_1(Is, Len, StrAcc, IsAcc) ->
+ {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}.
+
+field_endian({field_flags,F}) -> field_endian_1(F).
+
+field_endian_1([big=E|_]) -> E;
+field_endian_1([little=E|_]) -> E;
+field_endian_1([native=E|_]) -> E;
+field_endian_1([_|Fs]) -> field_endian_1(Fs).
+
+force_big({field_flags,F}) ->
+ {field_flags,force_big_1(F)}.
+
+force_big_1([big|_]=Fs) -> Fs;
+force_big_1([little|Fs]) -> [big|Fs];
+force_big_1([F|Fs]) -> [F|force_big_1(Fs)].
+
+bs_split_int(0, Sz, _, _) when Sz > 64 ->
+ %% We don't want to split in this case because the
+ %% string will consist of only zeroes.
+ no_split;
+bs_split_int(-1, Sz, _, _) when Sz > 64 ->
+ %% We don't want to split in this case because the
+ %% string will consist of only 255 bytes.
+ no_split;
+bs_split_int(N, Sz, Fail, Acc) ->
+ FirstByteSz = case Sz rem 8 of
+ 0 -> 8;
+ Rem -> Rem
+ end,
+ bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
+
+bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 ->
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,Sz},{integer,-1}]},
+ [I|Acc];
+bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 ->
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,Sz},{integer,0}]},
+ [I|Acc];
+bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 ->
+ Mask = (1 bsl ByteSz) - 1,
+ I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}},
+ [{integer,ByteSz},{integer,N band Mask}]},
+ bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
+bs_split_int_1(_, _, _, _, Acc) -> Acc.
+
+%%%
+%%% Optimization of bit syntax matching: get rid
+%%% of redundant bs_restore2/2 instructions across select_val
+%%% instructions, as well as a few other simple peep-hole
+%%% optimizations.
+%%%
+
+bsm_opt(Is0, Lc0) ->
+ {Is1,D0,Lc} = bsm_scan(Is0, [], Lc0, []),
+ Is2 = case D0 of
+ [] ->
+ %% No bit syntax matching in this function.
+ Is1;
+ [_|_] ->
+ %% Optimize the bit syntax matching.
+ D = gb_trees:from_orddict(orddict:from_list(D0)),
+ bsm_reroute(Is1, D, none, [])
+ end,
+ Is = beam_clean:bs_clean_saves(Is2),
+ {bsm_opt_2(Is, []),Lc}.
+
+bsm_scan([{label,L}=Lbl,{bs_restore2,_,Save}=R|Is], D0, Lc, Acc0) ->
+ D = [{{L,Save},Lc}|D0],
+ Acc = [{label,Lc},R,Lbl|Acc0],
+ bsm_scan(Is, D, Lc+1, Acc);
+bsm_scan([I|Is], D, Lc, Acc) ->
+ bsm_scan(Is, D, Lc, [I|Acc]);
+bsm_scan([], D, Lc, Acc) ->
+ {reverse(Acc),D,Lc}.
+
+bsm_reroute([{bs_save2,Reg,Save}=I|Is], D, _, Acc) ->
+ bsm_reroute(Is, D, {Reg,Save}, [I|Acc]);
+bsm_reroute([{bs_restore2,Reg,Save}=I|Is], D, _, Acc) ->
+ bsm_reroute(Is, D, {Reg,Save}, [I|Acc]);
+bsm_reroute([{label,_}=I|Is], D, S, Acc) ->
+ bsm_reroute(Is, D, S, [I|Acc]);
+bsm_reroute([{select,select_val,Reg,F0,Lbls0}|Is], D, {_,Save}=S, Acc0) ->
+ [F|Lbls] = bsm_subst_labels([F0|Lbls0], Save, D),
+ Acc = [{select,select_val,Reg,F,Lbls}|Acc0],
+ bsm_reroute(Is, D, S, Acc);
+bsm_reroute([{test,TestOp,F0,TestArgs}=I|Is], D, {_,Save}=S, Acc0) ->
+ F = bsm_subst_label(F0, Save, D),
+ Acc = [{test,TestOp,F,TestArgs}|Acc0],
+ case bsm_not_bs_test(I) of
+ true ->
+ %% The test instruction will not update the bit offset for
+ %% the binary being matched. Therefore the save position
+ %% can be kept.
+ bsm_reroute(Is, D, S, Acc);
+ false ->
+ %% The test instruction might update the bit offset. Kill
+ %% our remembered Save position.
+ bsm_reroute(Is, D, none, Acc)
+ end;
+bsm_reroute([{test,TestOp,F0,Live,TestArgs,Dst}|Is], D, {_,Save}, Acc0) ->
+ F = bsm_subst_label(F0, Save, D),
+ Acc = [{test,TestOp,F,Live,TestArgs,Dst}|Acc0],
+ %% The test instruction will update the bit offset. Kill our
+ %% remembered Save position.
+ bsm_reroute(Is, D, none, Acc);
+bsm_reroute([{block,[{set,[],[],{alloc,_,_}}]}=Bl,
+ {bs_context_to_binary,_}=I|Is], D, S, Acc) ->
+ %% To help further bit syntax optimizations.
+ bsm_reroute([I,Bl|Is], D, S, Acc);
+bsm_reroute([I|Is], D, _, Acc) ->
+ bsm_reroute(Is, D, none, [I|Acc]);
+bsm_reroute([], _, _, Acc) -> reverse(Acc).
+
+bsm_opt_2([{test,bs_test_tail2,F,[Ctx,Bits]}|Is],
+ [{test,bs_skip_bits2,F,[Ctx,{integer,I},Unit,_Flags]}|Acc]) ->
+ bsm_opt_2(Is, [{test,bs_test_tail2,F,[Ctx,Bits+I*Unit]}|Acc]);
+bsm_opt_2([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,_]}|Is],
+ [{test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,Flags]}|Acc]) ->
+ bsm_opt_2(Is, [{test,bs_skip_bits2,F,
+ [Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
+bsm_opt_2([I|Is], Acc) ->
+ bsm_opt_2(Is, [I|Acc]);
+bsm_opt_2([], Acc) -> reverse(Acc).
+
+%% bsm_not_bs_test({test,Name,_,Operands}) -> true|false.
+%% Test whether is the test is a "safe", i.e. does not move the
+%% bit offset for a binary.
+%%
+%% 'true' means that the test is safe, 'false' that we don't know or
+%% that the test moves the offset (e.g. bs_get_integer2).
+
+bsm_not_bs_test({test,bs_test_tail2,_,[_,_]}) -> true;
+bsm_not_bs_test(Test) -> beam_utils:is_pure_test(Test).
+
+bsm_subst_labels(Fs, Save, D) ->
+ bsm_subst_labels_1(Fs, Save, D, []).
+
+bsm_subst_labels_1([F|Fs], Save, D, Acc) ->
+ bsm_subst_labels_1(Fs, Save, D, [bsm_subst_label(F, Save, D)|Acc]);
+bsm_subst_labels_1([], _, _, Acc) ->
+ reverse(Acc).
+
+bsm_subst_label({f,Lbl0}=F, Save, D) ->
+ case gb_trees:lookup({Lbl0,Save}, D) of
+ {value,Lbl} -> {f,Lbl};
+ none -> F
+ end;
+bsm_subst_label(Other, _, _) -> Other.
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index 4f76350269..ae1b34ba49 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -205,8 +205,15 @@ btb_reaches_match_1(Is, Regs, D) ->
btb_reaches_match_2([{block,Bl}|Is], Regs0, D) ->
Regs = btb_reaches_match_block(Bl, Regs0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
- btb_call(Arity, Lbl, Regs, Is, D);
+btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs0, D) ->
+ case is_tail_call(Is) of
+ true ->
+ Regs1 = btb_kill_not_live(Arity, Regs0),
+ Regs = btb_kill_yregs(Regs1),
+ btb_tail_call(Lbl, Regs, D);
+ false ->
+ btb_call(Arity, Lbl, Regs0, Is, D)
+ end;
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) ->
@@ -360,6 +367,10 @@ btb_reaches_match_2([{line,_}|Is], Regs, D) ->
btb_reaches_match_2([I|_], Regs, _) ->
btb_error({btb_context_regs(Regs),I,not_handled}).
+is_tail_call([{deallocate,_}|_]) -> true;
+is_tail_call([return|_]) -> true;
+is_tail_call(_) -> false.
+
btb_call(Arity, Lbl, Regs0, Is, D0) ->
Regs = btb_kill_not_live(Arity, Regs0),
case btb_are_x_registers_empty(Regs) of
@@ -369,15 +380,15 @@ btb_call(Arity, Lbl, Regs0, Is, D0) ->
D = btb_tail_call(Lbl, Regs, D0),
%% No problem so far (the called function can handle a
- %% match context). Now we must make sure that the rest
- %% of this function following the call does not attempt
- %% to use the match context in case there is a copy
- %% tucked away in a y register.
+ %% match context). Now we must make sure that we don't
+ %% have any copies of the match context tucked away in an
+ %% y register.
RegList = btb_context_regs(Regs),
- YRegs = [R || {y,_}=R <- RegList],
- case btb_are_all_unused(YRegs, Is, D) of
- true -> D;
- false -> btb_error({multiple_uses,RegList})
+ case [R || {y,_}=R <- RegList] of
+ [] ->
+ D;
+ [_|_] ->
+ btb_error({multiple_uses,RegList})
end;
true ->
%% No match context in any x register. It could have been
@@ -421,7 +432,8 @@ btb_follow_branches([], _, D) -> D.
btb_follow_branch(0, _Regs, D) -> D;
btb_follow_branch(Lbl, Regs, #btb{ok_br=Br0,index=Li}=D) ->
- case gb_sets:is_member(Lbl, Br0) of
+ Key = {Lbl,Regs},
+ case gb_sets:is_member(Key, Br0) of
true ->
%% We have already followed this branch and it was OK.
D;
@@ -432,7 +444,7 @@ btb_follow_branch(Lbl, Regs, #btb{ok_br=Br0,index=Li}=D) ->
btb_reaches_match_1(Is, Regs, D),
%% Since we got back, this branch is OK.
- D#btb{ok_br=gb_sets:insert(Lbl, Br),must_not_save=MustNotSave,
+ D#btb{ok_br=gb_sets:insert(Key, Br),must_not_save=MustNotSave,
must_save=MustSave}
end.
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 919ee3ee7d..10805a3c36 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -141,7 +141,7 @@ renumber_labels([{bif,is_record,{f,_},
renumber_labels(Is, Acc, St);
renumber_labels([{test,is_record,{f,_}=Fail,
[Term,{atom,Tag}=TagAtom,{integer,Arity}]}|Is0], Acc, St) ->
- Tmp = {x,1023},
+ Tmp = {x,1022},
Is = case is_record_tuple(Term, Tag, Arity) of
yes ->
Is0;
@@ -190,17 +190,11 @@ replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
replace([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D) ->
replace(Is, [{test,Test,{f,label(Lbl, D)},Live,Ops,Dst}|Acc], D);
replace([{select,I,R,{f,Fail0},Vls0}|Is], Acc, D) ->
- Vls1 = map(fun ({f,L}) -> {f,label(L, D)};
- (Other) -> Other end, Vls0),
+ Vls = map(fun ({f,L}) -> {f,label(L, D)};
+ (Other) -> Other
+ end, Vls0),
Fail = label(Fail0, D),
- case redundant_values(Vls1, Fail, []) of
- [] ->
- %% Oops, no choices left. The loader will not accept that.
- %% Convert to a plain jump.
- replace(Is, [{jump,{f,Fail}}|Acc], D);
- Vls ->
- replace(Is, [{select,I,R,{f,Fail},Vls}|Acc], D)
- end;
+ replace(Is, [{select,I,R,{f,Fail},Vls}|Acc], D);
replace([{'try',R,{f,Lbl}}|Is], Acc, D) ->
replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D);
replace([{'catch',R,{f,Lbl}}|Is], Acc, D) ->
@@ -241,12 +235,6 @@ label(Old, D) ->
{value,Val} -> Val;
none -> throw({error,{undefined_label,Old}})
end.
-
-redundant_values([_,{f,Fail}|Vls], Fail, Acc) ->
- redundant_values(Vls, Fail, Acc);
-redundant_values([Val,Lbl|Vls], Fail, Acc) ->
- redundant_values(Vls, Fail, [Lbl,Val|Acc]);
-redundant_values([], _, Acc) -> reverse(Acc).
%%%
%%% Final fixup of bs_start_match2/5,bs_save2/bs_restore2 instructions for
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index ead88b57e9..6f6d742293 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -239,15 +239,30 @@ backward([{test,is_eq_exact,Fail,[Dst,{integer,Arity}]}=I|
backward([{label,Lbl}=L|Is], D, Acc) ->
backward(Is, beam_utils:index_label(Lbl, Acc, D), [L|Acc]);
backward([{select,select_val,Reg,{f,Fail0},List0}|Is], D, Acc) ->
- List = shortcut_select_list(List0, Reg, D, []),
+ List1 = shortcut_select_list(List0, Reg, D, []),
Fail1 = shortcut_label(Fail0, D),
Fail = shortcut_bs_test(Fail1, Is, D),
- Sel = {select,select_val,Reg,{f,Fail},List},
- backward(Is, D, [Sel|Acc]);
+ List = prune_redundant(List1, Fail),
+ case List of
+ [] ->
+ Jump = {jump,{f,Fail}},
+ backward([Jump|Is], D, Acc);
+ [V,F] ->
+ Test = {test,is_eq_exact,{f,Fail},[Reg,V]},
+ Jump = {jump,F},
+ backward([Jump,Test|Is], D, Acc);
+ [{atom,B1},F,{atom,B2},F] when B1 =:= not B2 ->
+ Test = {test,is_boolean,{f,Fail},[Reg]},
+ Jump = {jump,F},
+ backward([Jump,Test|Is], D, Acc);
+ [_|_] ->
+ Sel = {select,select_val,Reg,{f,Fail},List},
+ backward(Is, D, [Sel|Acc])
+ end;
backward([{jump,{f,To0}},{move,Src,Reg}=Move|Is], D, Acc) ->
To = shortcut_select_label(To0, Reg, Src, D),
Jump = {jump,{f,To}},
- case beam_utils:is_killed_at(Reg, To, D) of
+ case is_killed_at(Reg, To, D) of
false -> backward([Move|Is], D, [Jump|Acc]);
true -> backward([Jump|Is], D, Acc)
end;
@@ -295,7 +310,28 @@ backward([{test,Op,{f,To0},Ops0}|Is], D, Acc) ->
is_eq_exact -> combine_eqs(To, Ops0, D, Acc);
_ -> {test,Op,{f,To},Ops0}
end,
- backward(Is, D, [I|Acc]);
+ case {I,Acc} of
+ {{test,is_atom,Fail,Ops0},[{test,is_boolean,Fail,Ops0}|_]} ->
+ %% An is_atom test before an is_boolean test (with the
+ %% same failure label) is redundant.
+ backward(Is, D, Acc);
+ {{test,is_atom,Fail,[R]},
+ [{test,is_eq_exact,Fail,[R,{atom,_}]}|_]} ->
+ %% An is_atom test before a comparison with an atom (with
+ %% the same failure label) is redundant.
+ backward(Is, D, Acc);
+ {{test,is_integer,Fail,[R]},
+ [{test,is_eq_exact,Fail,[R,{integer,_}]}|_]} ->
+ %% An is_integer test before a comparison with an integer
+ %% (with the same failure label) is redundant.
+ backward(Is, D, Acc);
+ {{test,_,_,_},_} ->
+ %% Still a test instruction. Done.
+ backward(Is, D, [I|Acc]);
+ {_,_} ->
+ %% Rewritten to a select_val. Rescan.
+ backward([I|Is], D, Acc)
+ end;
backward([{test,Op,{f,To0},Live,Ops0,Dst}|Is], D, Acc) ->
To1 = shortcut_bs_test(To0, Is, D),
To2 = shortcut_label(To1, D),
@@ -348,6 +384,12 @@ shortcut_label(To0, D) ->
shortcut_select_label(To, Reg, Lit, D) ->
shortcut_rel_op(To, is_ne_exact, [Reg,Lit], D).
+prune_redundant([_,{f,Fail}|T], Fail) ->
+ prune_redundant(T, Fail);
+prune_redundant([V,F|T], Fail) ->
+ [V,F|prune_redundant(T, Fail)];
+prune_redundant([], _) -> [].
+
%% Replace a comparison operator with a test instruction and a jump.
%% For example, if we have this code:
%%
@@ -375,7 +417,7 @@ comp_op_find_shortcut(To0, Reg, Val, D) ->
To0 ->
not_possible();
To ->
- case beam_utils:is_killed_at(Reg, To, D) of
+ case is_killed_at(Reg, To, D) of
false -> not_possible();
true -> To
end
@@ -818,3 +860,17 @@ get_literal(nil) ->
get_literal({literal,_}=Lit) ->
Lit;
get_literal({_,_}) -> error.
+
+
+%%%
+%%% Removing stores to Y registers is not always safe
+%%% if there is an instruction that causes an exception
+%%% within a catch. In practice, there are few or no
+%%% opportunities for removing stores to Y registers anyway
+%%% if sys_core_fold has been run.
+%%%
+
+is_killed_at({x,_}=Reg, Lbl, D) ->
+ beam_utils:is_killed_at(Reg, Lbl, D);
+is_killed_at({y,_}, _, _) ->
+ false.
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index 2b5f8c1b7f..9565ab74c4 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@
locals = [] :: [{label(), arity(), label()}],
imports = gb_trees:empty() :: import_tab(),
strings = <<>> :: binary(), %String pool
- lambdas = [], %[{...}]
+ lambdas = {0,[]}, %[{...}]
literals = dict:new() :: literal_tab(),
fnames = #{} :: fname_tab(),
lines = #{} :: line_tab(),
@@ -145,15 +145,14 @@ string(Str, Dict) when is_list(Str) ->
-spec lambda(label(), non_neg_integer(), bdict()) ->
{non_neg_integer(), bdict()}.
-lambda(Lbl, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
- OldIndex = length(Lambdas0),
+lambda(Lbl, NumFree, #asm{lambdas={OldIndex,Lambdas0}}=Dict) ->
%% Set Index the same as OldIndex.
Index = OldIndex,
%% Initialize OldUniq to 0. It will be set to an unique value
%% based on the MD5 checksum of the BEAM code for the module.
OldUniq = 0,
Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
- {OldIndex,Dict#asm{lambdas=Lambdas}}.
+ {OldIndex,Dict#asm{lambdas={OldIndex+1,Lambdas}}}.
%% Returns the index for a literal (adding it to the literal table if necessary).
%% literal(Literal, Dict) -> {Index,Dict'}
@@ -236,13 +235,13 @@ string_table(#asm{strings=Strings,string_offset=Size}) ->
-spec lambda_table(bdict()) -> {non_neg_integer(), [<<_:192>>]}.
-lambda_table(#asm{locals=Loc0,lambdas=Lambdas0}) ->
+lambda_table(#asm{locals=Loc0,lambdas={NumLambdas,Lambdas0}}) ->
Lambdas1 = sofs:relation(Lambdas0),
Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]),
Lambdas2 = sofs:relative_product1(Lambdas1, Loc),
Lambdas = [<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32>> ||
{{_,Lbl,Index,NumFree,OldUniq},{F,A}} <- sofs:to_external(Lambdas2)],
- {length(Lambdas),Lambdas}.
+ {NumLambdas,Lambdas}.
%% Returns the literal table.
%% literal_table(Dict) -> {NumLiterals, [<<TermSize>>,TermInExternalFormat]}
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 84a94f09e3..c699672db1 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -314,10 +314,7 @@ get_funs({LsR0,[{func_info,[{atom,M}=AtomM,{atom,F}=AtomF,ArityArg]}|Code0]})
when is_atom(M), is_atom(F) ->
Arity = resolve_arg_unsigned(ArityArg),
{LsR,Code,RestCode} = get_fun(Code0, []),
- Entry = case Code of
- [{label,[{u,E}]}|_] -> E;
- _ -> undefined
- end,
+ [{label,[{u,Entry}]}|_] = Code,
[#function{name=F,
arity=Arity,
entry=Entry,
diff --git a/lib/compiler/src/beam_disasm.hrl b/lib/compiler/src/beam_disasm.hrl
index 30e3f22665..d968cd9587 100644
--- a/lib/compiler/src/beam_disasm.hrl
+++ b/lib/compiler/src/beam_disasm.hrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,7 +22,9 @@
%% the system (e.g. in the translation from Beam to Icode).
%%
-%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE...
+%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE.
+%% IT SHOULD BE MOVED TO A FILE THAT DEFINES (AND EXPORTS)
+%% PROPER TYPES FOR THE SET OF BEAM INSTRUCTIONS.
%%
-type beam_instr() :: 'bs_init_writable' | 'fclearerror' | 'if_end'
| 'remove_message' | 'return' | 'send' | 'timeout'
@@ -34,7 +36,7 @@
-record(function, {name :: atom(),
arity :: byte(),
- entry, %% unused ??
+ entry :: beam_lib:label(), %% unnecessary ?
code = [] :: [beam_instr()]}).
-record(beam_file, {module :: module(),
diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl
index e33655281f..4a181c1923 100644
--- a/lib/compiler/src/beam_except.erl
+++ b/lib/compiler/src/beam_except.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -133,10 +133,12 @@ translate_exception(_, _, _, _) -> no.
fix_block(Is, 0) ->
reverse(Is);
fix_block(Is, Words) ->
- fix_block_1(reverse(Is), Words).
+ reverse(fix_block_1(Is, Words)).
-fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is], Words) ->
- [{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is];
+fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) ->
+ Needed = Needed0 - Words,
+ true = Needed >= 0, %Assertion.
+ [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is];
fix_block_1([I|Is], Words) ->
[I|fix_block_1(Is, Words)].
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 13c243b155..c9ff07b496 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -64,7 +64,6 @@ norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
-%% get_map_elements is always handled in beam_split (moved out of block)
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 5e58e0f6ac..48b5a32814 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -167,12 +167,18 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
end;
share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
reverse(Is, [I|Acc]);
+share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) ->
Dict = clean_non_sharable(Dict0),
share_1(Is, Dict, [I|Seq], Acc);
+share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) ->
+ Dict = clean_non_sharable(Dict0),
+ share_1(Is, Dict, [I|Seq], Acc);
share_1([I|Is], Dict, Seq, Acc) ->
case is_unreachable_after(I) of
false ->
@@ -182,18 +188,18 @@ share_1([I|Is], Dict, Seq, Acc) ->
end.
clean_non_sharable(Dict) ->
- %% We are passing in or out of a 'try' block. Remove
- %% sequences that should not shared over the boundaries
- %% of a 'try' block. Since the end of the sequence must match,
- %% the only possible match between a sequence outside and
- %% a sequence inside the 'try' block is a sequence that ends
- %% with an instruction that causes an exception. Any sequence
- %% that causes an exception must contain a line/1 instruction.
+ %% We are passing in or out of a 'catch' or 'try' block. Remove
+ %% sequences that should not be shared over the boundaries of the
+ %% block. Since the end of the sequence must match, the only
+ %% possible match between a sequence outside and a sequence inside
+ %% the 'catch'/'try' block is a sequence that ends with an
+ %% instruction that causes an exception. Any sequence that causes
+ %% an exception must contain a line/1 instruction.
maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict).
sharable_with_try([{line,_}|_]) ->
%% This sequence may cause an exception and may potentially
- %% match a sequence on the other side of the 'try' block
+ %% match a sequence on the other side of the 'catch'/'try' block
%% boundary.
false;
sharable_with_try([_|Is]) ->
@@ -266,17 +272,17 @@ extract_seq_1(_, _) -> no.
%%% (3) (4) (5) (6) Jump and unreachable code optimizations.
%%%
--record(st, {fc, %Label for function class errors.
- entry, %Entry label (must not be moved).
- mlbl, %Moved labels.
- labels :: cerl_sets:set() %Set of referenced labels.
- }).
+-record(st,
+ {
+ entry, %Entry label (must not be moved).
+ mlbl, %Moved labels.
+ labels :: cerl_sets:set() %Set of referenced labels.
+ }).
-opt([{label,Fc}|_]=Is0, CLabel) ->
- Lbls = initial_labels(Is0),
+opt(Is0, CLabel) ->
find_fixpoint(fun(Is) ->
- St = #st{fc=Fc,entry=CLabel,mlbl=#{},
- labels=Lbls},
+ Lbls = initial_labels(Is),
+ St = #st{entry=CLabel,mlbl=#{},labels=Lbls},
opt(Is, [], St)
end, Is0).
@@ -327,7 +333,8 @@ opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) ->
%% since we will rescan the inserted labels. We MUST rescan.
St = St0#st{mlbl=maps:remove(Lbl, Mlbl)},
insert_labels([Lbl|Lbls], Is, Acc, St);
- error -> opt(Is, [I|Acc], St0)
+ error ->
+ opt(Is, [I|Acc], St0)
end;
opt([{jump,{f,_}=X}|[{label,_},{jump,X}|_]=Is], Acc, St) ->
opt(Is, Acc, St);
@@ -362,12 +369,19 @@ opt([I|Is], Acc, #st{labels=Used0}=St0) ->
true -> skip_unreachable(Is, [I|Acc], St);
false -> opt(Is, [I|Acc], St)
end;
-opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) ->
+opt([], Acc, #st{mlbl=Mlbl}) ->
Code = reverse(Acc),
- case maps:find(Fc, Mlbl) of
- {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code);
- error -> Code
- end.
+ insert_fc_labels(Code, Mlbl).
+
+insert_fc_labels([{label,L}=I|Is0], Mlbl) ->
+ case maps:find(L, Mlbl) of
+ error ->
+ [I|insert_fc_labels(Is0, Mlbl)];
+ {ok,Lbls} ->
+ Is = [{label,Lb} || Lb <- Lbls] ++ Is0,
+ [I|insert_fc_labels(Is, maps:remove(L, Mlbl))]
+ end;
+insert_fc_labels([_|_]=Is, _) -> Is.
maps_append_list(K,Vs,M) ->
case M of
@@ -375,16 +389,6 @@ maps_append_list(K,Vs,M) ->
_ -> M#{K => Vs}
end.
-insert_fc_labels([L|Ls], Mlbl, Acc0) ->
- Acc = [{label,L}|Acc0],
- case maps:find(L, Mlbl) of
- error ->
- insert_fc_labels(Ls, Mlbl, Acc);
- {ok,Lbls} ->
- insert_fc_labels(Lbls++Ls, Mlbl, Acc)
- end;
-insert_fc_labels([], _, Acc) -> Acc.
-
collect_labels(Is, #st{entry=Entry}) ->
collect_labels_1(Is, Entry, []).
@@ -495,7 +499,7 @@ is_label_used_in_block({set,_,_,Info}, Lbl) ->
{alloc,_,{gc_bif,_,{f,F}}} -> F =:= Lbl;
{alloc,_,{put_map,_,{f,F}}} -> F =:= Lbl;
{get_map_elements,{f,F}} -> F =:= Lbl;
- {'catch',{f,F}} -> F =:= Lbl;
+ {try_catch,_,{f,F}} -> F =:= Lbl;
{alloc,_,_} -> false;
{put_tuple,_} -> false;
{get_tuple_element,_} -> false;
diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl
index fd5c0a042b..ce566373bb 100644
--- a/lib/compiler/src/beam_listing.erl
+++ b/lib/compiler/src/beam_listing.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl
index 17fd2e502a..c8bef31824 100644
--- a/lib/compiler/src/beam_peep.erl
+++ b/lib/compiler/src/beam_peep.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -65,18 +65,6 @@ function({function,Name,Arity,CLabel,Is0}) ->
%% InEncoding =:= latin1, OutEncoding =:= unicode;
%% InEncoding =:= latin1, OutEncoding =:= utf8 ->
%%
-%% (2) A select_val/4 instruction that only verifies that
-%% its argument is either 'true' or 'false' can be
-%% be replaced with an is_boolean/2 instruction. That is:
-%%
-%% select_val Reg Fail [ true Next false Next ]
-%% Next: ...
-%%
-%% can be rewritten to
-%%
-%% is_boolean Fail Reg
-%% Next: ...
-%%
peep(Is) ->
peep(Is, gb_sets:empty(), []).
@@ -95,12 +83,19 @@ peep([{gc_bif,_,_,_,_,Dst}=I|Is], SeenTests0, Acc) ->
%% Kill all remembered tests that depend on the destination register.
SeenTests = kill_seen(Dst, SeenTests0),
peep(Is, SeenTests, [I|Acc]);
-peep([{test,is_boolean,{f,Fail},Ops}|_]=Is, SeenTests,
- [{test,is_atom,{f,Fail},Ops}|Acc]) ->
- %% The previous is_atom/2 test (with the same failure label) is redundant.
- %% (If is_boolean(Src) is true, is_atom(Src) is also true, so it is
- %% OK to still remember that we have seen is_atom/1.)
- peep(Is, SeenTests, Acc);
+peep([{jump,{f,L}},{label,L}=I|Is], _, Acc) ->
+ %% Sometimes beam_jump has missed this optimization.
+ peep(Is, gb_sets:empty(), [I|Acc]);
+peep([{select,Op,R,F,Vls0}|Is], _, Acc) ->
+ case prune_redundant_values(Vls0, F) of
+ [] ->
+ %% No values left. Must convert to plain jump.
+ I = {jump,F},
+ peep(Is, gb_sets:empty(), [I|Acc]);
+ [_|_]=Vls ->
+ I = {select,Op,R,F,Vls},
+ peep(Is, gb_sets:empty(), [I|Acc])
+ end;
peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) ->
case beam_utils:is_pure_test(I) of
false ->
@@ -121,16 +116,6 @@ peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) ->
peep(Is, SeenTests, [I|Acc])
end
end;
-peep([{select,select_val,Src,Fail,
- [{atom,false},{f,L},{atom,true},{f,L}]}|
- [{label,L}|_]=Is], SeenTests, Acc) ->
- I = {test,is_boolean,Fail,[Src]},
- peep([I|Is], SeenTests, Acc);
-peep([{select,select_val,Src,Fail,
- [{atom,true},{f,L},{atom,false},{f,L}]}|
- [{label,L}|_]=Is], SeenTests, Acc) ->
- I = {test,is_boolean,Fail,[Src]},
- peep([I|Is], SeenTests, Acc);
peep([I|Is], _, Acc) ->
%% An unknown instruction. Throw away all information we
%% have collected about test instructions.
@@ -155,3 +140,9 @@ kill_seen_1([{_,Ops}=Test|T], Dst) ->
false -> [Test|kill_seen_1(T, Dst)]
end;
kill_seen_1([], _) -> [].
+
+prune_redundant_values([_Val,F|Vls], F) ->
+ prune_redundant_values(Vls, F);
+prune_redundant_values([Val,Lbl|Vls], F) ->
+ [Val,Lbl|prune_redundant_values(Vls, F)];
+prune_redundant_values([], _) -> [].
diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl
index 7276537949..89cafe27ce 100644
--- a/lib/compiler/src/beam_receive.erl
+++ b/lib/compiler/src/beam_receive.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -177,7 +177,8 @@ opt_recv([I|Is], D, R0, L0, Acc) ->
no;
false ->
opt_recv(Is, D, R, L, [I|Acc])
- end.
+ end;
+opt_recv([], _, _, _, _) -> no.
opt_update_regs({block,Bl}, R, L) ->
{opt_update_regs_bl(Bl, R),L};
diff --git a/lib/compiler/src/beam_reorder.erl b/lib/compiler/src/beam_reorder.erl
new file mode 100644
index 0000000000..6a7c033ec6
--- /dev/null
+++ b/lib/compiler/src/beam_reorder.erl
@@ -0,0 +1,148 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_reorder).
+
+-export([module/2]).
+-import(lists, [member/2,reverse/1]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = [function(F) || F <- Fs0],
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ try
+ Is = reorder(Is0),
+ {function,Name,Arity,CLabel,Is}
+ catch
+ Class:Error ->
+ Stack = erlang:get_stacktrace(),
+ io:fwrite("Function: ~w/~w\n", [Name,Arity]),
+ erlang:raise(Class, Error, Stack)
+ end.
+
+%% reorder(Instructions0) -> Instructions
+%% Reorder instructions before the beam_block pass, because reordering
+%% will be more cumbersome when the blocks are in place.
+%%
+%% Execution of get_tuple_element instructions can be delayed until
+%% they are actually needed. Consider the sequence:
+%%
+%% get_tuple_element Tuple Pos Dst
+%% test Test Fail Operands
+%%
+%% If Dst is killed at label Fail (and not referenced in Operands),
+%% we can can swap the instructions:
+%%
+%% test Test Fail Operands
+%% get_tuple_element Tuple Pos Dst
+%%
+%% That can be beneficial in two ways: Firstly, if the branch is taken
+%% we have avoided execution of the get_tuple_element instruction.
+%% Secondly, even if the branch is not taken, subsequent optimization
+%% (opt_blocks/1) may be able to change Dst to the final destination
+%% register and eliminate a 'move' instruction.
+
+reorder(Is) ->
+ D = beam_utils:index_labels(Is),
+ reorder_1(Is, D, []).
+
+reorder_1([{Op,_,_}=TryCatch|[I|Is]=Is0], D, Acc)
+ when Op =:= 'catch'; Op =:= 'try' ->
+ %% Don't allow 'try' or 'catch' instructions to split blocks if
+ %% it can be avoided.
+ case is_safe(I) of
+ false ->
+ reorder_1(Is0, D, [TryCatch|Acc]);
+ true ->
+ reorder_1([TryCatch|Is], D, [I|Acc])
+ end;
+reorder_1([{label,L}=I|_], D, Acc) ->
+ Is = beam_utils:code_at(L, D),
+ reorder_1(Is, D, [I|Acc]);
+reorder_1([{test,is_nonempty_list,_,_}=I|Is], D, Acc) ->
+ %% The run-time system may combine the is_nonempty_list test with
+ %% the following get_list instruction.
+ reorder_1(Is, D, [I|Acc]);
+reorder_1([{test,_,_,_}=I,
+ {select,_,_,_,_}=S|Is], D, Acc) ->
+ %% There is nothing to gain by inserting a get_tuple_element
+ %% instruction between the test instruction and the select
+ %% instruction.
+ reorder_1(Is, D, [S,I|Acc]);
+reorder_1([{test,_,{f,_},[Src|_]}=I|Is], D,
+ [{get_tuple_element,Src,_,_}|_]=Acc) ->
+ %% We want to avoid code that can confuse beam_validator such as:
+ %% is_tuple Fail Src
+ %% test_arity Fail Src Arity
+ %% is_map Fail Src
+ %% get_tuple_element Src Pos Dst
+ %% Therefore, don't reorder the instructions in such cases.
+ reorder_1(Is, D, [I|Acc]);
+reorder_1([{test,_,{f,L},Ss}=I|Is0], D0,
+ [{get_tuple_element,_,_,El}=G|Acc0]=Acc) ->
+ case member(El, Ss) of
+ true ->
+ reorder_1(Is0, D0, [I|Acc]);
+ false ->
+ case beam_utils:is_killed_at(El, L, D0) of
+ true ->
+ Is = [I,G|Is0],
+ reorder_1(Is, D0, Acc0);
+ false ->
+ case beam_utils:is_killed(El, Is0, D0) of
+ true ->
+ Code0 = beam_utils:code_at(L, D0),
+ Code = [G|Code0],
+ D = beam_utils:index_label(L, Code, D0),
+ Is = [I|Is0],
+ reorder_1(Is, D, Acc0);
+ false ->
+ reorder_1(Is0, D0, [I|Acc])
+ end
+ end
+ end;
+reorder_1([{allocate_zero,N,Live}=I0|Is], D,
+ [{get_tuple_element,{x,Tup},_,{x,Dst}}=G|Acc]=Acc0) ->
+ case Tup < Dst andalso Dst+1 =:= Live of
+ true ->
+ %% Move allocation instruction upwards past
+ %% get_tuple_element instructions to create more
+ %% opportunities for moving get_tuple_element
+ %% instructions.
+ I = {allocate_zero,N,Dst},
+ reorder_1([I,G|Is], D, Acc);
+ false ->
+ reorder_1(Is, D, [I0|Acc0])
+ end;
+reorder_1([I|Is], D, Acc) ->
+ reorder_1(Is, D, [I|Acc]);
+reorder_1([], _, Acc) -> reverse(Acc).
+
+%% is_safe(Instruction) -> true|false
+%% Test whether an instruction is safe (cannot cause an exception).
+
+is_safe({kill,_}) -> true;
+is_safe({move,_,_}) -> true;
+is_safe({put,_}) -> true;
+is_safe({put_list,_,_,_}) -> true;
+is_safe({put_tuple,_,_}) -> true;
+is_safe({test_heap,_,_}) -> true;
+is_safe(_) -> false.
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index 3be9311080..feeab0af50 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,6 +47,8 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) ->
split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]);
split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]);
+split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
+ split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]);
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
@@ -54,11 +56,8 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}|
make_block(Bl, Acc)]);
-split_block([{set,Ds,[S|Ss],{get_map_elements,Fail}}|Is], Bl, Acc) ->
- Gets = beam_utils:join_even(Ss,Ds),
- split_block(Is, [], [{get_map_elements,Fail,S,{list,Gets}}|make_block(Bl, Acc)]);
-split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) ->
- split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]);
+split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) ->
+ split_block(Is, [], [{Op,R,L}|make_block(Bl, Acc)]);
split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) ->
split_block(Is, [], [Line|make_block(Bl, Acc)]);
split_block([I|Is], Bl, Acc) ->
diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl
index 509e013b62..a8dc6805bc 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 5298589f83..acaf3ede66 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,7 +23,8 @@
-export([module/2]).
--import(lists, [foldl/3,reverse/1,filter/2]).
+-import(lists, [filter/2,foldl/3,keyfind/3,member/2,
+ reverse/1,reverse/2,sort/1]).
module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
Fs = [function(F) || F <- Fs0],
@@ -92,8 +93,19 @@ simplify_basic_1([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Acc) -
Ts = update(I, Ts0),
simplify_basic_1(Is0, Ts, [I|Acc])
end;
-simplify_basic_1([{set,_,_,{'catch',_}}=I|Is], _Ts, Acc) ->
+simplify_basic_1([{set,_,_,{try_catch,_,_}}=I|Is], _Ts, Acc) ->
simplify_basic_1(Is, tdb_new(), [I|Acc]);
+simplify_basic_1([{test,is_atom,_,[R]}=I|Is], Ts, Acc) ->
+ case tdb_find(R, Ts) of
+ boolean -> simplify_basic_1(Is, Ts, Acc);
+ _ -> simplify_basic_1(Is, Ts, [I|Acc])
+ end;
+simplify_basic_1([{test,is_integer,_,[R]}=I|Is], Ts, Acc) ->
+ case tdb_find(R, Ts) of
+ integer -> simplify_basic_1(Is, Ts, Acc);
+ {integer,_} -> simplify_basic_1(Is, Ts, Acc);
+ _ -> simplify_basic_1(Is, Ts, [I|Acc])
+ end;
simplify_basic_1([{test,is_tuple,_,[R]}=I|Is], Ts, Acc) ->
case tdb_find(R, Ts) of
{tuple,_,_} -> simplify_basic_1(Is, Ts, Acc);
@@ -137,6 +149,16 @@ simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0
Ts = update(I, Ts0),
simplify_basic_1(Is, Ts, [I|Acc])
end;
+simplify_basic_1([{select,select_val,Reg,_,_}=I0|Is], Ts, Acc) ->
+ I = case tdb_find(Reg, Ts) of
+ {integer,Range} ->
+ simplify_select_val_int(I0, Range);
+ boolean ->
+ simplify_select_val_bool(I0);
+ _ ->
+ I0
+ end,
+ simplify_basic_1(Is, tdb_new(), [I|Acc]);
simplify_basic_1([I|Is], Ts0, Acc) ->
Ts = update(I, Ts0),
simplify_basic_1(Is, Ts, [I|Acc]);
@@ -144,6 +166,32 @@ simplify_basic_1([], Ts, Acc) ->
Is = reverse(Acc),
{Is,Ts}.
+simplify_select_val_int({select,select_val,R,_,L0}=I, {Min,Max}) ->
+ Vs = sort([V || {integer,V} <- L0]),
+ case eq_ranges(Vs, Min, Max) of
+ false -> I;
+ true -> simplify_select_val_1(L0, {integer,Max}, R, [])
+ end.
+
+simplify_select_val_bool({select,select_val,R,_,L}=I) ->
+ Vs = sort([V || {atom,V} <- L]),
+ case Vs of
+ [false,true] ->
+ simplify_select_val_1(L, {atom,false}, R, []);
+ _ ->
+ I
+ end.
+
+simplify_select_val_1([Val,F|T], Val, R, Acc) ->
+ L = reverse(Acc, T),
+ {select,select_val,R,F,L};
+simplify_select_val_1([V,F|T], Val, R, Acc) ->
+ simplify_select_val_1(T, Val, R, [F,V|Acc]).
+
+eq_ranges([H], H, H) -> true;
+eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max);
+eq_ranges(_, _, _) -> false.
+
%% simplify_float([Instruction], TypeDatabase) ->
%% {[Instruction],TypeDatabase'} | not_possible
%% Simplify floating point operations in blocks.
@@ -199,7 +247,7 @@ simplify_float_1([{set,[D0],[A0,B0],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0,
Ts = tdb_update([{D0,float}], Ts0),
simplify_float_1(Is, Ts, Rs, Acc)
end;
-simplify_float_1([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
+simplify_float_1([{set,_,_,{try_catch,_,_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
Acc = flush_all(Rs0, Is0, Acc0),
simplify_float_1(Is, tdb_new(), Rs0, [I|Acc]);
simplify_float_1([{set,_,_,{line,_}}=I|Is], Ts, Rs, Acc) ->
@@ -311,7 +359,7 @@ flt_need_heap_2({set,_,_,{get_tuple_element,_}}, H, Fl) ->
{[],H,Fl};
flt_need_heap_2({set,_,_,get_list}, H, Fl) ->
{[],H,Fl};
-flt_need_heap_2({set,_,_,{'catch',_}}, H, Fl) ->
+flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) ->
{[],H,Fl};
%% All other instructions should cause the insertion of an allocation
%% instruction if needed.
@@ -382,6 +430,17 @@ update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) ->
tdb_update([{Reg,{tuple,I,[]}},{D,kill}], Ts0);
update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) ->
tdb_update([{Reg,{tuple,0,[]}},{D,kill}], Ts0);
+update({set,[D],Args,{bif,N,_}}, Ts0) ->
+ Ar = length(Args),
+ BoolOp = erl_internal:new_type_test(N, Ar) orelse
+ erl_internal:comp_op(N, Ar) orelse
+ erl_internal:bool_op(N, Ar),
+ case BoolOp of
+ true ->
+ tdb_update([{D,boolean}], Ts0);
+ false ->
+ tdb_update([{D,kill}], Ts0)
+ end;
update({set,[D],[S],{get_tuple_element,0}}, Ts) ->
tdb_update([{D,{tuple_element,S,0}}], Ts);
update({set,[D],[S],{alloc,_,{gc_bif,float,{f,0}}}}, Ts0) ->
@@ -390,6 +449,13 @@ update({set,[D],[S],{alloc,_,{gc_bif,float,{f,0}}}}, Ts0) ->
true -> tdb_update([{D,float}], Ts0);
false -> Ts0
end;
+update({set,[D],[S1,S2],{alloc,_,{gc_bif,'band',{f,0}}}}, Ts) ->
+ case keyfind(integer, 1, [S1,S2]) of
+ {integer,N} ->
+ update_band(N, D, Ts);
+ false ->
+ tdb_update([{D,integer}], Ts)
+ end;
update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts0) ->
%% Make sure we reject non-numeric literals.
case possibly_numeric(S1) andalso possibly_numeric(S2) of
@@ -397,15 +463,17 @@ update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts0) ->
false -> Ts0
end;
update({set,[D],[S1,S2],{alloc,_,{gc_bif,Op,{f,0}}}}, Ts0) ->
- case arith_op(Op) of
- no ->
- tdb_update([{D,kill}], Ts0);
- {yes,_} ->
+ case op_type(Op) of
+ integer ->
+ tdb_update([{D,integer}], Ts0);
+ {float,_} ->
case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
{float,_} -> tdb_update([{D,float}], Ts0);
{_,float} -> tdb_update([{D,float}], Ts0);
{_,_} -> tdb_update([{D,kill}], Ts0)
- end
+ end;
+ unknown ->
+ tdb_update([{D,kill}], Ts0)
end;
update({set,[],_Src,_Op}, Ts0) -> Ts0;
update({set,[D],_Src,_Op}, Ts0) ->
@@ -437,26 +505,72 @@ update({test,is_record,_Fail,[Src,Tag,{integer,Arity}]}, Ts) ->
tdb_update([{Src,{tuple,Arity,[Tag]}}], Ts);
update({test,_Test,_Fail,_Other}, Ts) ->
Ts;
+update({test,bs_get_integer2,_,_,Args,Dst}, Ts) ->
+ tdb_update([{Dst,get_bs_integer_type(Args)}], Ts);
update({call_ext,Ar,{extfunc,math,Math,Ar}}, Ts) ->
case is_math_bif(Math, Ar) of
true -> tdb_update([{{x,0},float}], Ts);
false -> tdb_kill_xregs(Ts)
end;
update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
- Op = case tdb_find({x,1}, Ts0) of
- error -> kill;
- Info -> Info
- end,
- Ts1 = tdb_kill_xregs(Ts0),
- tdb_update([{{x,0},Op}], Ts1);
+ Ts = tdb_kill_xregs(Ts0),
+ case tdb_find({x,1}, Ts0) of
+ {tuple,Sz,_}=T0 ->
+ T = case tdb_find({x,0}, Ts0) of
+ {integer,{I,I}} when I > 1 ->
+ %% First element is not changed. The result
+ %% will have the same type.
+ T0;
+ _ ->
+ %% Position is 1 or unknown. May change the
+ %% first element of the tuple.
+ {tuple,Sz,[]}
+ end,
+ tdb_update([{{x,0},T}], Ts);
+ _ ->
+ Ts
+ end;
update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts);
update({line,_}, Ts) -> Ts;
+update({bs_save2,_,_}, Ts) -> Ts;
+update({bs_restore2,_,_}, Ts) -> Ts;
%% The instruction is unknown. Kill all information.
update(_I, _Ts) -> tdb_new().
+update_band(N, Reg, Ts) ->
+ Type = update_band_1(N, 0),
+ tdb_update([{Reg,Type}], Ts).
+
+update_band_1(N, Bits) when Bits < 64 ->
+ case 1 bsl Bits of
+ P when P =:= N + 1 ->
+ {integer,{0,N}};
+ P when P > N + 1 ->
+ integer;
+ _ ->
+ update_band_1(N, Bits+1)
+ end;
+update_band_1(_, _) ->
+ %% Negative or large positive number. Give up.
+ integer.
+
+get_bs_integer_type([_,{integer,N},U,{field_flags,Fl}])
+ when N*U < 64 ->
+ NumBits = N*U,
+ case member(unsigned, Fl) of
+ true ->
+ {integer,{0,(1 bsl NumBits)-1}};
+ false ->
+ %% Signed integer. Don't bother.
+ integer
+ end;
+get_bs_integer_type(_) ->
+ %% Avoid creating ranges with a huge upper limit.
+ integer.
+
is_math_bif(cos, 1) -> true;
is_math_bif(cosh, 1) -> true;
is_math_bif(sin, 1) -> true;
@@ -545,11 +659,22 @@ load_reg(V, Ts, Rs0, Is0) ->
{Rs,Is}
end.
-arith_op('+') -> {yes,fadd};
-arith_op('-') -> {yes,fsub};
-arith_op('*') -> {yes,fmul};
-arith_op('/') -> {yes,fdiv};
-arith_op(_) -> no.
+arith_op(Op) ->
+ case op_type(Op) of
+ {float,Instr} -> {yes,Instr};
+ _ -> no
+ end.
+
+op_type('+') -> {float,fadd};
+op_type('-') -> {float,fsub};
+op_type('*') -> {float,fmul};
+%% '/' and 'band' are specially handled.
+op_type('bor') -> integer;
+op_type('bxor') -> integer;
+op_type('bsl') -> integer;
+op_type('bsr') -> integer;
+op_type('div') -> integer;
+op_type(_) -> unknown.
flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) ->
Acc = flush_all(Rs, Is0, Acc0),
@@ -618,7 +743,6 @@ checkerror(Is) ->
checkerror_1(Is, Is).
checkerror_1([{set,[],[],fcheckerror}|_], OrigIs) -> OrigIs;
-checkerror_1([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
checkerror_1([{set,_,_,{bif,fadd,_}}|_], OrigIs) -> checkerror_2(OrigIs);
checkerror_1([{set,_,_,{bif,fsub,_}}|_], OrigIs) -> checkerror_2(OrigIs);
checkerror_1([{set,_,_,{bif,fmul,_}}|_], OrigIs) -> checkerror_2(OrigIs);
@@ -635,11 +759,14 @@ checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs].
%%%
%%% {tuple,Size,First} means that the corresponding register contains a
%%% tuple with *at least* Size elements. An tuple with unknown
-%%% size is represented as {tuple,0}. First is either [] (meaning that
+%%% size is represented as {tuple,0,[]}. First is either [] (meaning that
%%% the tuple's first element is unknown) or [FirstElement] (the contents
%%% of the first element).
%%%
%%% 'float' means that the register contains a float.
+%%%
+%%% 'integer' or {integer,{Min,Max}} that the register contains an
+%%% integer.
%% tdb_new() -> EmptyDataBase
%% Creates a new, empty type database.
@@ -669,21 +796,45 @@ tdb_copy({Tag,_}=S, D, Ts) when Tag =:= x; Tag =:= y ->
error -> orddict:erase(D, Ts);
Type -> orddict:store(D, Type, Ts)
end;
-tdb_copy(Literal, D, Ts) -> orddict:store(D, Literal, Ts).
+tdb_copy(Literal, D, Ts) ->
+ Type = case Literal of
+ {atom,_} -> Literal;
+ {float,_} -> float;
+ {integer,Int} -> {integer,{Int,Int}};
+ {literal,[_|_]} -> nonempty_list;
+ {literal,#{}} -> map;
+ {literal,Tuple} when tuple_size(Tuple) >= 1 ->
+ Lit = tag_literal(element(1, Tuple)),
+ {tuple,tuple_size(Tuple),[Lit]};
+ _ -> term
+ end,
+ if
+ Type =:= term ->
+ orddict:erase(D, Ts);
+ true ->
+ verify_type(Type),
+ orddict:store(D, Type, Ts)
+ end.
+
+tag_literal(A) when is_atom(A) -> {atom,A};
+tag_literal(F) when is_float(F) -> {float,F};
+tag_literal(I) when is_integer(I) -> {integer,I};
+tag_literal([]) -> nil;
+tag_literal(Lit) -> {literal,Lit}.
%% tdb_update([UpdateOp], Db) -> NewDb
%% UpdateOp = {Register,kill}|{Register,NewInfo}
%% Updates a type database. If a 'kill' operation is given, the type
%% information for that register will be removed from the database.
%% A kill operation takes precedence over other operations for the same
-%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the
+%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5,[]}}] means that the
%% the existing type information, if any, will be discarded, and the
-%% the '{tuple,5}' information ignored.
+%% the '{tuple,5,[]}' information ignored.
%%
%% If NewInfo information is given and there exists information about
%% the register, the old and new type information will be merged.
-%% For instance, {tuple,5} and {tuple,10} will be merged to produce
-%% {tuple,10}.
+%% For instance, {tuple,5,_} and {tuple,10,_} will be merged to produce
+%% {tuple,10,_}.
tdb_update(Uis0, Ts0) ->
Uis1 = filter(fun ({{x,_},_Op}) -> true;
@@ -694,7 +845,8 @@ tdb_update(Uis0, Ts0) ->
tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K ->
tdb_update1(remove_key(Key, Ops), Db);
-tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
+tdb_update1([{Key,Type}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
+ verify_type(Type),
[New|tdb_update1(Ops, Db)];
tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) ->
tdb_update1(remove_key(Key, Ops), Db);
@@ -704,7 +856,8 @@ tdb_update1([{_,_}|_]=Ops, [Old|Db]) ->
[Old|tdb_update1(Ops, Db)];
tdb_update1([{Key,kill}|Ops], []) ->
tdb_update1(remove_key(Key, Ops), []);
-tdb_update1([{_,_}=New|Ops], []) ->
+tdb_update1([{_,Type}=New|Ops], []) ->
+ verify_type(Type),
[New|tdb_update1(Ops, [])];
tdb_update1([], Db) -> Db.
@@ -729,10 +882,21 @@ merge_type_info({tuple,Sz1,[]}, {tuple,_Sz2,First}=Tuple2) ->
merge_type_info({tuple,Sz1,First}, Tuple2);
merge_type_info({tuple,_Sz1,First}=Tuple1, {tuple,Sz2,_}) ->
merge_type_info(Tuple1, {tuple,Sz2,First});
+merge_type_info(integer, {integer,_}=Int) ->
+ Int;
+merge_type_info({integer,_}=Int, integer) ->
+ Int;
+merge_type_info({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) ->
+ {integer,{max(Min1, Min2),min(Max1, Max2)}};
merge_type_info(NewType, _) ->
verify_type(NewType),
NewType.
+verify_type({atom,_}) -> ok;
+verify_type(boolean) -> ok;
+verify_type(integer) -> ok;
+verify_type({integer,{Min,Max}})
+ when is_integer(Min), is_integer(Max) -> ok;
verify_type(map) -> ok;
verify_type(nonempty_list) -> ok;
verify_type({tuple,Sz,[]}) when is_integer(Sz) -> ok;
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index fbcd5de1bb..249d9395ca 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,9 +25,8 @@
is_not_used/3,is_not_used_at/3,
empty_label_index/0,index_label/3,index_labels/1,
code_at/2,bif_to_test/3,is_pure_test/1,
- live_opt/1,delete_live_annos/1,combine_heap_needs/2]).
-
--export([join_even/2,split_even/1]).
+ live_opt/1,delete_live_annos/1,combine_heap_needs/2,
+ split_even/1]).
-import(lists, [member/2,sort/1,reverse/1,splitwith/2]).
@@ -67,8 +66,7 @@ is_killed(R, Is, D) ->
St = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
- {used,_} -> false;
- {unknown,_} -> false
+ {used,_} -> false
end.
%% is_killed_at(Reg, Lbl, State) -> true|false
@@ -78,8 +76,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
St0 = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St0) of
{killed,_} -> true;
- {used,_} -> false;
- {unknown,_} -> false
+ {used,_} -> false
end.
%% is_not_used(Register, [Instruction], State) -> true|false
@@ -93,8 +90,7 @@ is_not_used(R, Is, D) ->
St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
- {used,_} -> false;
- {unknown,_} -> false
+ {used,_} -> false
end.
%% is_not_used(Register, [Instruction], State) -> true|false
@@ -108,8 +104,7 @@ is_not_used_at(R, Lbl, D) ->
St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St) of
{killed,_} -> true;
- {used,_} -> false;
- {unknown,_} -> false
+ {used,_} -> false
end.
%% index_labels(FunctionIs) -> State
@@ -137,10 +132,7 @@ index_label(Lbl, Is0, Acc) ->
%% Retrieve the code at the given label.
code_at(L, Ll) ->
- case gb_trees:lookup(L, Ll) of
- {value,Code} -> Code;
- none -> none
- end.
+ gb_trees:get(L, Ll).
%% bif_to_test(Bif, [Op], Fail) -> {test,Test,Fail,[Op]}
%% Convert a BIF to a test. Fail if not possible.
@@ -164,10 +156,10 @@ bif_to_test('=<', [A,B], Fail) -> {test,is_ge,Fail,[B,A]};
bif_to_test('>', [A,B], Fail) -> {test,is_lt,Fail,[B,A]};
bif_to_test('<', [_,_]=Ops, Fail) -> {test,is_lt,Fail,Ops};
bif_to_test('>=', [_,_]=Ops, Fail) -> {test,is_ge,Fail,Ops};
-bif_to_test('==', [A,[]], Fail) -> {test,is_nil,Fail,[A]};
+bif_to_test('==', [A,nil], Fail) -> {test,is_nil,Fail,[A]};
bif_to_test('==', [_,_]=Ops, Fail) -> {test,is_eq,Fail,Ops};
bif_to_test('/=', [_,_]=Ops, Fail) -> {test,is_ne,Fail,Ops};
-bif_to_test('=:=', [A,[]], Fail) -> {test,is_nil,Fail,[A]};
+bif_to_test('=:=', [A,nil], Fail) -> {test,is_nil,Fail,[A]};
bif_to_test('=:=', [_,_]=Ops, Fail) -> {test,is_eq_exact,Fail,Ops};
bif_to_test('=/=', [_,_]=Ops, Fail) -> {test,is_ne_exact,Fail,Ops};
bif_to_test(is_record, [_,_,_]=Ops, Fail) -> {test,is_record,Fail,Ops}.
@@ -175,8 +167,7 @@ bif_to_test(is_record, [_,_,_]=Ops, Fail) -> {test,is_record,Fail,Ops}.
%% is_pure_test({test,Op,Fail,Ops}) -> true|false.
%% Return 'true' if the test instruction does not modify any
-%% registers and/or bit syntax matching state, nor modifies
-%% any bit syntax matching state.
+%% registers and/or bit syntax matching state.
%%
is_pure_test({test,is_eq,_,[_,_]}) -> true;
is_pure_test({test,is_ne,_,[_,_]}) -> true;
@@ -188,6 +179,8 @@ is_pure_test({test,is_nil,_,[_]}) -> true;
is_pure_test({test,is_nonempty_list,_,[_]}) -> true;
is_pure_test({test,test_arity,_,[_,_]}) -> true;
is_pure_test({test,has_map_fields,_,[_|_]}) -> true;
+is_pure_test({test,is_bitstr,_,[_]}) -> true;
+is_pure_test({test,is_function2,_,[_,_]}) -> true;
is_pure_test({test,Op,_,Ops}) ->
erl_internal:new_type_test(Op, length(Ops)).
@@ -235,21 +228,23 @@ combine_heap_needs(Words, {alloc,Alloc}) when is_integer(Words) ->
combine_heap_needs(H1, H2) when is_integer(H1), is_integer(H2) ->
H1+H2.
+%% split_even/1
+%% [1,2,3,4,5,6] -> {[1,3,5],[2,4,6]}
+
+split_even(Rs) -> split_even(Rs, [], []).
+
+
%%%
%%% Local functions.
%%%
-%% check_liveness(Reg, [Instruction], {State,BlockCheckFun}) ->
-%% {killed | used | unknown,UpdateState}
-%% Finds out how Reg is used in the instruction sequence. Returns one of:
-%% killed - Reg is assigned a new value or killed by an allocation instruction
-%% used - Reg is used (or possibly referenced by an allocation instruction)
-%% unknown - not possible to determine (perhaps because of an instruction
-%% that we don't recognize)
+%% check_liveness(Reg, [Instruction], #live{}) ->
+%% {killed | used, #live{}}
+%% Find out whether Reg is used or killed in instruction sequence.
+%% 'killed' means that Reg is assigned a new value or killed by an
+%% allocation instruction. 'used' means that Reg is used in some way.
-check_liveness(R, [{set,_,_,_}=I|_], St) ->
- erlang:error(only_allowed_in_blocks, [R,I,St]);
check_liveness(R, [{block,Blk}|Is], #live{bl=BlockCheck}=St0) ->
case BlockCheck(R, Blk, St0) of
{transparent,St} -> check_liveness(R, Is, St);
@@ -325,8 +320,11 @@ check_liveness(R, [{deallocate,_}|Is], St) ->
{y,_} -> {killed,St};
_ -> check_liveness(R, Is, St)
end;
-check_liveness(R, [return|_], St) ->
- check_liveness_live_ret(R, 1, St);
+check_liveness({x,_}=R, [return|_], St) ->
+ case R of
+ {x,0} -> {used,St};
+ {x,_} -> {killed,St}
+ end;
check_liveness(R, [{call,Live,_}|Is], St) ->
case R of
{x,X} when X < Live -> {used,St};
@@ -461,8 +459,9 @@ check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) ->
{x,_} ->
{killed,St};
_ ->
- %% y register. Rarely happens. Be very conversative.
- {unknown,St}
+ %% y register. Rarely happens. Be very conversative and
+ %% assume it's used.
+ {used,St}
end;
check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) ->
check_liveness_at(R, Fail, St);
@@ -484,13 +483,30 @@ check_liveness(R, [{get_map_elements,{f,Fail},S,{list,L}}|Is], St0) ->
Other
end
end;
+check_liveness(R, [{put_map,{f,_},_,Src,_D,Live,{list,_}}|_], St0) ->
+ case R of
+ Src ->
+ {used,St0};
+ {x,X} when X < Live ->
+ {used,St0};
+ {x,_} ->
+ {killed,St0};
+ {y,_} ->
+ %% Conservatively mark it as used.
+ {used,St0}
+ end;
+check_liveness(R, [{test_heap,N,Live}|Is], St) ->
+ I = {block,[{set,[],[],{alloc,Live,{nozero,nostack,N,[]}}}]},
+ check_liveness(R, [I|Is], St);
+check_liveness(R, [{allocate_zero,N,Live}|Is], St) ->
+ I = {block,[{set,[],[],{alloc,Live,{zero,N,0,[]}}}]},
+ check_liveness(R, [I|Is], St);
+check_liveness(R, [{get_list,S,D1,D2}|Is], St) ->
+ I = {block,[{set,[D1,D2],[S],get_list}]},
+ check_liveness(R, [I|Is], St);
check_liveness(_R, Is, St) when is_list(Is) ->
-%% case Is of
-%% [I|_] ->
-%% io:format("~p ~p\n", [_R,I]);
-%% _ -> ok
-%% end,
- {unknown,St}.
+ %% Not implemented. Conservatively assume that the register is used.
+ {used,St}.
check_liveness_everywhere(R, [{f,Lbl}|T], St0) ->
case check_liveness_at(R, Lbl, St0) of
@@ -509,7 +525,7 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) ->
none ->
{Res,St} = case gb_trees:lookup(Lbl, Ll) of
{value,Is} -> check_liveness(R, Is, St0);
- none -> {unknown,St0}
+ none -> {used,St0}
end,
{Res,St#live{res=gb_trees:insert(Lbl, Res, St#live.res)}}
end.
@@ -517,14 +533,6 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) ->
check_liveness_ret(R, R, St) -> {used,St};
check_liveness_ret(_, _, St) -> {killed,St}.
-check_liveness_live_ret({x,R}, Live, St) ->
- if
- R < Live -> {used,St};
- true -> {killed,St}
- end;
-check_liveness_live_ret({y,_}, _, St) ->
- {killed,St}.
-
check_liveness_fail(_, _, _, 0, St) ->
{killed,St};
check_liveness_fail(R, Op, Args, Fail, St) ->
@@ -561,9 +569,9 @@ check_killed_block(R, [{set,Ds,Ss,_Op}|Is]) ->
false -> check_killed_block(R, Is)
end
end;
-check_killed_block(R, [{'%live',Live,_}|Is]) ->
+check_killed_block(R, [{'%live',_,Regs}|Is]) ->
case R of
- {x,X} when X >= Live -> killed;
+ {x,X} when (Regs bsr X) band 1 =:= 0 -> killed;
_ -> check_killed_block(R, Is)
end;
check_killed_block(_, []) -> transparent.
@@ -574,8 +582,10 @@ check_killed_block(_, []) -> transparent.
%% killed - Reg is assigned a new value or killed by an allocation instruction
%% transparent - Reg is neither used nor killed
%% used - Reg is explicitly used by an instruction
-%%
-%% (Unknown instructions will cause an exception.)
+%%
+%% '%live' annotations are not allowed.
+%%
+%% (Unknown instructions will cause an exception.)
check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St) ->
if
@@ -584,11 +594,6 @@ check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St) ->
end;
check_used_block(R, [{set,Ds,Ss,Op}|Is], St) ->
check_used_block_1(R, Ss, Ds, Op, Is, St);
-check_used_block(R, [{'%live',Live,_}|Is], St) ->
- case R of
- {x,X} when X >= Live -> {killed,St};
- _ -> check_used_block(R, Is, St)
- end;
check_used_block(_, [], St) -> {transparent,St}.
check_used_block_1(R, Ss, Ds, Op, Is, St0) ->
@@ -619,8 +624,7 @@ is_reg_used_at_1(_, 0, St) ->
is_reg_used_at_1(R, Lbl, St0) ->
case check_liveness_at(R, Lbl, St0) of
{killed,St} -> {false,St};
- {used,St} -> {true,St};
- {unknown,St} -> {true,St}
+ {used,St} -> {true,St}
end.
index_labels_1([{label,Lbl}|Is0], Acc) ->
@@ -736,11 +740,6 @@ live_opt([{select,_,Src,Fail,List}=I|Is], Regs0, D, Acc) ->
Regs1 = x_live([Src], Regs0),
Regs = live_join_labels([Fail|List], D, Regs1),
live_opt(Is, Regs, D, [I|Acc]);
-live_opt([{'try',_,_}=I|Is], Regs, D, Acc) ->
- %% If an exeption happens, all x registers will be killed.
- %% Therefore, we should only base liveness of the code inside
- %% the try.
- live_opt(Is, Regs, D, [I|Acc]);
live_opt([{try_case,_}=I|Is], _, D, Acc) ->
live_opt(Is, live_call(1), D, [I|Acc]);
live_opt([{loop_rec,_Fail,_Dst}=I|Is], _, D, Acc) ->
@@ -749,6 +748,11 @@ live_opt([timeout=I|Is], _, D, Acc) ->
live_opt(Is, 0, D, [I|Acc]);
live_opt([{wait,_}=I|Is], _, D, Acc) ->
live_opt(Is, 0, D, [I|Acc]);
+live_opt([{get_map_elements,Fail,Src,{list,List}}=I|Is], Regs0, D, Acc) ->
+ {Ss,Ds} = split_even(List),
+ Regs1 = x_live([Src|Ss], x_dead(Ds, Regs0)),
+ Regs = live_join_label(Fail, D, Regs1),
+ live_opt(Is, Regs, D, [I|Acc]);
%% Transparent instructions - they neither use nor modify x registers.
live_opt([{deallocate,_}=I|Is], Regs, D, Acc) ->
@@ -840,14 +844,7 @@ x_live([], Regs) -> Regs.
is_live(X, Regs) -> ((Regs bsr X) band 1) =:= 1.
-%% split_even/1
-%% [1,2,3,4,5,6] -> {[1,3,5],[2,4,6]}
-split_even(Rs) -> split_even(Rs,[],[]).
-split_even([],Ss,Ds) -> {reverse(Ss),reverse(Ds)};
-split_even([S,D|Rs],Ss,Ds) ->
- split_even(Rs,[S|Ss],[D|Ds]).
-
-%% join_even/1
-%% {[1,3,5],[2,4,6]} -> [1,2,3,4,5,6]
-join_even([],[]) -> [];
-join_even([S|Ss],[D|Ds]) -> [S,D|join_even(Ss,Ds)].
+split_even([], Ss, Ds) ->
+ {reverse(Ss),reverse(Ds)};
+split_even([S,D|Rs], Ss, Ds) ->
+ split_even(Rs, [S|Ss], [D|Ds]).
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 942d69a756..16dba35adc 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,15 +31,6 @@
-import(lists, [reverse/1,foldl/3,foreach/2,dropwhile/2]).
--define(MAXREG, 1024).
-
-%%-define(DEBUG, 1).
--ifdef(DEBUG).
--define(DBG_FORMAT(F, D), (io:format((F), (D)))).
--else.
--define(DBG_FORMAT(F, D), ok).
--endif.
-
%% To be called by the compiler.
module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)
when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) ->
@@ -170,29 +161,25 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
% in the module (those that start with bs_start_match2).
}).
--ifdef(DEBUG).
-print_st(#st{x=Xs,y=Ys,numy=NumY,h=H,ct=Ct}) ->
- io:format(" #st{x=~p~n"
- " y=~p~n"
- " numy=~p,h=~p,ct=~w~n",
- [gb_trees:to_list(Xs),gb_trees:to_list(Ys),NumY,H,Ct]).
--endif.
+%% Match context type.
+-record(ms,
+ {id=make_ref() :: reference(), %Unique ID.
+ valid=0 :: non_neg_integer(), %Valid slots
+ slots=0 :: non_neg_integer() %Number of slots
+ }).
validate_1(Is, Name, Arity, Entry, Ft) ->
validate_2(labels(Is), Name, Arity, Entry, Ft).
validate_2({Ls1,[{func_info,{atom,Mod},{atom,Name},Arity}=_F|Is]},
Name, Arity, Entry, Ft) ->
- lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [{label,_L}]) end, Ls1),
- ?DBG_FORMAT(" ~p.~n", [_F]),
validate_3(labels(Is), Name, Arity, Entry, Mod, Ls1, Ft);
validate_2({Ls1,Is}, Name, Arity, _Entry, _Ft) ->
error({{'_',Name,Arity},{first(Is),length(Ls1),illegal_instruction}}).
validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1, Ft) ->
- lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [{label,_L}]) end, Ls2),
Offset = 1 + length(Ls1) + 1 + length(Ls2),
- EntryOK = (Entry =:= undefined) orelse lists:member(Entry, Ls2),
+ EntryOK = lists:member(Entry, Ls2),
if
EntryOK ->
St = init_state(Arity),
@@ -260,7 +247,6 @@ valfun([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) ->
error({MFA,Error})
end;
valfun([I|Is], MFA, Offset, Vst0) ->
- ?DBG_FORMAT(" ~p.\n", [I]),
valfun(Is, MFA, Offset+1,
try
Vst = val_dsetel(I, Vst0),
@@ -278,7 +264,6 @@ valfun_1({label,Lbl}, #vst{current=St0,branched=B,labels=Lbls}=Vst) ->
valfun_1(_I, #vst{current=none}=Vst) ->
%% Ignore instructions after erlang:error/1,2, which
%% the original R10B compiler thought would return.
- ?DBG_FORMAT("Ignoring ~p\n", [_I]),
Vst;
valfun_1({badmatch,Src}, Vst) ->
assert_term(Src, Vst),
@@ -296,7 +281,7 @@ valfun_1({bs_context_to_binary,Ctx}, #vst{current=#st{x=Xs}}=Vst) ->
case Ctx of
{Tag,X} when Tag =:= x; Tag =:= y ->
Type = case gb_trees:lookup(X, Xs) of
- {value,{match_context,_,_}} -> term;
+ {value,#ms{}} -> term;
_ -> get_term_type(Ctx, Vst)
end,
set_type_reg(Type, Ctx, Vst);
@@ -531,6 +516,9 @@ valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
TupleType = upgrade_tuple_type({tuple,[get_tuple_size(PosType)]}, TupleType0),
Vst = set_type(TupleType, Tuple, Vst1),
set_type_reg(term, Dst, Vst);
+valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) ->
+ validate_src(Src, Vst),
+ kill_state(Vst);
valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
validate_src(Src, Vst0),
Vst = branch_state(Fail, Vst0),
@@ -594,7 +582,7 @@ valfun_4({test,bs_start_match2,{f,Fail},Live,[Ctx,NeedSlots],Ctx}, Vst0) ->
verify_live(Live, Vst0),
Vst1 = prune_x_regs(Live, Vst0),
BranchVst = case CtxType of
- {match_context,_,_} ->
+ #ms{} ->
%% The failure branch will never be taken when Ctx
%% is a match context. Therefore, the type for Ctx
%% at the failure label must not be match_context
@@ -670,8 +658,10 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst0) ->
case Src of
{Tag,_} when Tag =:= x; Tag =:= y ->
set_type_reg(map, Src, Vst);
+ {literal,Map} when is_map(Map) ->
+ Vst;
_ ->
- Vst
+ kill_state(Vst)
end;
valfun_4({test,_Op,{f,Lbl},Src}, Vst) ->
validate_src(Src, Vst),
@@ -758,10 +748,20 @@ valfun_4(_, _) ->
verify_get_map(Fail, Src, List, Vst0) ->
assert_type(map, Src, Vst0),
- Vst1 = branch_state(Fail, Vst0),
+ Vst1 = foldl(fun(D, Vsti) ->
+ case is_reg_defined(D,Vsti) of
+ true -> set_type_reg(term,D,Vsti);
+ false -> Vsti
+ end
+ end, Vst0, extract_map_vals(List)),
+ Vst2 = branch_state(Fail, Vst1),
Keys = extract_map_keys(List),
assert_unique_map_keys(Keys),
- verify_get_map_pair(List,Vst0,Vst1).
+ verify_get_map_pair(List,Vst0,Vst2).
+
+extract_map_vals([_Key,Val|T]) ->
+ [Val|extract_map_vals(T)];
+extract_map_vals([]) -> [].
extract_map_keys([Key,_Val|T]) ->
[Key|extract_map_keys(T)];
@@ -808,9 +808,11 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->
%% A possibility for garbage collection must not occur between setelement/3 and
%% set_tuple_element/3.
%%
+%% Note that #vst.current will be 'none' if the instruction is unreachable.
+%%
val_dsetel({move,_,_}, Vst) ->
Vst;
-val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=St}=Vst) ->
+val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=#st{}=St}=Vst) ->
Vst#vst{current=St#st{setelem=true}};
val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->
error(illegal_context_for_set_tuple_element);
@@ -837,7 +839,7 @@ kill_state_1(Vst) ->
%% The stackframe must be initialized.
%% The instruction will return to the instruction following the call.
call(Name, Live, #vst{current=St}=Vst) ->
- verify_live(Live, Vst),
+ verify_call_args(Name, Live, Vst),
verify_y_init(Vst),
case return_type(Name, Vst) of
Type when Type =/= exception ->
@@ -849,44 +851,74 @@ call(Name, Live, #vst{current=St}=Vst) ->
%% Tail call.
%% The stackframe must have a known size and be initialized.
%% Does not return to the instruction following the call.
-tail_call(Name, Live, Vst) ->
+tail_call(Name, Live, Vst0) ->
+ verify_y_init(Vst0),
+ Vst = deallocate(Vst0),
verify_call_args(Name, Live, Vst),
- verify_y_init(Vst),
verify_no_ct(Vst),
kill_state(Vst).
verify_call_args(_, 0, #vst{}) ->
ok;
verify_call_args({f,Lbl}, Live, Vst) when is_integer(Live)->
- Verify = fun(R) ->
- case get_move_term_type(R, Vst) of
- {match_context,_,_} ->
- verify_call_match_context(Lbl, Vst);
- _ ->
- ok
- end
- end,
- verify_call_args_1(Live, Verify, Vst);
+ verify_local_call(Lbl, Live, Vst);
verify_call_args(_, Live, Vst) when is_integer(Live)->
- Verify = fun(R) -> get_term_type(R, Vst) end,
- verify_call_args_1(Live, Verify, Vst);
+ verify_call_args_1(Live, Vst);
verify_call_args(_, Live, _) ->
error({bad_number_of_live_regs,Live}).
-verify_call_args_1(0, _, _) -> ok;
-verify_call_args_1(N, Verify, Vst) ->
+verify_call_args_1(0, _) -> ok;
+verify_call_args_1(N, Vst) ->
X = N - 1,
- Verify({x,X}),
- verify_call_args_1(X, Verify, Vst).
+ get_term_type({x,X}, Vst),
+ verify_call_args_1(X, Vst).
+
+verify_local_call(Lbl, Live, Vst) ->
+ case all_ms_in_x_regs(Live, Vst) of
+ [{R,Ctx}] ->
+ %% Verify that there is a suitable bs_start_match2 instruction.
+ verify_call_match_context(Lbl, R, Vst),
+
+ %% Since the callee has consumed the match context,
+ %% there must be no additional copies in Y registers.
+ #ms{id=Id} = Ctx,
+ case ms_in_y_regs(Id, Vst) of
+ [] ->
+ ok;
+ [_|_]=Ys ->
+ error({multiple_match_contexts,[R|Ys]})
+ end;
+ [_,_|_]=Xs0 ->
+ Xs = [R || {R,_} <- Xs0],
+ error({multiple_match_contexts,Xs});
+ [] ->
+ ok
+ end.
-verify_call_match_context(Lbl, #vst{ft=Ft}) ->
+all_ms_in_x_regs(0, _Vst) ->
+ [];
+all_ms_in_x_regs(Live0, Vst) ->
+ Live = Live0 - 1,
+ R = {x,Live},
+ case get_move_term_type(R, Vst) of
+ #ms{}=M ->
+ [{R,M}|all_ms_in_x_regs(Live, Vst)];
+ _ ->
+ all_ms_in_x_regs(Live, Vst)
+ end.
+
+ms_in_y_regs(Id, #vst{current=#st{y=Ys0}}) ->
+ Ys = gb_trees:to_list(Ys0),
+ [Y || {Y,#ms{id=OtherId}} <- Ys, OtherId =:= Id].
+
+verify_call_match_context(Lbl, Ctx, #vst{ft=Ft}) ->
case gb_trees:lookup(Lbl, Ft) of
none ->
error(no_bs_start_match2);
{value,[{test,bs_start_match2,_,_,[Ctx,_],Ctx}|_]} ->
ok;
- {value,[{test,bs_start_match2,_,_,[Bin,_,_],Ctx}|_]} ->
- error({binary_and_context_regs_different,Bin,Ctx})
+ {value,[{test,bs_start_match2,_,_,_,_}=I|_]} ->
+ error({unsuitable_bs_start_match2,I})
end.
allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst0) ->
@@ -970,9 +1002,9 @@ get_fls(#vst{current=#st{fls=Fls}}) when is_atom(Fls) -> Fls.
init_fregs() -> 0.
-set_freg({fr,Fr}, #vst{current=#st{f=Fregs0}=St}=Vst)
+set_freg({fr,Fr}=Freg, #vst{current=#st{f=Fregs0}=St}=Vst)
when is_integer(Fr), 0 =< Fr ->
- limit_check(Fr),
+ check_limit(Freg),
Bit = 1 bsl Fr,
if
Fregs0 band Bit =:= 0 ->
@@ -985,9 +1017,10 @@ set_freg(Fr, _) -> error({bad_target,Fr}).
assert_freg_set({fr,Fr}=Freg, #vst{current=#st{f=Fregs}})
when is_integer(Fr), 0 =< Fr ->
if
- Fregs band (1 bsl Fr) =/= 0 ->
- limit_check(Fr);
- true -> error({uninitialized_reg,Freg})
+ (Fregs bsr Fr) band 1 =:= 0 ->
+ error({uninitialized_reg,Freg});
+ true ->
+ ok
end;
assert_freg_set(Fr, _) -> error({bad_source,Fr}).
@@ -1017,7 +1050,7 @@ assert_unique_map_keys([_,_|_]=Ls) ->
%%%
bsm_match_state(Slots) ->
- {match_context,0,Slots}.
+ #ms{slots=Slots}.
bsm_validate_context(Reg, Vst) ->
_ = bsm_get_context(Reg, Vst),
@@ -1025,7 +1058,7 @@ bsm_validate_context(Reg, Vst) ->
bsm_get_context({x,X}=Reg, #vst{current=#st{x=Xs}}=_Vst) when is_integer(X) ->
case gb_trees:lookup(X, Xs) of
- {value,{match_context,_,_}=Ctx} -> Ctx;
+ {value,#ms{}=Ctx} -> Ctx;
_ -> error({no_bsm_context,Reg})
end;
bsm_get_context(Reg, _) -> error({bad_source,Reg}).
@@ -1037,8 +1070,8 @@ bsm_save(Reg, {atom,start}, Vst) ->
Vst;
bsm_save(Reg, SavePoint, Vst) ->
case bsm_get_context(Reg, Vst) of
- {match_context,Bits,Slots} when SavePoint < Slots ->
- Ctx = {match_context,Bits bor (1 bsl SavePoint),Slots},
+ #ms{valid=Bits,slots=Slots}=Ctxt0 when SavePoint < Slots ->
+ Ctx = Ctxt0#ms{valid=Bits bor (1 bsl SavePoint),slots=Slots},
set_type_reg(Ctx, Reg, Vst);
_ -> error({illegal_save,SavePoint})
end.
@@ -1050,7 +1083,7 @@ bsm_restore(Reg, {atom,start}, Vst) ->
Vst;
bsm_restore(Reg, SavePoint, Vst) ->
case bsm_get_context(Reg, Vst) of
- {match_context,Bits,Slots} when SavePoint < Slots ->
+ #ms{valid=Bits,slots=Slots} when SavePoint < Slots ->
case Bits band (1 bsl SavePoint) of
0 -> error({illegal_restore,SavePoint,not_set});
_ -> Vst
@@ -1066,16 +1099,16 @@ set_type(Type, {x,_}=Reg, Vst) -> set_type_reg(Type, Reg, Vst);
set_type(Type, {y,_}=Reg, Vst) -> set_type_y(Type, Reg, Vst);
set_type(_, _, #vst{}=Vst) -> Vst.
-set_type_reg(Type, {x,X}, #vst{current=#st{x=Xs}=St}=Vst)
+set_type_reg(Type, {x,X}=Reg, #vst{current=#st{x=Xs}=St}=Vst)
when is_integer(X), 0 =< X ->
- limit_check(X),
+ check_limit(Reg),
Vst#vst{current=St#st{x=gb_trees:enter(X, Type, Xs)}};
set_type_reg(Type, Reg, Vst) ->
set_type_y(Type, Reg, Vst).
set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0}=St}=Vst)
when is_integer(Y), 0 =< Y ->
- limit_check(Y),
+ check_limit(Reg),
Ys = case gb_trees:lookup(Y, Ys0) of
none ->
error({invalid_store,Reg,Type});
@@ -1093,6 +1126,17 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
Ys = gb_trees:update(Y, initialized, Ys0),
Vst#vst{current=St#st{y=Ys}}.
+
+is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
+is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);
+is_reg_defined(V, #vst{}) -> error({not_a_register, V}).
+
+is_type_defined_x({x,X}, #vst{current=#st{x=Xs}}) ->
+ gb_trees:is_defined(X,Xs).
+
+is_type_defined_y({y,Y}, #vst{current=#st{y=Ys}}) ->
+ gb_trees:is_defined(Y,Ys).
+
assert_term(Src, Vst) ->
get_term_type(Src, Vst),
ok.
@@ -1120,7 +1164,7 @@ assert_term(Src, Vst) ->
%% Thus 'exception' is never stored as type descriptor
%% for a register.
%%
-%% {match_context,_,_} A matching context for bit syntax matching. We do allow
+%% #ms{} A match context for bit syntax matching. We do allow
%% it to moved/to from stack, but otherwise it must only
%% be accessed by bit syntax matching instructions.
%%
@@ -1162,12 +1206,17 @@ assert_type(WantedType, Term, Vst) ->
assert_type(Correct, Correct) -> ok;
assert_type(float, {float,_}) -> ok;
assert_type(tuple, {tuple,_}) -> ok;
+assert_type(tuple, {literal,Tuple}) when is_tuple(Tuple) -> ok;
assert_type({tuple_element,I}, {tuple,[Sz]})
when 1 =< I, I =< Sz ->
ok;
assert_type({tuple_element,I}, {tuple,Sz})
when is_integer(Sz), 1 =< I, I =< Sz ->
ok;
+assert_type({tuple_element,I}, {literal,Lit}) when I =< tuple_size(Lit) ->
+ ok;
+assert_type(cons, {literal,[_|_]}) ->
+ ok;
assert_type(Needed, Actual) ->
error({bad_type,{needed,Needed},{actual,Actual}}).
@@ -1222,7 +1271,7 @@ get_term_type(Src, Vst) ->
initialized -> error({unassigned,Src});
{catchtag,_} -> error({catchtag,Src});
{trytag,_} -> error({trytag,Src});
- {match_context,_,_} -> error({match_context,Src});
+ #ms{} -> error({match_context,Src});
Type -> Type
end.
@@ -1374,11 +1423,12 @@ merge_types(bool, {atom,A}) ->
merge_bool(A);
merge_types({atom,A}, bool) ->
merge_bool(A);
-merge_types({match_context,B0,Slots},{match_context,B1,Slots}) ->
- {match_context,B0 bor B1,Slots};
-merge_types({match_context,_,_}=M, _) ->
+merge_types(#ms{id=Id,valid=B0,slots=Slots}=M,
+ #ms{id=Id,valid=B1,slots=Slots}) ->
+ M#ms{valid=B0 bor B1,slots=Slots};
+merge_types(#ms{}=M, _) ->
M;
-merge_types(_, {match_context,_,_}=M) ->
+merge_types(_, #ms{}=M) ->
M;
merge_types(T1, T2) when T1 =/= T2 ->
%% Too different. All we know is that the type is a 'term'.
@@ -1502,7 +1552,6 @@ bif_type(node, [_], _) -> {atom,[]};
bif_type(hd, [_], _) -> term;
bif_type(tl, [_], _) -> term;
bif_type(get, [_], _) -> term;
-bif_type(raise, [_,_], _) -> exception;
bif_type(Bif, _, _) when is_atom(Bif) -> term.
is_bif_safe('/=', 2) -> true;
@@ -1516,6 +1565,7 @@ is_bif_safe('>=', 2) -> true;
is_bif_safe(is_atom, 1) -> true;
is_bif_safe(is_boolean, 1) -> true;
is_bif_safe(is_binary, 1) -> true;
+is_bif_safe(is_bitstring, 1) -> true;
is_bif_safe(is_float, 1) -> true;
is_bif_safe(is_function, 1) -> true;
is_bif_safe(is_integer, 1) -> true;
@@ -1546,8 +1596,12 @@ return_type_1(erlang, setelement, 3, Vst) ->
Tuple = {x,1},
TupleType =
case get_term_type(Tuple, Vst) of
- {tuple,_}=TT -> TT;
- _ -> {tuple,[0]}
+ {tuple,_}=TT ->
+ TT;
+ {literal,Lit} when is_tuple(Lit) ->
+ {tuple,tuple_size(Lit)};
+ _ ->
+ {tuple,[0]}
end,
case get_term_type({x,0}, Vst) of
{integer,[]} -> TupleType;
@@ -1591,17 +1645,19 @@ return_type_math(pow, 2) -> {float,[]};
return_type_math(pi, 0) -> {float,[]};
return_type_math(F, A) when is_atom(F), is_integer(A), A >= 0 -> term.
-limit_check(Num) when is_integer(Num), Num >= ?MAXREG ->
- error(limit);
-limit_check(_) -> ok.
+check_limit({x,X}) when is_integer(X), X < 1023 ->
+ %% Note: x(1023) is reserved for use by the BEAM loader.
+ ok;
+check_limit({y,Y}) when is_integer(Y), Y < 1024 ->
+ ok;
+check_limit({fr,Fr}) when is_integer(Fr), Fr < 1024 ->
+ ok;
+check_limit(_) ->
+ error(limit).
min(A, B) when is_integer(A), is_integer(B), A < B -> A;
min(A, B) when is_integer(A), is_integer(B) -> B.
gb_trees_from_list(L) -> gb_trees:from_orddict(lists:sort(L)).
--ifdef(DEBUG).
-error(Error) -> exit(Error).
--else.
error(Error) -> throw(Error).
--endif.
diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl
index 8381578b68..6c7f8543c2 100644
--- a/lib/compiler/src/beam_z.erl
+++ b/lib/compiler/src/beam_z.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
-export([module/2]).
+-import(lists, [dropwhile/2]).
+
module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
Fs = [function(F) || F <- Fs0],
{ok,{Mod,Exp,Attr,Fs,Lc}}.
@@ -51,6 +53,16 @@ undo_renames([{call,A,F},return|Is]) ->
[{call_only,A,F}|undo_renames(Is)];
undo_renames([{call_ext,A,F},return|Is]) ->
[{call_ext_only,A,F}|undo_renames(Is)];
+undo_renames([{bif,raise,_,_,_}=I|Is0]) ->
+ %% A minor optimization. Done here because:
+ %% (1) beam_jump may move or share 'raise' instructions, and that
+ %% may confuse beam_validator.
+ %% (2) beam_trim cannot do its optimization if the 'deallocate'
+ %% instruction after 'raise' has been removed.
+ Is = dropwhile(fun({label,_}) -> false;
+ (_) -> true
+ end, Is0),
+ [I|undo_renames(Is)];
undo_renames([I|Is]) ->
[undo_rename(I)|undo_renames(Is)];
undo_renames([]) -> [].
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 010327b5e3..61abae344c 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -126,6 +126,7 @@
%% keep map exports here for now
c_map_pattern/1,
is_c_map/1,
+ is_c_map_pattern/1,
map_es/1,
map_arg/1,
update_c_map/3,
@@ -134,7 +135,7 @@
ann_c_map_pattern/2,
map_pair_op/1,map_pair_key/1,map_pair_val/1,
update_c_map_pair/4,
- c_map_pair/2,
+ c_map_pair/2, c_map_pair_exact/2,
ann_c_map_pair/4
]).
@@ -1598,13 +1599,20 @@ is_c_map(#c_literal{val = V}) when is_map(V) ->
is_c_map(_) ->
false.
--spec map_es(c_map()) -> [c_map_pair()].
+-spec map_es(c_map() | c_literal()) -> [c_map_pair()].
+map_es(#c_literal{anno=As,val=M}) when is_map(M) ->
+ [ann_c_map_pair(As,
+ #c_literal{anno=As,val='assoc'},
+ #c_literal{anno=As,val=K},
+ #c_literal{anno=As,val=V}) || {K,V} <- maps:to_list(M)];
map_es(#c_map{es = Es}) ->
Es.
--spec map_arg(c_map()) -> c_map() | c_literal().
+-spec map_arg(c_map() | c_literal()) -> c_map() | c_literal().
+map_arg(#c_literal{anno=As,val=M}) when is_map(M) ->
+ #c_literal{anno=As,val=#{}};
map_arg(#c_map{arg=M}) ->
M.
@@ -1629,6 +1637,11 @@ is_c_map_empty(#c_map{ es=[] }) -> true;
is_c_map_empty(#c_literal{val=M}) when is_map(M),map_size(M) =:= 0 -> true;
is_c_map_empty(_) -> false.
+-spec is_c_map_pattern(c_map()) -> boolean().
+
+is_c_map_pattern(#c_map{is_pat=IsPat}) ->
+ IsPat.
+
-spec ann_c_map([term()], [c_map_pair()]) -> c_map() | c_literal().
ann_c_map(As, Es) ->
@@ -1681,6 +1694,11 @@ map_pair_op(#c_map_pair{op=Op}) -> Op.
c_map_pair(Key,Val) ->
#c_map_pair{op=#c_literal{val=assoc},key=Key,val=Val}.
+-spec c_map_pair_exact(cerl(), cerl()) -> c_map_pair().
+
+c_map_pair_exact(Key,Val) ->
+ #c_map_pair{op=#c_literal{val=exact},key=Key,val=Val}.
+
-spec ann_c_map_pair([term()], cerl(), cerl(), cerl()) ->
c_map_pair().
@@ -1937,7 +1955,7 @@ is_c_var(_) ->
false.
-%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl()
+%% @spec c_fname(Name::atom(), Arity::arity()) -> cerl()
%% @equiv c_var({Name, Arity})
%% @see fname_id/1
%% @see fname_arity/1
@@ -1945,18 +1963,18 @@ is_c_var(_) ->
%% @see ann_c_fname/3
%% @see update_c_fname/3
--spec c_fname(atom(), non_neg_integer()) -> c_var().
+-spec c_fname(atom(), arity()) -> c_var().
c_fname(Atom, Arity) ->
c_var({Atom, Arity}).
-%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) ->
+%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::arity()) ->
%% cerl()
%% @equiv ann_c_var(As, {Atom, Arity})
%% @see c_fname/2
--spec ann_c_fname([term()], atom(), non_neg_integer()) -> c_var().
+-spec ann_c_fname([term()], atom(), arity()) -> c_var().
ann_c_fname(As, Atom, Arity) ->
ann_c_var(As, {Atom, Arity}).
@@ -1974,13 +1992,13 @@ update_c_fname(#c_var{name = {_, Arity}, anno = As}, Atom) ->
#c_var{name = {Atom, Arity}, anno = As}.
-%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) ->
+%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::arity()) ->
%% cerl()
%% @equiv update_c_var(Old, {Atom, Arity})
%% @see update_c_fname/2
%% @see c_fname/2
--spec update_c_fname(c_var(), atom(), integer()) -> c_var().
+-spec update_c_fname(c_var(), atom(), arity()) -> c_var().
update_c_fname(Node, Atom, Arity) ->
update_c_var(Node, {Atom, Arity}).
@@ -2029,14 +2047,14 @@ fname_id(#c_var{name={A,_}}) ->
A.
-%% @spec fname_arity(cerl()) -> byte()
+%% @spec fname_arity(cerl()) -> arity()
%%
%% @doc Returns the arity part of an abstract function name variable.
%%
%% @see fname_id/1
%% @see c_fname/2
--spec fname_arity(c_var()) -> byte().
+-spec fname_arity(c_var()) -> arity().
fname_arity(#c_var{name={_,N}}) ->
N.
@@ -2482,7 +2500,7 @@ fun_body(Node) ->
Node#c_fun.body.
-%% @spec fun_arity(Node::cerl()) -> integer()
+%% @spec fun_arity(Node::cerl()) -> arity()
%%
%% @doc Returns the number of parameter subtrees of an abstract
%% fun-expression.
@@ -2493,7 +2511,7 @@ fun_body(Node) ->
%% @see c_fun/2
%% @see fun_vars/1
--spec fun_arity(c_fun()) -> non_neg_integer().
+-spec fun_arity(c_fun()) -> arity().
fun_arity(Node) ->
length(fun_vars(Node)).
@@ -3400,7 +3418,7 @@ apply_args(Node) ->
Node#c_apply.args.
-%% @spec apply_arity(Node::cerl()) -> integer()
+%% @spec apply_arity(Node::cerl()) -> arity()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% function application.
@@ -3412,7 +3430,7 @@ apply_args(Node) ->
%% @see c_apply/2
%% @see apply_args/1
--spec apply_arity(c_apply()) -> non_neg_integer().
+-spec apply_arity(c_apply()) -> arity().
apply_arity(Node) ->
length(apply_args(Node)).
@@ -3518,7 +3536,7 @@ call_args(Node) ->
Node#c_call.args.
-%% @spec call_arity(Node::cerl()) -> integer()
+%% @spec call_arity(Node::cerl()) -> arity()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% inter-module call.
@@ -3530,7 +3548,7 @@ call_args(Node) ->
%% @see c_call/3
%% @see call_args/1
--spec call_arity(c_call()) -> non_neg_integer().
+-spec call_arity(c_call()) -> arity().
call_arity(Node) ->
length(call_args(Node)).
@@ -3622,7 +3640,7 @@ primop_args(Node) ->
Node#c_primop.args.
-%% @spec primop_arity(Node::cerl()) -> integer()
+%% @spec primop_arity(Node::cerl()) -> arity()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% primitive operation call.
@@ -3634,7 +3652,7 @@ primop_args(Node) ->
%% @see c_primop/2
%% @see primop_args/1
--spec primop_arity(c_primop()) -> non_neg_integer().
+-spec primop_arity(c_primop()) -> arity().
primop_arity(Node) ->
length(primop_args(Node)).
diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index 4d2f1ebd1b..805095e30c 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index cbcacf9e8e..2a8cf2e758 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 2c9b72a30b..b3decbec1f 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
-module(cerl_trees).
-export([depth/1, fold/3, free_variables/1, get_label/1, label/1, label/2,
- map/2, mapfold/3, size/1, variables/1]).
+ map/2, mapfold/3, mapfold/4, size/1, variables/1]).
-import(cerl, [alias_pat/1, alias_var/1, ann_c_alias/3, ann_c_apply/3,
ann_c_binary/2, ann_c_bitstr/6, ann_c_call/4,
@@ -61,6 +61,7 @@
map_arg/1, map_es/1,
ann_c_map/3,
update_c_map/3,
+ is_c_map_pattern/1, ann_c_map_pattern/2,
map_pair_key/1,map_pair_val/1,map_pair_op/1,
ann_c_map_pair/4,
update_c_map_pair/4
@@ -340,136 +341,162 @@ fold_pairs(_, S, []) ->
%% starting with the given value <code>Initial</code>, while doing a
%% post-order traversal of the tree, much like <code>fold/3</code>.
%%
+%% This is the same as mapfold/4, with an identity function as the
+%% pre-operation.
+%%
%% @see map/2
%% @see fold/3
+%% @see mapfold/4
-spec mapfold(fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}),
term(), cerl:cerl()) -> {cerl:cerl(), term()}.
mapfold(F, S0, T) ->
+ mapfold(fun(T0, A) -> {T0, A} end, F, S0, T).
+
+
+%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) ->
+%% {cerl(), term()}
+%%
+%% Pre = Post = (cerl(), term()) -> {cerl(), term()}
+%%
+%% @doc Does a combined map/fold operation on the nodes of the
+%% tree. It begins by calling <code>Pre</code> on the tree, using the
+%% <code>Initial</code> value. It then deconstructs the top node of
+%% the returned tree and recurses on the children, using the returned
+%% value as the new initial and carrying the returned values from one
+%% call to the next. Finally it reassembles the top node from the
+%% children, calls <code>Post</code> on it and returns the result.
+
+-spec mapfold(fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}),
+ fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}),
+ term(), cerl:cerl()) -> {cerl:cerl(), term()}.
+
+mapfold(Pre, Post, S00, T0) ->
+ {T, S0} = Pre(T0, S00),
case type(T) of
literal ->
case concrete(T) of
[_ | _] ->
- {T1, S1} = mapfold(F, S0, cons_hd(T)),
- {T2, S2} = mapfold(F, S1, cons_tl(T)),
- F(update_c_cons(T, T1, T2), S2);
+ {T1, S1} = mapfold(Pre, Post, S0, cons_hd(T)),
+ {T2, S2} = mapfold(Pre, Post, S1, cons_tl(T)),
+ Post(update_c_cons(T, T1, T2), S2);
V when tuple_size(V) > 0 ->
- {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
- F(update_c_tuple(T, Ts), S1);
+ {Ts, S1} = mapfold_list(Pre, Post, S0, tuple_es(T)),
+ Post(update_c_tuple(T, Ts), S1);
_ ->
- F(T, S0)
+ Post(T, S0)
end;
var ->
- F(T, S0);
+ Post(T, S0);
values ->
- {Ts, S1} = mapfold_list(F, S0, values_es(T)),
- F(update_c_values(T, Ts), S1);
+ {Ts, S1} = mapfold_list(Pre, Post, S0, values_es(T)),
+ Post(update_c_values(T, Ts), S1);
cons ->
- {T1, S1} = mapfold(F, S0, cons_hd(T)),
- {T2, S2} = mapfold(F, S1, cons_tl(T)),
- F(update_c_cons_skel(T, T1, T2), S2);
+ {T1, S1} = mapfold(Pre, Post, S0, cons_hd(T)),
+ {T2, S2} = mapfold(Pre, Post, S1, cons_tl(T)),
+ Post(update_c_cons_skel(T, T1, T2), S2);
tuple ->
- {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
- F(update_c_tuple_skel(T, Ts), S1);
+ {Ts, S1} = mapfold_list(Pre, Post, S0, tuple_es(T)),
+ Post(update_c_tuple_skel(T, Ts), S1);
map ->
- {M , S1} = mapfold(F, S0, map_arg(T)),
- {Ts, S2} = mapfold_list(F, S1, map_es(T)),
- F(update_c_map(T, M, Ts), S2);
+ {M , S1} = mapfold(Pre, Post, S0, map_arg(T)),
+ {Ts, S2} = mapfold_list(Pre, Post, S1, map_es(T)),
+ Post(update_c_map(T, M, Ts), S2);
map_pair ->
- {Op, S1} = mapfold(F, S0, map_pair_op(T)),
- {Key, S2} = mapfold(F, S1, map_pair_key(T)),
- {Val, S3} = mapfold(F, S2, map_pair_val(T)),
- F(update_c_map_pair(T,Op,Key,Val), S3);
+ {Op, S1} = mapfold(Pre, Post, S0, map_pair_op(T)),
+ {Key, S2} = mapfold(Pre, Post, S1, map_pair_key(T)),
+ {Val, S3} = mapfold(Pre, Post, S2, map_pair_val(T)),
+ Post(update_c_map_pair(T,Op,Key,Val), S3);
'let' ->
- {Vs, S1} = mapfold_list(F, S0, let_vars(T)),
- {A, S2} = mapfold(F, S1, let_arg(T)),
- {B, S3} = mapfold(F, S2, let_body(T)),
- F(update_c_let(T, Vs, A, B), S3);
+ {Vs, S1} = mapfold_list(Pre, Post, S0, let_vars(T)),
+ {A, S2} = mapfold(Pre, Post, S1, let_arg(T)),
+ {B, S3} = mapfold(Pre, Post, S2, let_body(T)),
+ Post(update_c_let(T, Vs, A, B), S3);
seq ->
- {A, S1} = mapfold(F, S0, seq_arg(T)),
- {B, S2} = mapfold(F, S1, seq_body(T)),
- F(update_c_seq(T, A, B), S2);
+ {A, S1} = mapfold(Pre, Post, S0, seq_arg(T)),
+ {B, S2} = mapfold(Pre, Post, S1, seq_body(T)),
+ Post(update_c_seq(T, A, B), S2);
apply ->
- {E, S1} = mapfold(F, S0, apply_op(T)),
- {As, S2} = mapfold_list(F, S1, apply_args(T)),
- F(update_c_apply(T, E, As), S2);
+ {E, S1} = mapfold(Pre, Post, S0, apply_op(T)),
+ {As, S2} = mapfold_list(Pre, Post, S1, apply_args(T)),
+ Post(update_c_apply(T, E, As), S2);
call ->
- {M, S1} = mapfold(F, S0, call_module(T)),
- {N, S2} = mapfold(F, S1, call_name(T)),
- {As, S3} = mapfold_list(F, S2, call_args(T)),
- F(update_c_call(T, M, N, As), S3);
+ {M, S1} = mapfold(Pre, Post, S0, call_module(T)),
+ {N, S2} = mapfold(Pre, Post, S1, call_name(T)),
+ {As, S3} = mapfold_list(Pre, Post, S2, call_args(T)),
+ Post(update_c_call(T, M, N, As), S3);
primop ->
- {N, S1} = mapfold(F, S0, primop_name(T)),
- {As, S2} = mapfold_list(F, S1, primop_args(T)),
- F(update_c_primop(T, N, As), S2);
+ {N, S1} = mapfold(Pre, Post, S0, primop_name(T)),
+ {As, S2} = mapfold_list(Pre, Post, S1, primop_args(T)),
+ Post(update_c_primop(T, N, As), S2);
'case' ->
- {A, S1} = mapfold(F, S0, case_arg(T)),
- {Cs, S2} = mapfold_list(F, S1, case_clauses(T)),
- F(update_c_case(T, A, Cs), S2);
+ {A, S1} = mapfold(Pre, Post, S0, case_arg(T)),
+ {Cs, S2} = mapfold_list(Pre, Post, S1, case_clauses(T)),
+ Post(update_c_case(T, A, Cs), S2);
clause ->
- {Ps, S1} = mapfold_list(F, S0, clause_pats(T)),
- {G, S2} = mapfold(F, S1, clause_guard(T)),
- {B, S3} = mapfold(F, S2, clause_body(T)),
- F(update_c_clause(T, Ps, G, B), S3);
+ {Ps, S1} = mapfold_list(Pre, Post, S0, clause_pats(T)),
+ {G, S2} = mapfold(Pre, Post, S1, clause_guard(T)),
+ {B, S3} = mapfold(Pre, Post, S2, clause_body(T)),
+ Post(update_c_clause(T, Ps, G, B), S3);
alias ->
- {V, S1} = mapfold(F, S0, alias_var(T)),
- {P, S2} = mapfold(F, S1, alias_pat(T)),
- F(update_c_alias(T, V, P), S2);
+ {V, S1} = mapfold(Pre, Post, S0, alias_var(T)),
+ {P, S2} = mapfold(Pre, Post, S1, alias_pat(T)),
+ Post(update_c_alias(T, V, P), S2);
'fun' ->
- {Vs, S1} = mapfold_list(F, S0, fun_vars(T)),
- {B, S2} = mapfold(F, S1, fun_body(T)),
- F(update_c_fun(T, Vs, B), S2);
+ {Vs, S1} = mapfold_list(Pre, Post, S0, fun_vars(T)),
+ {B, S2} = mapfold(Pre, Post, S1, fun_body(T)),
+ Post(update_c_fun(T, Vs, B), S2);
'receive' ->
- {Cs, S1} = mapfold_list(F, S0, receive_clauses(T)),
- {E, S2} = mapfold(F, S1, receive_timeout(T)),
- {A, S3} = mapfold(F, S2, receive_action(T)),
- F(update_c_receive(T, Cs, E, A), S3);
+ {Cs, S1} = mapfold_list(Pre, Post, S0, receive_clauses(T)),
+ {E, S2} = mapfold(Pre, Post, S1, receive_timeout(T)),
+ {A, S3} = mapfold(Pre, Post, S2, receive_action(T)),
+ Post(update_c_receive(T, Cs, E, A), S3);
'try' ->
- {E, S1} = mapfold(F, S0, try_arg(T)),
- {Vs, S2} = mapfold_list(F, S1, try_vars(T)),
- {B, S3} = mapfold(F, S2, try_body(T)),
- {Evs, S4} = mapfold_list(F, S3, try_evars(T)),
- {H, S5} = mapfold(F, S4, try_handler(T)),
- F(update_c_try(T, E, Vs, B, Evs, H), S5);
+ {E, S1} = mapfold(Pre, Post, S0, try_arg(T)),
+ {Vs, S2} = mapfold_list(Pre, Post, S1, try_vars(T)),
+ {B, S3} = mapfold(Pre, Post, S2, try_body(T)),
+ {Evs, S4} = mapfold_list(Pre, Post, S3, try_evars(T)),
+ {H, S5} = mapfold(Pre, Post, S4, try_handler(T)),
+ Post(update_c_try(T, E, Vs, B, Evs, H), S5);
'catch' ->
- {B, S1} = mapfold(F, S0, catch_body(T)),
- F(update_c_catch(T, B), S1);
+ {B, S1} = mapfold(Pre, Post, S0, catch_body(T)),
+ Post(update_c_catch(T, B), S1);
binary ->
- {Ds, S1} = mapfold_list(F, S0, binary_segments(T)),
- F(update_c_binary(T, Ds), S1);
+ {Ds, S1} = mapfold_list(Pre, Post, S0, binary_segments(T)),
+ Post(update_c_binary(T, Ds), S1);
bitstr ->
- {Val, S1} = mapfold(F, S0, bitstr_val(T)),
- {Size, S2} = mapfold(F, S1, bitstr_size(T)),
- {Unit, S3} = mapfold(F, S2, bitstr_unit(T)),
- {Type, S4} = mapfold(F, S3, bitstr_type(T)),
- {Flags, S5} = mapfold(F, S4, bitstr_flags(T)),
- F(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5);
+ {Val, S1} = mapfold(Pre, Post, S0, bitstr_val(T)),
+ {Size, S2} = mapfold(Pre, Post, S1, bitstr_size(T)),
+ {Unit, S3} = mapfold(Pre, Post, S2, bitstr_unit(T)),
+ {Type, S4} = mapfold(Pre, Post, S3, bitstr_type(T)),
+ {Flags, S5} = mapfold(Pre, Post, S4, bitstr_flags(T)),
+ Post(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5);
letrec ->
- {Ds, S1} = mapfold_pairs(F, S0, letrec_defs(T)),
- {B, S2} = mapfold(F, S1, letrec_body(T)),
- F(update_c_letrec(T, Ds, B), S2);
+ {Ds, S1} = mapfold_pairs(Pre, Post, S0, letrec_defs(T)),
+ {B, S2} = mapfold(Pre, Post, S1, letrec_body(T)),
+ Post(update_c_letrec(T, Ds, B), S2);
module ->
- {N, S1} = mapfold(F, S0, module_name(T)),
- {Es, S2} = mapfold_list(F, S1, module_exports(T)),
- {As, S3} = mapfold_pairs(F, S2, module_attrs(T)),
- {Ds, S4} = mapfold_pairs(F, S3, module_defs(T)),
- F(update_c_module(T, N, Es, As, Ds), S4)
+ {N, S1} = mapfold(Pre, Post, S0, module_name(T)),
+ {Es, S2} = mapfold_list(Pre, Post, S1, module_exports(T)),
+ {As, S3} = mapfold_pairs(Pre, Post, S2, module_attrs(T)),
+ {Ds, S4} = mapfold_pairs(Pre, Post, S3, module_defs(T)),
+ Post(update_c_module(T, N, Es, As, Ds), S4)
end.
-mapfold_list(F, S0, [T | Ts]) ->
- {T1, S1} = mapfold(F, S0, T),
- {Ts1, S2} = mapfold_list(F, S1, Ts),
+mapfold_list(Pre, Post, S0, [T | Ts]) ->
+ {T1, S1} = mapfold(Pre, Post, S0, T),
+ {Ts1, S2} = mapfold_list(Pre, Post, S1, Ts),
{[T1 | Ts1], S2};
-mapfold_list(_, S, []) ->
+mapfold_list(_, _, S, []) ->
{[], S}.
-mapfold_pairs(F, S0, [{T1, T2} | Ps]) ->
- {T3, S1} = mapfold(F, S0, T1),
- {T4, S2} = mapfold(F, S1, T2),
- {Ps1, S3} = mapfold_pairs(F, S2, Ps),
+mapfold_pairs(Pre, Post, S0, [{T1, T2} | Ps]) ->
+ {T3, S1} = mapfold(Pre, Post, S0, T1),
+ {T4, S2} = mapfold(Pre, Post, S1, T2),
+ {Ps1, S3} = mapfold_pairs(Pre, Post, S2, Ps),
{[{T3, T4} | Ps1], S3};
-mapfold_pairs(_, S, []) ->
+mapfold_pairs(_, _, S, []) ->
{[], S}.
@@ -640,8 +667,8 @@ vars_in_list([], _, A) ->
vars_in_defs(Ds, S) ->
vars_in_defs(Ds, S, []).
-vars_in_defs([{_, F} | Ds], S, A) ->
- vars_in_defs(Ds, S, ordsets:union(variables(F, S), A));
+vars_in_defs([{_, Post} | Ds], S, A) ->
+ vars_in_defs(Ds, S, ordsets:union(variables(Post, S), A));
vars_in_defs([], _, A) ->
A.
@@ -703,13 +730,14 @@ label(T, N, Env) ->
%% Constant literals are not labeled.
{T, N};
var ->
- case dict:find(var_name(T), Env) of
- {ok, L} ->
- {As, _} = label_ann(T, L),
- N1 = N;
- error ->
- {As, N1} = label_ann(T, N)
- end,
+ {As, N1} =
+ case dict:find(var_name(T), Env) of
+ {ok, L} ->
+ {A, _} = label_ann(T, L),
+ {A, N};
+ error ->
+ label_ann(T, N)
+ end,
{set_ann(T, As), N1};
values ->
{Ts, N1} = label_list(values_es(T), N, Env),
@@ -725,14 +753,21 @@ label(T, N, Env) ->
{As, N2} = label_ann(T, N1),
{ann_c_tuple_skel(As, Ts), N2};
map ->
- {M, N1} = label(map_arg(T), N, Env),
- {Ts, N2} = label_list(map_es(T), N1, Env),
- {As, N3} = label_ann(T, N2),
- {ann_c_map(As, M, Ts), N3};
+ case is_c_map_pattern(T) of
+ false ->
+ {M, N1} = label(map_arg(T), N, Env),
+ {Ts, N2} = label_list(map_es(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_map(As, M, Ts), N3};
+ true ->
+ {Ts, N1} = label_list(map_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_map_pattern(As, Ts), N2}
+ end;
map_pair ->
{Op, N1} = label(map_pair_op(T), N, Env),
- {Val, N2} = label(map_pair_key(T), N1, Env),
- {Key, N3} = label(map_pair_val(T), N2, Env),
+ {Key, N2} = label(map_pair_key(T), N1, Env),
+ {Val, N3} = label(map_pair_val(T), N2, Env),
{As, N4} = label_ann(T, N3),
{ann_c_map_pair(As,Op,Key,Val), N4};
'let' ->
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index e0a29fe9b1..97d63d399a 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -26,6 +26,7 @@
-export([forms/1,forms/2,noenv_forms/2]).
-export([output_generated/1,noenv_output_generated/1]).
-export([options/0]).
+-export([env_compiler_options/0]).
%% Erlc interface.
-export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
@@ -40,6 +41,12 @@
%%----------------------------------------------------------------------
+-type abstract_code() :: [erl_parse:abstract_form()].
+
+%% Internal representations used for 'from_asm' and 'from_beam' compilation can
+%% also be valid, but have no relevant types defined.
+-type forms() :: abstract_code() | cerl:c_module().
+
-type option() :: atom() | {atom(), term()} | {'d', atom(), term()}.
-type err_info() :: {erl_anno:line() | 'none',
@@ -48,6 +55,9 @@
-type warnings() :: [{file:filename(), [err_info()]}].
-type mod_ret() :: {'ok', module()}
| {'ok', module(), cerl:c_module()} %% with option 'to_core'
+ | {'ok', %% with option 'to_pp'
+ module() | [], %% module() if 'to_exp'
+ abstract_code()}
| {'ok', module(), warnings()}.
-type bin_ret() :: {'ok', module(), binary()}
| {'ok', module(), binary(), warnings()}.
@@ -78,7 +88,11 @@ file(File, Opts) when is_list(Opts) ->
file(File, Opt) ->
file(File, [Opt|?DEFAULT_OPTIONS]).
-forms(File) -> forms(File, ?DEFAULT_OPTIONS).
+-spec forms(abstract_code()) -> comp_ret().
+
+forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS).
+
+-spec forms(forms(), [option()] | option()) -> comp_ret().
forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
@@ -106,6 +120,8 @@ noenv_file(File, Opts) when is_list(Opts) ->
noenv_file(File, Opt) ->
noenv_file(File, [Opt|?DEFAULT_OPTIONS]).
+-spec noenv_forms(forms(), [option()] | option()) -> comp_ret().
+
noenv_forms(Forms, Opts) when is_list(Opts) ->
do_compile({forms,Forms}, [binary|Opts]);
noenv_forms(Forms, Opt) when is_atom(Opt) ->
@@ -120,6 +136,14 @@ noenv_output_generated(Opts) ->
end, Passes).
%%
+%% Retrieve ERL_COMPILER_OPTIONS as a list of terms
+%%
+
+-spec env_compiler_options() -> [term()].
+
+env_compiler_options() -> env_default_opts().
+
+%%
%% Local functions
%%
@@ -216,6 +240,8 @@ format_error({epp,E}) ->
epp:format_error(E);
format_error(write_error) ->
"error writing file";
+format_error({write_error, Error}) ->
+ io_lib:format("error writing file: ~ts", [file:format_error(Error)]);
format_error({rename,From,To,Error}) ->
io_lib:format("failed to rename ~ts to ~ts: ~ts",
[From,To,file:format_error(Error)]);
@@ -658,7 +684,7 @@ kernel_passes() ->
{iff,core,?pass(save_core_code)},
%% Kernel Erlang and code generation.
- {pass,v3_kernel},
+ ?pass(v3_kernel),
{iff,dkern,{listing,"kernel"}},
{iff,'to_kernel',{done,"kernel"}},
{pass,v3_life},
@@ -671,11 +697,16 @@ asm_passes() ->
%% Assembly level optimisations.
[{delay,
[{pass,beam_a},
+ {iff,da,{listing,"a"}},
{unless,no_postopt,
- [{pass,beam_block},
+ [{unless,no_reorder,{pass,beam_reorder}},
+ {iff,dre,{listing,"reorder"}},
+ {pass,beam_block},
{iff,dblk,{listing,"block"}},
{unless,no_except,{pass,beam_except}},
{iff,dexcept,{listing,"except"}},
+ {unless,no_bs_opt,{pass,beam_bs}},
+ {iff,dbs,{listing,"bs"}},
{unless,no_bopt,{pass,beam_bool}},
{iff,dbool,{listing,"bool"}},
{unless,no_topt,{pass,beam_type}},
@@ -703,6 +734,7 @@ asm_passes() ->
{iff,no_postopt,[{pass,beam_clean}]},
{pass,beam_z},
+ {iff,dz,{listing,"z"}},
{iff,dopt,{listing,"optimize"}},
{iff,'S',{listing,"S"}},
{iff,'to_asm',{done,"S"}}]},
@@ -1180,7 +1212,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) ->
end,
{ok,St}
catch
- exit:_ ->
+ error:_ ->
%% Couldn't write to output Makefile.
Err = {St#compile.ifile,[{none,?MODULE,write_error}]},
{error,St#compile{errors=St#compile.errors++[Err]}}
@@ -1209,6 +1241,17 @@ core_fold_module_after_inlining(#compile{code=Code0,options=Opts}=St) ->
{ok,Code,_Ws} = sys_core_fold:module(Code0, Opts),
{ok,St#compile{code=Code}}.
+v3_kernel(#compile{code=Code0,options=Opts,warnings=Ws0}=St) ->
+ {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
+ case Ws =:= [] orelse test_core_inliner(St) of
+ false ->
+ {ok,St#compile{code=Code,warnings=Ws0++Ws}};
+ true ->
+ %% cerl_inline may produce code that generates spurious
+ %% warnings. Ignore any such warnings.
+ {ok,St#compile{code=Code}}
+ end.
+
test_old_inliner(#compile{options=Opts}) ->
%% The point of this test is to avoid loading the old inliner
%% if we know that it will not be used.
@@ -1300,27 +1343,18 @@ generate_key(String) when is_list(String) ->
encrypt({des3_cbc=Type,Key,IVec,BlockSize}, Bin0) ->
Bin1 = case byte_size(Bin0) rem BlockSize of
0 -> Bin0;
- N -> list_to_binary([Bin0,random_bytes(BlockSize-N)])
+ N -> list_to_binary([Bin0,crypto:strong_rand_bytes(BlockSize-N)])
end,
Bin = crypto:block_encrypt(Type, Key, IVec, Bin1),
TypeString = atom_to_list(Type),
list_to_binary([0,length(TypeString),TypeString,Bin]).
-random_bytes(N) ->
- _ = random:seed(erlang:time_offset(),
- erlang:monotonic_time(),
- erlang:unique_integer()),
- random_bytes_1(N, []).
-
-random_bytes_1(0, Acc) -> Acc;
-random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]).
-
save_core_code(St) ->
{ok,St#compile{core_code=cerl:from_records(St#compile.code)}}.
beam_asm(#compile{ifile=File,code=Code0,
abstract_code=Abst,mod_options=Opts0}=St) ->
- Source = filename:absname(File),
+ Source = paranoid_absname(File),
Opts1 = lists:map(fun({debug_info_key,_}) -> {debug_info_key,'********'};
(Other) -> Other
end, Opts0),
@@ -1329,6 +1363,16 @@ beam_asm(#compile{ifile=File,code=Code0,
{ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}}
end.
+paranoid_absname(""=File) ->
+ File;
+paranoid_absname(File) ->
+ case file:get_cwd() of
+ {ok,Cwd} ->
+ filename:absname(File, Cwd);
+ _ ->
+ File
+ end.
+
test_native(#compile{options=Opts}) ->
%% This test is done late, in case some other option has turned off native.
%% 'native' given on the command line can be overridden by
@@ -1452,8 +1496,8 @@ save_binary_1(St) ->
end,
{error,St#compile{errors=St#compile.errors ++ Es}}
end;
- {error,_Error} ->
- Es = [{Tfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Tfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
@@ -1601,8 +1645,8 @@ listing(LFun, Ext, St) ->
LFun(Lf, Code),
ok = file:close(Lf),
{ok,St};
- {error,_Error} ->
- Es = [{Lfile,[{none,compile,write_error}]}],
+ {error,Error} ->
+ Es = [{Lfile,[{none,compile,{write_error,Error}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
@@ -1612,11 +1656,8 @@ output_encoding(F, #compile{encoding = Encoding}) ->
ok = io:setopts(F, [{encoding, Encoding}]),
ok = io:fwrite(F, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
-restore_expanded_types("P", Fs) ->
- epp:restore_typed_record_fields(Fs);
restore_expanded_types("E", {M,I,Fs0}) ->
- Fs1 = restore_expand_module(Fs0),
- Fs = epp:restore_typed_record_fields(Fs1),
+ Fs = restore_expand_module(Fs0),
{M,I,Fs};
restore_expanded_types(_Ext, Code) -> Code.
@@ -1628,6 +1669,8 @@ restore_expand_module([{attribute,Line,spec,[Arg]}|Fs]) ->
[{attribute,Line,spec,Arg}|restore_expand_module(Fs)];
restore_expand_module([{attribute,Line,callback,[Arg]}|Fs]) ->
[{attribute,Line,callback,Arg}|restore_expand_module(Fs)];
+restore_expand_module([{attribute,Line,record,[R]}|Fs]) ->
+ [{attribute,Line,record,R}|restore_expand_module(Fs)];
restore_expand_module([F|Fs]) ->
[F|restore_expand_module(Fs)];
restore_expand_module([]) -> [].
@@ -1674,6 +1717,7 @@ help(_) ->
%% Compile entry point for erl_compile.
compile(File0, _OutFile, Options) ->
+ pre_load(),
File = shorten_filename(File0),
case file(File, make_erl_options(Options)) of
{ok,_Mod} -> ok;
@@ -1738,3 +1782,47 @@ make_erl_options(Opts) ->
end,
Options ++ [report_errors, {cwd, Cwd}, {outdir, Outdir}|
[{i, Dir} || Dir <- Includes]] ++ Specific.
+
+pre_load() ->
+ L = [beam_a,
+ beam_asm,
+ beam_block,
+ beam_bool,
+ beam_bs,
+ beam_bsm,
+ beam_clean,
+ beam_dead,
+ beam_dict,
+ beam_except,
+ beam_flatten,
+ beam_jump,
+ beam_opcodes,
+ beam_peep,
+ beam_receive,
+ beam_reorder,
+ beam_split,
+ beam_trim,
+ beam_type,
+ beam_utils,
+ beam_validator,
+ beam_z,
+ cerl,
+ cerl_clauses,
+ cerl_sets,
+ cerl_trees,
+ core_lib,
+ epp,
+ erl_bifs,
+ erl_expand_records,
+ erl_lint,
+ erl_parse,
+ erl_scan,
+ sys_core_dsetel,
+ sys_core_fold,
+ sys_pre_expand,
+ v3_codegen,
+ v3_core,
+ v3_kernel,
+ v3_life],
+ _ = code:ensure_modules_loaded(L),
+ ok.
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index afb85f4710..1fd7800e85 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
beam_asm,
beam_block,
beam_bool,
+ beam_bs,
beam_bsm,
beam_clean,
beam_dead,
@@ -37,6 +38,7 @@
beam_opcodes,
beam_peep,
beam_receive,
+ beam_reorder,
beam_split,
beam_trim,
beam_type,
diff --git a/lib/compiler/src/compiler.appup.src b/lib/compiler/src/compiler.appup.src
index 3ada2e933f..e9db37e203 100644
--- a/lib/compiler/src/compiler.appup.src
+++ b/lib/compiler/src/compiler.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index 3abb520485..c1806272bd 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,52 +21,16 @@
-module(core_lib).
--deprecated({get_anno,1,next_major_release}).
--deprecated({set_anno,2,next_major_release}).
--deprecated({is_literal,1,next_major_release}).
--deprecated({is_literal_list,1,next_major_release}).
--deprecated({literal_value,1,next_major_release}).
-
--export([get_anno/1,set_anno/2]).
--export([is_literal/1,is_literal_list/1]).
--export([literal_value/1]).
-export([make_values/1]).
-export([is_var_used/2]).
-include("core_parse.hrl").
-%%
-%% Generic get/set annotation that should be used only with cerl() structures.
-%%
--spec get_anno(cerl:cerl()) -> term().
-
-get_anno(C) -> cerl:get_ann(C).
-
--spec set_anno(cerl:cerl(), term()) -> cerl:cerl().
-
-set_anno(C, A) -> cerl:set_ann(C, A).
-
--spec is_literal(cerl:cerl()) -> boolean().
-
-is_literal(Cerl) ->
- cerl:is_literal(cerl:fold_literal(Cerl)).
-
--spec is_literal_list([cerl:cerl()]) -> boolean().
-
-is_literal_list(Es) -> lists:all(fun is_literal/1, Es).
-
-%% Return the value of LitExpr.
--spec literal_value(cerl:c_literal() | cerl:c_binary() |
- cerl:c_map() | cerl:c_cons() | cerl:c_tuple()) -> term().
-
-literal_value(Cerl) ->
- cerl:concrete(cerl:fold_literal(Cerl)).
-
%% Make a suitable values structure, expr or values, depending on Expr.
-spec make_values([cerl:cerl()] | cerl:cerl()) -> cerl:cerl().
make_values([E]) -> E;
-make_values([H|_]=Es) -> #c_values{anno=get_anno(H),es=Es};
+make_values([H|_]=Es) -> #c_values{anno=cerl:get_ann(H),es=Es};
make_values([]) -> #c_values{es=[]};
make_values(E) -> E.
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index cc54f6e411..7d3513c0ba 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2015. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@
%% Define the lint state record.
-record(lint, {module :: module(), % Current module
- func :: fa(), % Current function
+ func :: fa() | 'undefined', % Current function
errors = [] :: [error()], % Errors
warnings= [] :: [warning()]}). % Warnings
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index ecf6cc9956..83a6f0179c 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index 59cfc97ebd..8028aa99bb 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -1,7 +1,7 @@
%% -*-Erlang-*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,12 +47,14 @@ receive_expr timeout try_expr
sequence catch_expr
variable clause clause_pattern
-map_expr map_pairs map_pair map_pair_assoc map_pair_exact
+map_expr anno_map_expr map_pairs anno_map_pair map_pair map_pair_assoc map_pair_exact
map_pattern map_pair_patterns map_pair_pattern
-annotation anno_fun anno_expression anno_expressions
+annotation anno_atom anno_fun anno_expression anno_expressions
anno_variable anno_variables anno_pattern anno_patterns
anno_function_name
+anno_literal
+anno_segment anno_segment_pattern
anno_clause anno_clauses.
Terminals
@@ -90,7 +92,7 @@ module_definition ->
module_definition ->
'(' 'module' atom module_export module_attribute module_defs 'end'
'-|' annotation ')' :
- #c_module{anno='$9',name=tok_val('$3'),exports='$4',
+ #c_module{anno='$9',name=#c_literal{val=tok_val('$3')},exports='$4',
attrs='$5',defs='$6'}.
module_export -> '[' ']' : [].
@@ -99,7 +101,7 @@ module_export -> '[' exported_names ']' : '$2'.
exported_names -> exported_name ',' exported_names : ['$1' | '$3'].
exported_names -> exported_name : ['$1'].
-exported_name -> function_name : '$1'.
+exported_name -> anno_function_name : '$1'.
module_attribute -> 'attributes' '[' ']' : [].
module_attribute -> 'attributes' '[' attribute_list ']' : '$3'.
@@ -107,8 +109,16 @@ module_attribute -> 'attributes' '[' attribute_list ']' : '$3'.
attribute_list -> attribute ',' attribute_list : ['$1' | '$3'].
attribute_list -> attribute : ['$1'].
-attribute -> atom '=' literal :
- {#c_literal{val=tok_val('$1')},'$3'}.
+attribute -> anno_atom '=' anno_literal :
+ {'$1','$3'}.
+
+anno_atom -> atom :
+ cerl:c_atom(tok_val('$1')).
+anno_atom -> '(' atom '-|' annotation ')' :
+ cerl:ann_c_atom('$4', tok_val('$2')).
+
+anno_literal -> literal : '$1'.
+anno_literal -> '(' literal '-|' annotation ')' : cerl:set_ann('$2', '$4').
module_defs -> function_definitions : '$1'.
@@ -186,7 +196,9 @@ tuple_pattern -> '{' anno_patterns '}' : c_tuple('$2').
map_pattern -> '~' '{' '}' '~' : c_map_pattern([]).
map_pattern -> '~' '{' map_pair_patterns '}' '~' :
- c_map_pattern(lists:sort('$3')).
+ c_map_pattern('$3').
+map_pattern -> '~' '{' map_pair_patterns '|' anno_map_expr '}' '~' :
+ ann_c_map_pattern('$5', '$3').
map_pair_patterns -> map_pair_pattern : ['$1'].
map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
@@ -194,6 +206,9 @@ map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3'].
map_pair_pattern -> anno_expression ':=' anno_pattern :
#c_map_pair{op=#c_literal{val=exact},
key='$1',val='$3'}.
+map_pair_pattern -> '(' anno_expression ':=' anno_pattern '-|' annotation ')' :
+ #c_map_pair{anno='$6',op=#c_literal{val=exact},
+ key='$2',val='$4'}.
cons_pattern -> '[' anno_pattern tail_pattern :
c_cons('$2', '$3').
@@ -206,8 +221,12 @@ tail_pattern -> ',' anno_pattern tail_pattern :
binary_pattern -> '#' '{' '}' '#' : #c_binary{segments=[]}.
binary_pattern -> '#' '{' segment_patterns '}' '#' : #c_binary{segments='$3'}.
-segment_patterns -> segment_pattern ',' segment_patterns : ['$1' | '$3'].
-segment_patterns -> segment_pattern : ['$1'].
+segment_patterns -> anno_segment_pattern ',' segment_patterns : ['$1' | '$3'].
+segment_patterns -> anno_segment_pattern : ['$1'].
+
+anno_segment_pattern -> segment_pattern : '$1'.
+anno_segment_pattern -> '(' segment_pattern '-|' annotation ')' :
+ cerl:set_ann('$2', '$4').
segment_pattern -> '#' '<' anno_pattern '>' '(' anno_expressions ')':
case '$6' of
@@ -289,11 +308,17 @@ tuple -> '{' anno_expressions '}' : c_tuple('$2').
map_expr -> '~' '{' '}' '~' : c_map([]).
map_expr -> '~' '{' map_pairs '}' '~' : c_map('$3').
-map_expr -> '~' '{' map_pairs '|' variable '}' '~' : ann_c_map([], '$5', '$3').
-map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : ann_c_map([], '$5', '$3').
+map_expr -> '~' '{' map_pairs '|' anno_variable '}' '~' : ann_c_map([], '$5', '$3').
+map_expr -> '~' '{' map_pairs '|' anno_map_expr '}' '~' : ann_c_map([], '$5', '$3').
-map_pairs -> map_pair : ['$1'].
-map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
+anno_map_expr -> map_expr : '$1'.
+anno_map_expr -> '(' map_expr '-|' annotation ')' : cerl:set_ann('$2', '$4').
+
+map_pairs -> anno_map_pair : ['$1'].
+map_pairs -> anno_map_pair ',' map_pairs : ['$1' | '$3'].
+
+anno_map_pair -> map_pair : '$1'.
+anno_map_pair -> '(' map_pair '-|' annotation ')' : cerl:set_ann('$2', '$4').
map_pair -> map_pair_assoc : '$1'.
map_pair -> map_pair_exact : '$1'.
@@ -312,8 +337,11 @@ tail -> ',' anno_expression tail : c_cons('$2', '$3').
binary -> '#' '{' '}' '#' : #c_literal{val = <<>>}.
binary -> '#' '{' segments '}' '#' : make_binary('$3').
-segments -> segment ',' segments : ['$1' | '$3'].
-segments -> segment : ['$1'].
+segments -> anno_segment ',' segments : ['$1' | '$3'].
+segments -> anno_segment : ['$1'].
+
+anno_segment -> segment : '$1'.
+anno_segment -> '(' segment '-|' annotation ')' : cerl:set_ann('$2', '$4').
segment -> '#' '<' anno_expression '>' '(' anno_expressions ')':
case '$6' of
@@ -413,7 +441,8 @@ Erlang code.
-include("core_parse.hrl").
--import(cerl, [ann_c_map/3,c_cons/2,c_map/1,c_map_pattern/1,c_tuple/1]).
+-import(cerl, [ann_c_map/3,ann_c_map_pattern/2,c_cons/2,c_map/1,
+ c_map_pattern/1,c_tuple/1]).
tok_val(T) -> element(3, T).
tok_line(T) -> element(2, T).
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index c2a6a81d5e..67209d06be 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
-module(core_pp).
--export([format/1]).
+-export([format/1,format_all/1]).
-include("core_parse.hrl").
@@ -33,25 +33,35 @@
%% Prettyprint-formats (naively) an abstract Core Erlang syntax
%% tree.
--record(ctxt, {class = term :: 'clause' | 'def' | 'expr' | 'term',
- indent = 0 :: integer(),
+-record(ctxt, {indent = 0 :: integer(),
item_indent = 2 :: integer(),
body_indent = 4 :: integer(),
- tab_width = 8 :: non_neg_integer(),
- line = 0 :: integer()}).
+ line = 0 :: integer(),
+ clean = true :: boolean()}).
+
+-define(TAB_WIDTH, 8).
-spec format(cerl:cerl()) -> iolist().
format(Node) ->
format(Node, #ctxt{}).
-maybe_anno(Node, Fun, Ctxt) ->
+-spec format_all(cerl:cerl()) -> iolist().
+
+format_all(Node) ->
+ format(Node, #ctxt{clean=false}).
+
+maybe_anno(Node, Fun, #ctxt{clean=false}=Ctxt) ->
As = cerl:get_ann(Node),
- case get_line(As) of
+ maybe_anno(Node, Fun, Ctxt, As);
+maybe_anno(Node, Fun, #ctxt{clean=true}=Ctxt) ->
+ As0 = cerl:get_ann(Node),
+ case get_line(As0) of
none ->
- maybe_anno(Node, Fun, Ctxt, As);
+ maybe_anno(Node, Fun, Ctxt, As0);
Line ->
- if Line > Ctxt#ctxt.line ->
+ As = strip_line(As0),
+ if Line > Ctxt#ctxt.line ->
[io_lib:format("%% Line ~w",[Line]),
nl_indent(Ctxt),
maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As)
@@ -61,22 +71,22 @@ maybe_anno(Node, Fun, Ctxt) ->
end
end.
-maybe_anno(Node, Fun, Ctxt, As) ->
- case strip_line(As) of
- [] ->
- Fun(Node, Ctxt);
- List ->
- Ctxt1 = add_indent(Ctxt, 2),
- Ctxt2 = add_indent(Ctxt1, 3),
- ["( ",
- Fun(Node, Ctxt1),
- nl_indent(Ctxt1),
- "-| ",format_anno(List, Ctxt2)," )"
- ]
- end.
+maybe_anno(Node, Fun, Ctxt, []) ->
+ Fun(Node, Ctxt);
+maybe_anno(Node, Fun, Ctxt, List) ->
+ Ctxt1 = add_indent(Ctxt, 2),
+ Ctxt2 = add_indent(Ctxt1, 3),
+ ["( ",
+ Fun(Node, Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",format_anno(List, Ctxt2)," )"
+ ].
format_anno([_|_]=List, Ctxt) ->
[$[,format_anno_list(List, Ctxt),$]];
+format_anno({file,Name}, _Ctxt) ->
+ %% Optimization: Reduces file size considerably.
+ io_lib:format("{'file',~p}", [Name]);
format_anno(Tuple, Ctxt) when is_tuple(Tuple) ->
[${,format_anno_list(tuple_to_list(Tuple), Ctxt),$}];
format_anno(Val, Ctxt) when is_atom(Val) ->
@@ -121,14 +131,11 @@ format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) ->
format_1(#c_binary{anno=A,segments=Segs}, Ctxt);
format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) ->
Pairs = maps:to_list(M),
- Op = case Ctxt of
- #ctxt{ class = clause } -> exact;
- _ -> assoc
- end,
- Cpairs = [#c_map_pair{op=#c_literal{val=Op},
+ Op = #c_literal{val=assoc},
+ Cpairs = [#c_map_pair{op=Op,
key=#c_literal{val=K},
val=#c_literal{val=V}} || {K,V} <- Pairs],
- format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt);
+ format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt);
format_1(#c_var{name={I,A}}, _) ->
[core_atom(I),$/,integer_to_list(A)];
format_1(#c_var{name=V}, _) ->
@@ -172,7 +179,8 @@ format_1(#c_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 ->
+format_1(#c_map{arg=#c_literal{anno=[],val=M},es=Es}, Ctxt)
+ when is_map(M), map_size(M) =:= 0 ->
["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
@@ -195,9 +203,16 @@ format_1(#c_values{es=Es}, Ctxt) ->
format_1(#c_alias{var=V,pat=P}, Ctxt) ->
Txt = [format(V, Ctxt)|" = "],
[Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))];
-format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) ->
- Vs = [cerl:set_ann(V, []) || V <- Vs0],
- case is_simple_term(A) of
+format_1(#c_let{anno=Anno0,vars=Vs0,arg=A0,body=B}, #ctxt{clean=Clean}=Ctxt) ->
+ {Vs,A,Anno} = case Clean of
+ false ->
+ {Vs0,A0,Anno0};
+ true ->
+ {[cerl:set_ann(V, []) || V <- Vs0],
+ cerl:set_ann(A0, []),
+ []}
+ end,
+ case is_simple_term(A) andalso Anno =:= [] of
false ->
Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
["let ",
@@ -214,7 +229,7 @@ format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) ->
["let ",
format_values(Vs, add_indent(Ctxt, 4)),
" = ",
- format(cerl:set_ann(A, []), Ctxt1),
+ format(A, Ctxt1),
nl_indent(Ctxt),
"in "
| format(B, add_indent(Ctxt, 4))
@@ -321,35 +336,30 @@ format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) ->
[Mod," [",
format_vseq(Es,
"", ",",
- add_indent(set_class(Ctxt, term), width(Mod, Ctxt)+2),
+ add_indent(Ctxt, width(Mod, Ctxt)+2),
fun format/2),
"]",
nl_indent(Ctxt),
" attributes [",
format_vseq(As,
"", ",",
- add_indent(set_class(Ctxt, def), 16),
+ add_indent(Ctxt, 16),
fun format_def/2),
"]",
nl_indent(Ctxt),
format_funcs(Ds, Ctxt),
nl_indent(Ctxt)
| "end"
- ];
-format_1(Type, _) ->
- ["** Unsupported type: ",
- io_lib:write(Type)
- | " **"
].
format_funcs(Fs, Ctxt) ->
format_vseq(Fs,
"", "",
- set_class(Ctxt, def),
+ Ctxt,
fun format_def/2).
format_def({N,V}, Ctxt0) ->
- Ctxt1 = add_indent(set_class(Ctxt0, expr), Ctxt0#ctxt.body_indent),
+ Ctxt1 = add_indent(Ctxt0, Ctxt0#ctxt.body_indent),
[format(N, Ctxt0),
" =",
nl_indent(Ctxt1)
@@ -362,7 +372,10 @@ format_values(Vs, Ctxt) ->
format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2),
$>].
-format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
+format_bitstr(Node, Ctxt) ->
+ maybe_anno(Node, fun do_format_bitstr/2, Ctxt).
+
+do_format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
Vs = [S, U, T, Fs],
Ctxt1 = add_indent(Ctxt0, 2),
Val = format(V, Ctxt1),
@@ -370,8 +383,7 @@ format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)].
format_clauses(Cs, Ctxt) ->
- format_vseq(Cs, "", "", set_class(Ctxt, clause),
- fun format_clause/2).
+ format_vseq(Cs, "", "", Ctxt, fun format_clause/2).
format_clause(Node, Ctxt) ->
maybe_anno(Node, fun format_clause_1/2, Ctxt).
@@ -383,15 +395,13 @@ format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) ->
case is_trivial_guard(G) of
true ->
[" when ",
- format_guard(G, add_indent(set_class(Ctxt, expr),
- width(Ptxt, Ctxt) + 6))];
+ format_guard(G, add_indent(Ctxt, width(Ptxt, Ctxt) + 6))];
false ->
[nl_indent(Ctxt2), "when ",
format_guard(G, add_indent(Ctxt2, 2))]
end++
" ->",
- nl_indent(Ctxt2)
- | format(B, set_class(Ctxt2, expr))
+ nl_indent(Ctxt2) | format(B, Ctxt2)
].
is_trivial_guard(#c_literal{val=Val}) when is_atom(Val) -> true;
@@ -445,50 +455,53 @@ format_list_tail(Tail, Ctxt) ->
format_map_pair(Op, K, V, Ctxt0) ->
Ctxt1 = add_indent(Ctxt0, 1),
- Txt = format(K, set_class(Ctxt1, expr)),
+ Txt = format(K, Ctxt1),
Ctxt2 = add_indent(Ctxt0, width(Txt, Ctxt1)),
[Txt,Op,format(V, Ctxt2)].
-indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
-
-indent(N, _) when N =< 0 -> "";
-indent(N, Ctxt) ->
- T = Ctxt#ctxt.tab_width,
- string:chars($\t, N div T, string:chars($\s, N rem T)).
+indent(#ctxt{indent=N}) ->
+ if
+ N =< 0 ->
+ "";
+ true ->
+ string:chars($\t, N div ?TAB_WIDTH, spaces(N rem ?TAB_WIDTH))
+ end.
nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+spaces(0) -> "";
+spaces(1) -> " ";
+spaces(2) -> " ";
+spaces(3) -> " ";
+spaces(4) -> " ";
+spaces(5) -> " ";
+spaces(6) -> " ";
+spaces(7) -> " ".
+%% Undo indentation done by nl_indent/1.
unindent(T, Ctxt) ->
- unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+ unindent(T, Ctxt#ctxt.indent, []).
-unindent(T, N, _, C) when N =< 0 ->
+unindent(T, N, C) when N =< 0 ->
[T|C];
-unindent([$\s|T], N, Ctxt, C) ->
- unindent(T, N - 1, Ctxt, C);
-unindent([$\t|T], N, Ctxt, C) ->
- Tab = Ctxt#ctxt.tab_width,
+unindent([$\s|T], N, C) ->
+ unindent(T, N - 1, C);
+unindent([$\t|T], N, C) ->
+ Tab = ?TAB_WIDTH,
if N >= Tab ->
- unindent(T, N - Tab, Ctxt, C);
+ unindent(T, N - Tab, C);
true ->
- unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ unindent([spaces(Tab - N)|T], 0, C)
end;
-unindent([L|T], N, Ctxt, C) when is_list(L) ->
- unindent(L, N, Ctxt, [T|C]);
-unindent([H|T], _, _, C) ->
- [H|[T|C]];
-unindent([], N, Ctxt, [H|T]) ->
- unindent(H, N, Ctxt, T);
-unindent([], _, _, []) -> [].
+unindent([L|T], N, C) when is_list(L) ->
+ unindent(L, N, [T|C]).
width(Txt, Ctxt) ->
- try width(Txt, 0, Ctxt, [])
- catch error:_ -> exit({bad_text,Txt})
- end.
+ width(Txt, 0, Ctxt, []).
width([$\t|T], A, Ctxt, C) ->
- width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+ width(T, A + ?TAB_WIDTH, Ctxt, C);
width([$\n|T], _, Ctxt, C) ->
width(unindent([T|C], Ctxt), Ctxt);
width([H|T], A, Ctxt, C) when is_list(H) ->
@@ -502,14 +515,9 @@ width([], A, _, []) -> A.
add_indent(Ctxt, Dx) ->
Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}.
-set_class(Ctxt, Class) ->
- Ctxt#ctxt{class = Class}.
-
core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
-is_simple_term(#c_values{es=Es}) ->
- length(Es) < 3 andalso lists:all(fun is_simple_term/1, Es);
is_simple_term(#c_tuple{es=Es}) ->
length(Es) < 4 andalso lists:all(fun is_simple_term/1, Es);
is_simple_term(#c_var{}) -> true;
diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl
index 5e85bba2bd..11b52f6c5f 100644
--- a/lib/compiler/src/core_scan.erl
+++ b/lib/compiler/src/core_scan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index c00f5eab70..6b2d781a76 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 8124729b35..dcbdeb32e6 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+# Copyright Ericsson AB 1998-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ BEAM_FORMAT_NUMBER=0
## Save the next instruction as the return address in the CP register.
4: call/2
-## @spec call_last Arity Label Dellocate
+## @spec call_last Arity Label Deallocate
## @doc Deallocate and do a tail recursive call to the function at Label.
## Do not update the CP register.
## Before the call deallocate Deallocate words of stack.
@@ -137,7 +137,7 @@ BEAM_FORMAT_NUMBER=0
# Sending & receiving.
#
## @spec send
-## @doc Send argument in x(0) as a message to the destination process in x(0).
+## @doc Send argument in x(1) as a message to the destination process in x(0).
## The message in x(1) ends up as the result of the send in x(0).
20: send/0
@@ -164,12 +164,12 @@ BEAM_FORMAT_NUMBER=0
25: wait/1
## @spec wait_timeout Lable Time
-## @doc Sets up a timeout of Time milllisecons and saves the address of the
+## @doc Sets up a timeout of Time milliseconds and saves the address of the
## following instruction as the entry point if the timeout triggers.
26: wait_timeout/2
#
-# Arithmethic opcodes.
+# Arithmetic opcodes.
#
27: -m_plus/4
28: -m_minus/4
@@ -316,7 +316,7 @@ BEAM_FORMAT_NUMBER=0
66: get_tuple_element/3
## @spec set_tuple_element NewElement Tuple Position
-## @doc Update the element at postition Position of the tuple Tuple
+## @doc Update the element at position Position of the tuple Tuple
## with the new element NewElement.
67: set_tuple_element/3
diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl
index 0e9e12d1ad..cdc513e57c 100644
--- a/lib/compiler/src/rec_env.erl
+++ b/lib/compiler/src/rec_env.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,8 +22,7 @@
%% @doc Abstract environments, supporting self-referential bindings and
%% automatic new-key generation.
-%% The current implementation is based on Erlang standard library
-%% dictionaries.
+%% The current implementation is based on Erlang standard library maps.
%%% -define(DEBUG, true).
@@ -62,7 +61,7 @@ test_0(Type, N) ->
io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]),
io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]),
io:fwrite("\nmax: ~w.\n", [get(new_key_max)]),
- dict:to_list(element(1,Env)).
+ maps:to_list(element(1,Env)).
test_1(integer = Type, N, Env) when is_integer(N), N > 0 ->
Key = new_key(Env),
@@ -80,14 +79,13 @@ test_1(_,0, Env) ->
%%
%% environment() = [Mapping]
%%
-%% Mapping = {map, Dict} | {rec, Dict, Dict}
-%% Dict = dict:dictionary()
+%% Mapping = {map, map()} | {rec, map(), map()}
%%
-%% An empty environment is a list containing a single `{map, Dict}'
+%% An empty environment is a list containing a single `{map, map()}'
%% element - empty lists are not valid environments. To find a key in an
%% environment, it is searched for in each mapping in the list, in
%% order, until it the key is found in some mapping, or the end of the
-%% list is reached. In a 'rec' mapping, we keep the original dictionary
+%% list is reached. In a 'rec' mapping, we keep the original map
%% together with a version where entries may have been deleted - this
%% makes it possible to garbage collect the entire 'rec' mapping when
%% all its entries are unused (for example, by being shadowed by later
@@ -97,7 +95,7 @@ test_1(_,0, Env) ->
%% =====================================================================
%% @type environment(). An abstract environment.
--type mapping() :: {'map', dict:dict()} | {'rec', dict:dict(), dict:dict()}.
+-type mapping() :: {'map', map()} | {'rec', map(), map()}.
-type environment() :: [mapping(),...].
%% =====================================================================
@@ -108,7 +106,7 @@ test_1(_,0, Env) ->
-spec empty() -> environment().
empty() ->
- [{map, dict:new()}].
+ [{map, #{}}].
%% =====================================================================
@@ -119,14 +117,14 @@ empty() ->
-spec is_empty(environment()) -> boolean().
-is_empty([{map, Dict} | Es]) ->
- N = dict:size(Dict),
+is_empty([{map, Map} | Es]) ->
+ N = map_size(Map),
if N =/= 0 -> false;
Es =:= [] -> true;
true -> is_empty(Es)
end;
-is_empty([{rec, Dict, _} | Es]) ->
- N = dict:size(Dict),
+is_empty([{rec, Map, _} | Es]) ->
+ N = map_size(Map),
if N =/= 0 -> false;
Es =:= [] -> true;
true -> is_empty(Es)
@@ -146,12 +144,12 @@ is_empty([{rec, Dict, _} | Es]) ->
size(Env) ->
env_size(Env).
-env_size([{map, Dict}]) ->
- dict:size(Dict);
-env_size([{map, Dict} | Env]) ->
- dict:size(Dict) + env_size(Env);
-env_size([{rec, Dict, _Dict0} | Env]) ->
- dict:size(Dict) + env_size(Env).
+env_size([{map, Map}]) ->
+ map_size(Map);
+env_size([{map, Map} | Env]) ->
+ map_size(Map) + env_size(Env);
+env_size([{rec, Map, _Map0} | Env]) ->
+ map_size(Map) + env_size(Env).
%% =====================================================================
@@ -165,8 +163,8 @@ env_size([{rec, Dict, _Dict0} | Env]) ->
-spec is_defined(term(), environment()) -> boolean().
-is_defined(Key, [{map, Dict} | Env]) ->
- case dict:is_key(Key, Dict) of
+is_defined(Key, [{map, Map} | Env]) ->
+ case maps:is_key(Key, Map) of
true ->
true;
false when Env =:= [] ->
@@ -174,8 +172,8 @@ is_defined(Key, [{map, Dict} | Env]) ->
false ->
is_defined(Key, Env)
end;
-is_defined(Key, [{rec, Dict, _Dict0} | Env]) ->
- dict:is_key(Key, Dict) orelse is_defined(Key, Env).
+is_defined(Key, [{rec, Map, _Map0} | Env]) ->
+ maps:is_key(Key, Map) orelse is_defined(Key, Env).
%% =====================================================================
@@ -188,12 +186,12 @@ is_defined(Key, [{rec, Dict, _Dict0} | Env]) ->
keys(Env) ->
lists:sort(keys(Env, [])).
-keys([{map, Dict}], S) ->
- dict:fetch_keys(Dict) ++ S;
-keys([{map, Dict} | Env], S) ->
- keys(Env, dict:fetch_keys(Dict) ++ S);
-keys([{rec, Dict, _Dict0} | Env], S) ->
- keys(Env, dict:fetch_keys(Dict) ++ S).
+keys([{map, Map}], S) ->
+ maps:keys(Map) ++ S;
+keys([{map, Map} | Env], S) ->
+ keys(Env, maps:keys(Map) ++ S);
+keys([{rec, Map, _Map0} | Env], S) ->
+ keys(Env, maps:keys(Map) ++ S).
%% =====================================================================
@@ -212,12 +210,12 @@ keys([{rec, Dict, _Dict0} | Env], S) ->
to_list(Env) ->
lists:sort(to_list(Env, [])).
-to_list([{map, Dict}], S) ->
- dict:to_list(Dict) ++ S;
-to_list([{map, Dict} | Env], S) ->
- to_list(Env, dict:to_list(Dict) ++ S);
-to_list([{rec, Dict, _Dict0} | Env], S) ->
- to_list(Env, dict:to_list(Dict) ++ S).
+to_list([{map, Map}], S) ->
+ maps:to_list(Map) ++ S;
+to_list([{map, Map} | Env], S) ->
+ to_list(Env, maps:to_list(Map) ++ S);
+to_list([{rec, Map, _Map0} | Env], S) ->
+ to_list(Env, maps:to_list(Map) ++ S).
%% =====================================================================
@@ -236,12 +234,12 @@ to_list([{rec, Dict, _Dict0} | Env], S) ->
-spec bind(term(), term(), environment()) -> environment().
-bind(Key, Value, [{map, Dict}]) ->
- [{map, dict:store(Key, Value, Dict)}];
-bind(Key, Value, [{map, Dict} | Env]) ->
- [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)];
+bind(Key, Value, [{map, Map}]) ->
+ [{map, maps:put(Key, Value, Map)}];
+bind(Key, Value, [{map, Map} | Env]) ->
+ [{map, maps:put(Key, Value, Map)} | delete_any(Key, Env)];
bind(Key, Value, Env) ->
- [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)].
+ [{map, maps:put(Key, Value, #{})} | delete_any(Key, Env)].
%% =====================================================================
@@ -259,17 +257,17 @@ bind(Key, Value, Env) ->
-spec bind_list([term()], [term()], environment()) -> environment().
-bind_list(Ks, Vs, [{map, Dict}]) ->
- [{map, store_list(Ks, Vs, Dict)}];
-bind_list(Ks, Vs, [{map, Dict} | Env]) ->
- [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)];
+bind_list(Ks, Vs, [{map, Map}]) ->
+ [{map, store_list(Ks, Vs, Map)}];
+bind_list(Ks, Vs, [{map, Map} | Env]) ->
+ [{map, store_list(Ks, Vs, Map)} | delete_list(Ks, Env)];
bind_list(Ks, Vs, Env) ->
- [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)].
+ [{map, store_list(Ks, Vs, #{})} | delete_list(Ks, Env)].
-store_list([K | Ks], [V | Vs], Dict) ->
- store_list(Ks, Vs, dict:store(K, V, Dict));
-store_list([], _, Dict) ->
- Dict.
+store_list([K | Ks], [V | Vs], Map) ->
+ store_list(Ks, Vs, maps:put(K, V, Map));
+store_list([], _, Map) ->
+ Map.
delete_list([K | Ks], Env) ->
delete_list(Ks, delete_any(K, Env));
@@ -298,48 +296,40 @@ delete_any(Key, Env) ->
-spec delete(term(), environment()) -> environment().
-delete(Key, [{map, Dict} = E | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- [{map, dict:erase(Key, Dict)} | Env];
- false ->
+delete(Key, [{map, Map} = E | Env]) ->
+ case maps:take(Key, Map) of
+ {_, Map1} ->
+ [{map, Map1} | Env];
+ error ->
delete_1(Key, Env, E)
end;
-delete(Key, [{rec, Dict, Dict0} = E | Env]) ->
- case dict:is_key(Key, Dict) of
- true ->
- %% The Dict0 component must be preserved as it is until all
- %% keys in Dict have been deleted.
- Dict1 = dict:erase(Key, Dict),
- case dict:size(Dict1) of
- 0 ->
- Env; % the whole {rec,...} is now garbage
- _ ->
- [{rec, Dict1, Dict0} | Env]
- end;
- false ->
+delete(Key, [{rec, Map, Map0} = E | Env]) ->
+ case maps:take(Key, Map) of
+ {_, Map1} when map_size(Map1) =:= 0 ->
+ Env; % the whole {rec,...} is now garbage
+ %% The Map0 component must be preserved as it is until all
+ %% keys in Map have been deleted.
+ {_, Map1} ->
+ [{rec, Map1, Map0} | Env];
+ error ->
[E | delete(Key, Env)]
end.
%% This is just like above, except we pass on the preceding 'map'
%% mapping in the list to enable merging when removing 'rec' mappings.
-delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) ->
- case dict:is_key(Key, Dict) of
- true ->
- Dict1 = dict:erase(Key, Dict),
- case dict:size(Dict1) of
- 0 ->
- concat(E1, Env);
- _ ->
- [E1, {rec, Dict1, Dict0} | Env]
- end;
- false ->
+delete_1(Key, [{rec, Map, Map0} = E | Env], E1) ->
+ case maps:take(Key, Map) of
+ {_, Map1} when map_size(Map1) =:= 0 ->
+ concat(E1, Env);
+ {_, Map1} ->
+ [E1, {rec, Map1, Map0} | Env];
+ error ->
[E1, E | delete(Key, Env)]
end.
-concat({map, D1}, [{map, D2} | Env]) ->
- [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env];
+concat({map, M1}, [{map, M2} | Env]) ->
+ [maps:merge(M2, M1) | Env];
concat(E1, Env) ->
[E1 | Env].
@@ -392,15 +382,15 @@ bind_recursive([], [], _, Env) ->
Env;
bind_recursive(Ks, Vs, F, Env) ->
F1 = fun (V) ->
- fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end
+ fun (Map) -> F(V, [{rec, Map, Map} | Env]) end
end,
- Dict = bind_recursive_1(Ks, Vs, F1, dict:new()),
- [{rec, Dict, Dict} | Env].
+ Map = bind_recursive_1(Ks, Vs, F1, #{}),
+ [{rec, Map, Map} | Env].
-bind_recursive_1([K | Ks], [V | Vs], F, Dict) ->
- bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict));
-bind_recursive_1([], [], _, Dict) ->
- Dict.
+bind_recursive_1([K | Ks], [V | Vs], F, Map) ->
+ bind_recursive_1(Ks, Vs, F, maps:put(K, F(V), Map));
+bind_recursive_1([], [], _, Map) ->
+ Map.
%% =====================================================================
@@ -416,8 +406,8 @@ bind_recursive_1([], [], _, Dict) ->
-spec lookup(term(), environment()) -> 'error' | {'ok', term()}.
-lookup(Key, [{map, Dict} | Env]) ->
- case dict:find(Key, Dict) of
+lookup(Key, [{map, Map} | Env]) ->
+ case maps:find(Key, Map) of
{ok, _}=Value ->
Value;
error when Env =:= [] ->
@@ -425,10 +415,10 @@ lookup(Key, [{map, Dict} | Env]) ->
error ->
lookup(Key, Env)
end;
-lookup(Key, [{rec, Dict, Dict0} | Env]) ->
- case dict:find(Key, Dict) of
+lookup(Key, [{rec, Map, Map0} | Env]) ->
+ case maps:find(Key, Map) of
{ok, F} ->
- {ok, F(Dict0)};
+ {ok, F(Map0)};
error ->
lookup(Key, Env)
end.
@@ -598,7 +588,17 @@ start_range(Env) ->
%% (pseudo-)randomly distributed over the range.
generate(_N, Range) ->
- random:uniform(Range). % works well
+ %% We must use the same sequence of random variables to ensure
+ %% that two compilations of the same source code generates the
+ %% same BEAM code.
+ case rand:export_seed() of
+ undefined ->
+ _ = rand:seed(exsplus, {1,42,2053}),
+ ok;
+ _ ->
+ ok
+ end,
+ rand:uniform(Range). % works well
%% =====================================================================
diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl
index ac32db10fe..bd3eeae238 100644
--- a/lib/compiler/src/sys_core_dsetel.erl
+++ b/lib/compiler/src/sys_core_dsetel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ module(M0, _Options) ->
{ok,M}.
visit_module(#c_module{defs=Ds0}=R) ->
- Env = dict:new(),
+ Env = #{},
Ds = visit_module_1(Ds0, Env, []),
R#c_module{defs=Ds}.
@@ -95,9 +95,11 @@ visit(Env, #c_var{name={_,_}}=R) ->
{R, Env};
visit(Env0, #c_var{name=X}=R) ->
%% There should not be any free variables. If there are,
- %% the next line will cause an exception.
- {ok, N} = dict:find(X, Env0),
- {R, dict:store(X, N+1, Env0)};
+ %% the case will fail with an exception.
+ case Env0 of
+ #{X:=N} ->
+ {R, Env0#{X:=N+1}}
+ end;
visit(Env, #c_literal{}=R) ->
{R, Env};
visit(Env0, #c_tuple{es=Es0}=R) ->
@@ -203,7 +205,7 @@ bind_vars(Vs, Env) ->
bind_vars(Vs, Env, []).
bind_vars([#c_var{name=X}|Vs], Env0, Xs)->
- bind_vars(Vs, dict:store(X, 0, Env0), [X|Xs]);
+ bind_vars(Vs, Env0#{X=>0}, [X|Xs]);
bind_vars([], Env,Xs) ->
{Xs, Env}.
@@ -217,7 +219,7 @@ visit_pats([], Env, Vs) ->
{Vs, Env}.
visit_pat(Env0, #c_var{name=V}, Vs) ->
- {[V|Vs], dict:store(V, 0, Env0)};
+ {[V|Vs], Env0#{V=>0}};
visit_pat(Env0, #c_tuple{es=Es}, Vs) ->
visit_pats(Es, Env0, Vs);
visit_pat(Env0, #c_map{es=Es}, Vs) ->
@@ -235,23 +237,25 @@ visit_pat(Env0, #c_bitstr{val=Val,size=Sz}, Vs0) ->
case Sz of
#c_var{name=V} ->
%% We don't tolerate free variables.
- {ok, N} = dict:find(V, Env0),
- {Vs0, dict:store(V, N+1, Env0)};
+ case Env0 of
+ #{V:=N} ->
+ {Vs0, Env0#{V:=N+1}}
+ end;
_ ->
visit_pat(Env0, Sz, Vs0)
end,
visit_pat(Env1, Val, Vs1);
visit_pat(Env0, #c_alias{pat=P,var=#c_var{name=V}}, Vs) ->
- visit_pat(dict:store(V, 0, Env0), P, [V|Vs]);
+ visit_pat(Env0#{V=>0}, P, [V|Vs]);
visit_pat(Env, #c_literal{}, Vs) ->
{Vs, Env}.
restore_vars([V|Vs], Env0, Env1) ->
- case dict:find(V, Env0) of
- {ok, N} ->
- restore_vars(Vs, Env0, dict:store(V, N, Env1));
- error ->
- restore_vars(Vs, Env0, dict:erase(V, Env1))
+ case Env0 of
+ #{V:=N} ->
+ restore_vars(Vs, Env0, Env1#{V=>N});
+ _ ->
+ restore_vars(Vs, Env0, maps:remove(V, Env1))
end;
restore_vars([], _, Env1) ->
Env1.
@@ -349,8 +353,8 @@ is_safe(#c_literal{}) -> true;
is_safe(_) -> false.
is_single_use(V, Env) ->
- case dict:find(V, Env) of
- {ok, 1} ->
+ case Env of
+ #{V:=1} ->
true;
_ ->
false
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 27d023d067..4922953407 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -277,7 +277,7 @@ expr(#c_fun{}=Fun, effect, _) ->
add_warning(Fun, useless_building),
void();
expr(#c_fun{vars=Vs0,body=B0}=Fun, Ctxt0, Sub0) ->
- {Vs1,Sub1} = pattern_list(Vs0, Sub0),
+ {Vs1,Sub1} = var_list(Vs0, Sub0),
Ctxt = case Ctxt0 of
{letrec,Ctxt1} -> Ctxt1;
value -> value
@@ -374,10 +374,21 @@ expr(#c_receive{clauses=Cs0,timeout=T0,action=A0}=Recv, Ctxt, Sub) ->
T1 = expr(T0, value, Sub),
A1 = body(A0, Ctxt, Sub),
Recv#c_receive{clauses=Cs1,timeout=T1,action=A1};
-expr(#c_apply{op=Op0,args=As0}=App, _, Sub) ->
+expr(#c_apply{anno=Anno,op=Op0,args=As0}=App, _, Sub) ->
Op1 = expr(Op0, value, Sub),
As1 = expr_list(As0, value, Sub),
- App#c_apply{op=Op1,args=As1};
+ case Op1 of
+ #c_var{} ->
+ App#c_apply{op=Op1,args=As1};
+ _ ->
+ add_warning(App, invalid_call),
+ Err = #c_call{anno=Anno,
+ module=#c_literal{val=erlang},
+ name=#c_literal{val=error},
+ args=[#c_tuple{es=[#c_literal{val='badfun'},
+ Op1]}]},
+ make_effect_seq(As1++[Err], Sub)
+ end;
expr(#c_call{module=M0,name=N0}=Call0, Ctxt, Sub) ->
M1 = expr(M0, value, Sub),
N1 = expr(N0, value, Sub),
@@ -420,13 +431,13 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
%% Here is the general try/catch construct outside of guards.
%% We can remove try if the value is simple and replace it with a let.
E1 = body(E0, value, Sub0),
- {Vs1,Sub1} = pattern_list(Vs0, Sub0),
+ {Vs1,Sub1} = var_list(Vs0, Sub0),
B1 = body(B0, value, Sub1),
case is_safe_simple(E1, Sub0) of
true ->
expr(#c_let{anno=A,vars=Vs1,arg=E1,body=B1}, value, Sub0);
false ->
- {Evs1,Sub2} = pattern_list(Evs0, Sub0),
+ {Evs1,Sub2} = var_list(Evs0, Sub0),
H1 = body(H0, value, Sub2),
Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}
end.
@@ -457,7 +468,8 @@ bitstr(#c_bitstr{val=Val,size=Size}=BinSeg, Sub) ->
%% Currently, we don't attempt to check binaries because they
%% are difficult to check.
-is_safe_simple(#c_var{}, _) -> true;
+is_safe_simple(#c_var{}=Var, _) ->
+ not cerl:is_c_fname(Var);
is_safe_simple(#c_cons{hd=H,tl=T}, Sub) ->
is_safe_simple(H, Sub) andalso is_safe_simple(T, Sub);
is_safe_simple(#c_tuple{es=Es}, Sub) -> is_safe_simple_list(Es, Sub);
@@ -722,7 +734,7 @@ call(#c_call{args=As}=Call, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) ->
false ->
case sys_core_fold_lists:call(Call, M, N, As) of
none ->
- call_1(Call, M, N, As, Sub);
+ call_1(Call, M0, N0, As, Sub);
Core ->
expr(Core, Sub)
end
@@ -775,7 +787,7 @@ fold_lit_args(Call, Module, Name, Args0) ->
Val ->
case cerl:is_literal_term(Val) of
true ->
- cerl:abstract(Val);
+ cerl:ann_abstract(cerl:get_ann(Call), Val);
false ->
%% Successful evaluation, but it was not possible
%% to express the computed value as a literal.
@@ -1078,15 +1090,28 @@ is_atom_or_var(_) -> false.
%% clause(Clause, Cepxr, Context, Sub) -> Clause.
-clause(#c_clause{pats=Ps0,guard=G0,body=B0}=Cl, Cexpr, Ctxt, Sub0) ->
- {Ps1,Sub1} = pattern_list(Ps0, Sub0),
+clause(#c_clause{pats=Ps0}=Cl, Cexpr, Ctxt, Sub0) ->
+ try pattern_list(Ps0, Sub0) of
+ {Ps1,Sub1} ->
+ clause_1(Cl, Ps1, Cexpr, Ctxt, Sub1)
+ catch
+ nomatch ->
+ Cl#c_clause{anno=[compiler_generated],
+ guard=#c_literal{val=false}}
+ end.
+
+clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) ->
Sub2 = update_types(Cexpr, Ps1, Sub1),
- GSub = case {Cexpr,Ps1} of
- {#c_var{name='_'},_} ->
+ GSub = case {Cexpr,Ps1,G0} of
+ {_,_,#c_literal{}} ->
+ %% No need for substitution tricks when the guard
+ %% does not contain any variables.
+ Sub2;
+ {#c_var{name='_'},_,_} ->
%% In a 'receive', Cexpr is the variable '_', which represents the
%% message being matched. We must NOT do any extra substiutions.
Sub2;
- {#c_var{},[#c_var{}=Var]} ->
+ {#c_var{},[#c_var{}=Var],_} ->
%% The idea here is to optimize expressions such as
%%
%% case A of A -> ...
@@ -1105,7 +1130,13 @@ clause(#c_clause{pats=Ps0,guard=G0,body=B0}=Cl, Cexpr, Ctxt, Sub0) ->
%%
%% case A of NewVar when true -> ...
%%
- sub_set_var(Var, Cexpr, Sub2);
+ case cerl:is_c_fname(Cexpr) of
+ false ->
+ sub_set_var(Var, Cexpr, Sub2);
+ true ->
+ %% We must not copy funs, and especially not into guards.
+ Sub2
+ end;
_ ->
Sub2
end,
@@ -1120,7 +1151,7 @@ clause(#c_clause{pats=Ps0,guard=G0,body=B0}=Cl, Cexpr, Ctxt, Sub0) ->
%% the unsubstituted variables and values.
let_substs(Vs0, As0, Sub0) ->
- {Vs1,Sub1} = pattern_list(Vs0, Sub0),
+ {Vs1,Sub1} = var_list(Vs0, Sub0),
{Vs2,As1,Ss} = let_substs_1(Vs1, As0, Sub1),
Sub2 = sub_add_scope([V || #c_var{name=V} <- Vs2], Sub1),
{Vs2,As1,
@@ -1206,20 +1237,132 @@ bin_pattern_list(Ps0, Isub, Osub0) ->
{Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0),
{Ps,Osub}.
-bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat, {Isub0,Osub0}) ->
+bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat0, {Isub0,Osub0}) ->
Size1 = expr(Size0, Isub0),
{E1,Osub} = pattern(E0, Isub0, Osub0),
Isub = case E0 of
#c_var{} -> sub_set_var(E0, E1, Isub0);
_ -> Isub0
end,
- {Pat#c_bitstr{val=E1,size=Size1},{Isub,Osub}}.
+ Pat = Pat0#c_bitstr{val=E1,size=Size1},
+ bin_pat_warn(Pat),
+ {Pat,{Isub,Osub}}.
pattern_list(Ps, Sub) -> pattern_list(Ps, Sub, Sub).
pattern_list(Ps0, Isub, Osub0) ->
mapfoldl(fun (P, Osub) -> pattern(P, Isub, Osub) end, Osub0, Ps0).
+%% var_list([Var], InSub) -> {Pattern,OutSub}.
+%% Works like pattern_list/2 but only accept variables and is
+%% guaranteed not to throw an exception.
+
+var_list(Vs, Sub0) ->
+ mapfoldl(fun (#c_var{}=V, Sub) ->
+ pattern(V, Sub, Sub)
+ end, Sub0, Vs).
+
+
+%%%
+%%% Generate warnings for binary patterns that will not match.
+%%%
+
+bin_pat_warn(#c_bitstr{type=#c_literal{val=Type},
+ val=Val0,
+ size=#c_literal{val=Sz},
+ unit=#c_literal{val=Unit},
+ flags=Fl}=Pat) ->
+ case {Type,Sz} of
+ {_,_} when is_integer(Sz), Sz >= 0 -> ok;
+ {binary,all} -> ok;
+ {utf8,undefined} -> ok;
+ {utf16,undefined} -> ok;
+ {utf32,undefined} -> ok;
+ {_,_} ->
+ add_warning(Pat, {nomatch_bit_syntax_size,Sz}),
+ throw(nomatch)
+ end,
+ case {Type,Val0} of
+ {integer,#c_literal{val=Val}} when is_integer(Val) ->
+ Signedness = signedness(Fl),
+ TotalSz = Sz * Unit,
+ bit_pat_warn_int(Val, TotalSz, Signedness, Pat);
+ {float,#c_literal{val=Val}} when is_float(Val) ->
+ ok;
+ {utf8,#c_literal{val=Val}} when is_integer(Val) ->
+ bit_pat_warn_unicode(Val, Pat);
+ {utf16,#c_literal{val=Val}} when is_integer(Val) ->
+ bit_pat_warn_unicode(Val, Pat);
+ {utf32,#c_literal{val=Val}} when is_integer(Val) ->
+ bit_pat_warn_unicode(Val, Pat);
+ {_,#c_literal{val=Val}} ->
+ add_warning(Pat, {nomatch_bit_syntax_type,Val,Type}),
+ throw(nomatch);
+ {_,_} ->
+ ok
+ end;
+bin_pat_warn(#c_bitstr{type=#c_literal{val=Type},val=Val0,flags=Fl}=Pat) ->
+ %% Size is variable. Not much that we can check.
+ case {Type,Val0} of
+ {integer,#c_literal{val=Val}} when is_integer(Val) ->
+ case signedness(Fl) of
+ unsigned when Val < 0 ->
+ add_warning(Pat, {nomatch_bit_syntax_unsigned,Val}),
+ throw(nomatch);
+ _ ->
+ ok
+ end;
+ {float,#c_literal{val=Val}} when is_float(Val) ->
+ ok;
+ {_,#c_literal{val=Val}} ->
+ add_warning(Pat, {nomatch_bit_syntax_type,Val,Type}),
+ throw(nomatch);
+ {_,_} ->
+ ok
+ end.
+
+bit_pat_warn_int(Val, 0, signed, Pat) ->
+ if
+ Val =:= 0 ->
+ ok;
+ true ->
+ add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,0}),
+ throw(nomatch)
+ end;
+bit_pat_warn_int(Val, Sz, signed, Pat) ->
+ if
+ Val < 0, Val bsr (Sz - 1) =/= -1 ->
+ add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,Sz}),
+ throw(nomatch);
+ Val > 0, Val bsr (Sz - 1) =/= 0 ->
+ add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,Sz}),
+ throw(nomatch);
+ true ->
+ ok
+ end;
+bit_pat_warn_int(Val, _Sz, unsigned, Pat) when Val < 0 ->
+ add_warning(Pat, {nomatch_bit_syntax_unsigned,Val}),
+ throw(nomatch);
+bit_pat_warn_int(Val, Sz, unsigned, Pat) ->
+ if
+ Val bsr Sz =:= 0 ->
+ ok;
+ true ->
+ add_warning(Pat, {nomatch_bit_syntax_truncated,unsigned,Val,Sz}),
+ throw(nomatch)
+ end.
+
+bit_pat_warn_unicode(U, _Pat) when 0 =< U, U =< 16#10FFFF ->
+ ok;
+bit_pat_warn_unicode(U, Pat) ->
+ add_warning(Pat, {nomatch_bit_syntax_unicode,U}),
+ throw(nomatch).
+
+signedness(#c_literal{val=Flags}) ->
+ [S] = [F || F <- Flags, F =:= signed orelse F =:= unsigned],
+ S.
+
+
%% is_subst(Expr) -> true | false.
%% Test whether an expression is a suitable substitution.
@@ -2040,24 +2183,22 @@ opt_not_in_let_1(V, Call, Body) ->
#c_call{module=#c_literal{val=erlang},
name=#c_literal{val='not'},
args=[#c_var{name=V}]} ->
- opt_not_in_let_2(Body);
+ opt_not_in_let_2(Body, Call);
_ ->
no
end.
-opt_not_in_let_2(#c_case{clauses=Cs0}=Case) ->
+opt_not_in_let_2(#c_case{clauses=Cs0}=Case, NotCall) ->
Vars = make_vars([], 1),
- Body = #c_call{module=#c_literal{val=erlang},
- name=#c_literal{val='not'},
- args=Vars},
+ Body = NotCall#c_call{args=Vars},
Cs = [begin
Let = #c_let{vars=Vars,arg=B,body=Body},
C#c_clause{body=opt_not_in_let(Let)}
end || #c_clause{body=B}=C <- Cs0],
{yes,Case#c_case{clauses=Cs}};
-opt_not_in_let_2(#c_call{}=Call0) ->
+opt_not_in_let_2(#c_call{}=Call0, _NotCall) ->
invert_call(Call0);
-opt_not_in_let_2(_) -> no.
+opt_not_in_let_2(_, _) -> no.
invert_call(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name0},
@@ -2251,11 +2392,11 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner,
%%
Arg = body(Arg0, Sub0),
ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
- {OuterVs,ScopeSub} = pattern_list(OuterVs0, ScopeSub0),
+ {OuterVs,ScopeSub} = var_list(OuterVs0, ScopeSub0),
OuterBody = body(OuterBody0, ScopeSub),
- {InnerVs,Sub} = pattern_list(InnerVs0, Sub0),
+ {InnerVs,Sub} = var_list(InnerVs0, Sub0),
InnerBody = body(InnerBody0, Sub),
Outer#c_let{vars=OuterVs,arg=Arg,
body=Inner#c_let{vars=InnerVs,arg=OuterBody,body=InnerBody}};
@@ -2271,39 +2412,49 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
case {TwoClauses,is_failing_clause(Ca0),is_failing_clause(Cb0)} of
{true,false,true} ->
%% let <Lvars> = case <Case-expr> of
- %% <Cvars> -> <Clause-body>;
- %% <OtherCvars> -> erlang:error(...)
+ %% <Cpats> -> <Clause-body>;
+ %% <OtherCpats> -> erlang:error(...)
%% end
%% in <Let-body>
%%
%% ==>
%%
%% case <Case-expr> of
- %% <Cvars> ->
+ %% <Cpats> ->
%% let <Lvars> = <Clause-body>
%% in <Let-body>;
- %% <OtherCvars> -> erlang:error(...)
+ %% <OtherCpats> -> erlang:error(...)
%% end
Cexpr = body(Cexpr0, Sub0),
- CaVars0 = Ca0#c_clause.pats,
+ CaPats0 = Ca0#c_clause.pats,
G0 = Ca0#c_clause.guard,
B0 = Ca0#c_clause.body,
ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
- {CaVars,ScopeSub} = pattern_list(CaVars0, ScopeSub0),
- G = guard(G0, ScopeSub),
-
- B1 = body(B0, ScopeSub),
-
- {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0),
- Sub2 = Sub1#sub{s=cerl_sets:union(ScopeSub#sub.s,
- Sub1#sub.s)},
- Lbody = body(Lbody0, Sub2),
- B = Let#c_let{vars=Lvs,arg=core_lib:make_values(B2),body=Lbody},
-
- Ca = Ca0#c_clause{pats=CaVars,guard=G,body=B},
- Cb = clause(Cb0, Cexpr, value, Sub0),
- Case#c_case{arg=Cexpr,clauses=[Ca,Cb]};
+ try pattern_list(CaPats0, ScopeSub0) of
+ {CaPats,ScopeSub} ->
+ G = guard(G0, ScopeSub),
+
+ B1 = body(B0, ScopeSub),
+
+ {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0),
+ Sub2 = Sub1#sub{s=cerl_sets:union(ScopeSub#sub.s,
+ Sub1#sub.s)},
+ Lbody = body(Lbody0, Sub2),
+ B = Let#c_let{vars=Lvs,
+ arg=core_lib:make_values(B2),
+ body=Lbody},
+
+ Ca = Ca0#c_clause{pats=CaPats,guard=G,body=B},
+ Cb = clause(Cb0, Cexpr, value, Sub0),
+ Case#c_case{arg=Cexpr,clauses=[Ca,Cb]}
+ catch
+ nomatch ->
+ %% This is not a defeat. The code will eventually
+ %% be optimized to erlang:error(...) by the other
+ %% optimizations done in this module.
+ impossible
+ end;
{_,_,_} -> impossible
end;
move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
@@ -2595,7 +2746,7 @@ move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg,
%% in case <InnerArg> of <InnerClauses> end
%%
ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}),
- {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0),
+ {OuterVars,ScopeSub} = var_list(OuterVars0, ScopeSub0),
InnerArg = body(InnerArg0, ScopeSub),
Outer#c_let{vars=OuterVars,arg=OuterArg,
body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}};
@@ -2624,14 +2775,18 @@ move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg,
%% 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]};
+
+ %% We KNOW that pattern_list/2 has already been called for OuterPats0;
+ %% therefore, it cannot throw an exception.
+ {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;
@@ -2793,12 +2948,18 @@ extract_type_1(Expr, Sub) ->
true -> bool
end.
+returns_integer('band', [_,_]) -> true;
+returns_integer('bnot', [_]) -> true;
+returns_integer('bor', [_,_]) -> true;
+returns_integer('bxor', [_,_]) -> true;
returns_integer(bit_size, [_]) -> true;
returns_integer('bsl', [_,_]) -> true;
returns_integer('bsr', [_,_]) -> true;
returns_integer(byte_size, [_]) -> true;
+returns_integer('div', [_,_]) -> true;
returns_integer(length, [_]) -> true;
returns_integer('rem', [_,_]) -> true;
+returns_integer('round', [_]) -> true;
returns_integer(size, [_]) -> true;
returns_integer(tuple_size, [_]) -> true;
returns_integer(trunc, [_]) -> true;
@@ -3091,12 +3252,12 @@ bsm_ensure_no_partition_2([#c_var{name=V}|Ps], N, G, Vstate, S) ->
bsm_ensure_no_partition_2([_|Ps], N, G, _, S) ->
bsm_ensure_no_partition_2(Ps, N-1, G, bin_argument_order, S).
-bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) ->
+bsm_ensure_no_partition_after([#c_clause{pats=Ps}=C|Cs], Pos) ->
case nth(Pos, Ps) of
#c_var{} ->
bsm_ensure_no_partition_after(Cs, Pos);
- P ->
- bsm_problem(P, bin_partition)
+ _ ->
+ bsm_problem(C, bin_partition)
end;
bsm_ensure_no_partition_after([], _) -> ok.
@@ -3207,6 +3368,29 @@ format_error(nomatch_shadow) ->
"this clause cannot match because a previous clause always matches";
format_error(nomatch_guard) ->
"the guard for this clause evaluates to 'false'";
+format_error({nomatch_bit_syntax_truncated,Signess,Val,Sz}) ->
+ S = case Signess of
+ signed -> "a 'signed'";
+ unsigned -> "an 'unsigned'"
+ end,
+ F = "this clause cannot match because the value ~P"
+ " will not fit in ~s binary segment of size ~p",
+ flatten(io_lib:format(F, [Val,10,S,Sz]));
+format_error({nomatch_bit_syntax_unsigned,Val}) ->
+ F = "this clause cannot match because the negative value ~P"
+ " will never match the value of an 'unsigned' binary segment",
+ flatten(io_lib:format(F, [Val,10]));
+format_error({nomatch_bit_syntax_size,Sz}) ->
+ F = "this clause cannot match because '~P' is not a valid size for a binary segment",
+ flatten(io_lib:format(F, [Sz,10]));
+format_error({nomatch_bit_syntax_type,Val,Type}) ->
+ F = "this clause cannot match because '~P' is not of the"
+ " expected type '~p'",
+ flatten(io_lib:format(F, [Val,10,Type]));
+format_error({nomatch_bit_syntax_unicode,Val}) ->
+ F = "this clause cannot match because the value ~p"
+ " is not a valid Unicode code point",
+ flatten(io_lib:format(F, [Val]));
format_error(no_clause_match) ->
"no clause will ever match";
format_error(nomatch_clause_type) ->
@@ -3227,6 +3411,8 @@ format_error({no_effect,{erlang,F,A}}) ->
format_error(result_ignored) ->
"the result of the expression is ignored "
"(suppress the warning by assigning the expression to the _ variable)";
+format_error(invalid_call) ->
+ "invalid function call";
format_error(useless_building) ->
"a term is constructed, but never used";
format_error(bin_opt_alias) ->
diff --git a/lib/compiler/src/sys_core_inline.erl b/lib/compiler/src/sys_core_inline.erl
index 838dda68c6..8c1f69d5de 100644
--- a/lib/compiler/src/sys_core_inline.erl
+++ b/lib/compiler/src/sys_core_inline.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/sys_pre_attributes.erl b/lib/compiler/src/sys_pre_attributes.erl
index f0cb630205..bc93c85989 100644
--- a/lib/compiler/src/sys_pre_attributes.erl
+++ b/lib/compiler/src/sys_pre_attributes.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index d9cc4b530c..7ab4e1845c 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -29,30 +29,26 @@
%% Main entry point.
-export([module/2]).
--import(ordsets, [from_list/1,union/2]).
-import(lists, [member/2,foldl/3,foldr/3]).
--include("../include/erl_bits.hrl").
-
-type fa() :: {atom(), arity()}.
-record(expand, {module=[], %Module name
exports=[], %Exports
- imports=[], %Imports
attributes=[], %Attributes
callbacks=[], %Callbacks
optional_callbacks=[] :: [fa()], %Optional callbacks
- defined, %Defined functions (gb_set)
vcount=0, %Variable counter
func=[], %Current function
arity=[], %Arity for current function
- fcount=0 %Local fun count
+ fcount=0, %Local fun count
+ ctype %Call type map
}).
%% module(Forms, CompileOptions)
%% {ModuleName,Exports,TransformedForms,CompileOptions'}
-%% Expand the forms in one module. N.B.: the lists of predefined
-%% exports and imports are really ordsets!
+%% Expand the forms in one module.
+%%
%% CompileOptions is augmented with options from -compile attributes.
module(Fs0, Opts0) ->
@@ -65,19 +61,28 @@ module(Fs0, Opts0) ->
%% Set pre-defined exported functions.
PreExp = [{module_info,0},{module_info,1}],
+ %% Build the set of defined functions and the initial call
+ %% type map.
+ Defined = defined_functions(Fs, PreExp),
+ Ctype = maps:from_list([{K,local} || K <- Defined]),
+
%% Build initial expand record.
St0 = #expand{exports=PreExp,
- defined=PreExp
+ ctype=Ctype
},
+
%% Expand the functions.
- {Tfs,St1} = forms(Fs, define_functions(Fs, St0)),
+ {Tfs,St1} = forms(Fs, St0),
+
%% Get the correct list of exported functions.
Exports = case member(export_all, Opts) of
- true -> gb_sets:to_list(St1#expand.defined);
+ true -> Defined;
false -> St1#expand.exports
end,
+ St2 = St1#expand{exports=Exports,ctype=undefined},
+
%% Generate all functions from stored info.
- {Ats,St3} = module_attrs(St1#expand{exports = Exports}),
+ {Ats,St3} = module_attrs(St2),
{Mfs,St4} = module_predef_funcs(St3),
{St4#expand.module, St4#expand.exports, Ats ++ Tfs ++ Mfs,
Opts}.
@@ -85,14 +90,14 @@ module(Fs0, Opts0) ->
compiler_options(Forms) ->
lists:flatten([C || {attribute,_,compile,C} <- Forms]).
-%% define_function(Form, State) -> State.
+%% defined_function(Forms, Predef) -> Functions.
%% Add function to defined if form is a function.
-define_functions(Forms, #expand{defined=Predef}=St) ->
+defined_functions(Forms, Predef) ->
Fs = foldl(fun({function,_,N,A,_Cs}, Acc) -> [{N,A}|Acc];
(_, Acc) -> Acc
end, Predef, Forms),
- St#expand{defined=gb_sets:from_list(Fs)}.
+ ordsets:from_list(Fs).
module_attrs(#expand{attributes=Attributes}=St) ->
Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes],
@@ -113,23 +118,21 @@ is_fa_list([{FuncName, Arity}|L])
is_fa_list([]) -> true;
is_fa_list(_) -> false.
-module_predef_funcs(St) ->
- {Mpf1,St1}=module_predef_func_beh_info(St),
- {Mpf2,St2}=module_predef_funcs_mod_info(St1),
+module_predef_funcs(St0) ->
+ {Mpf1,St1} = module_predef_func_beh_info(St0),
+ Mpf2 = module_predef_funcs_mod_info(St1),
Mpf = [erl_parse:new_anno(F) || F <- Mpf1++Mpf2],
- {Mpf,St2}.
+ {Mpf,St1}.
module_predef_func_beh_info(#expand{callbacks=[]}=St) ->
{[], St};
module_predef_func_beh_info(#expand{callbacks=Callbacks,
optional_callbacks=OptionalCallbacks,
- defined=Defined,
exports=Exports}=St) ->
- PreDef=[{behaviour_info,1}],
- PreExp=PreDef,
+ PreDef0 = [{behaviour_info,1}],
+ PreDef = ordsets:from_list(PreDef0),
{[gen_beh_info(Callbacks, OptionalCallbacks)],
- St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), Defined),
- exports=union(from_list(PreExp), Exports)}}.
+ St#expand{exports=ordsets:union(PreDef, Exports)}}.
gen_beh_info(Callbacks, OptionalCallbacks) ->
List = make_list(Callbacks),
@@ -156,20 +159,16 @@ make_optional_list([{Name,Arity}|Rest]) ->
{integer,0,Arity}]},
make_optional_list(Rest)}.
-module_predef_funcs_mod_info(St) ->
- PreDef = [{module_info,0},{module_info,1}],
- PreExp = PreDef,
- {[{function,0,module_info,0,
- [{clause,0,[],[],
+module_predef_funcs_mod_info(#expand{module=Mod}) ->
+ ModAtom = {atom,0,Mod},
+ [{function,0,module_info,0,
+ [{clause,0,[],[],
[{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
- [{atom,0,St#expand.module}]}]}]},
- {function,0,module_info,1,
- [{clause,0,[{var,0,'X'}],[],
+ [ModAtom]}]}]},
+ {function,0,module_info,1,
+ [{clause,0,[{var,0,'X'}],[],
[{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
- [{atom,0,St#expand.module},{var,0,'X'}]}]}]}],
- St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef),
- St#expand.defined),
- exports=union(from_list(PreExp), St#expand.exports)}}.
+ [ModAtom,{var,0,'X'}]}]}]}].
%% forms(Forms, State) ->
%% {TransformedForms,State'}
@@ -196,7 +195,8 @@ attribute(module, Module, _L, St) ->
true = is_atom(Module),
St#expand{module=Module};
attribute(export, Es, _L, St) ->
- St#expand{exports=union(from_list(Es), St#expand.exports)};
+ St#expand{exports=ordsets:union(ordsets:from_list(Es),
+ St#expand.exports)};
attribute(import, Is, _L, St) ->
import(Is, St);
attribute(compile, _C, _L, St) ->
@@ -231,8 +231,6 @@ head(As, St) -> pattern_list(As, St).
%% {TransformedPattern,State'}
%%
-pattern({var,_,'_'}=Var, St) -> %Ignore anonymous variable.
- {Var,St};
pattern({var,_,_}=Var, St) ->
{Var,St};
pattern({char,_,_}=Char, St) ->
@@ -385,19 +383,19 @@ expr({block,Line,Es0}, St0) ->
{Es,St1} = exprs(Es0, St0),
{{block,Line,Es},St1};
expr({'if',Line,Cs0}, St0) ->
- {Cs,St1} = icr_clauses(Cs0, St0),
+ {Cs,St1} = clauses(Cs0, St0),
{{'if',Line,Cs},St1};
expr({'case',Line,E0,Cs0}, St0) ->
{E,St1} = expr(E0, St0),
- {Cs,St2} = icr_clauses(Cs0, St1),
+ {Cs,St2} = clauses(Cs0, St1),
{{'case',Line,E,Cs},St2};
expr({'receive',Line,Cs0}, St0) ->
- {Cs,St1} = icr_clauses(Cs0, St0),
+ {Cs,St1} = clauses(Cs0, St0),
{{'receive',Line,Cs},St1};
expr({'receive',Line,Cs0,To0,ToEs0}, St0) ->
{To,St1} = expr(To0, St0),
{ToEs,St2} = exprs(ToEs0, St1),
- {Cs,St3} = icr_clauses(Cs0, St2),
+ {Cs,St3} = clauses(Cs0, St2),
{{'receive',Line,Cs,To,ToEs},St3};
expr({'fun',Line,Body}, St) ->
fun_tq(Line, Body, St);
@@ -406,21 +404,15 @@ expr({named_fun,Line,Name,Cs}, St) ->
expr({call,Line,{atom,La,N}=Atom,As0}, St0) ->
{As,St1} = expr_list(As0, St0),
Ar = length(As),
- case defined(N,Ar,St1) of
- true ->
+ Key = {N,Ar},
+ case St1#expand.ctype of
+ #{Key:=local} ->
{{call,Line,Atom,As},St1};
+ #{Key:={imported,Mod}} ->
+ {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1};
_ ->
- case imported(N, Ar, St1) of
- {yes,Mod} ->
- {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1};
- no ->
- case erl_internal:bif(N, Ar) of
- true ->
- {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1};
- false -> %% This should have been handled by erl_lint
- {{call,Line,Atom,As},St1}
- end
- end
+ true = erl_internal:bif(N, Ar),
+ {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1}
end;
expr({call,Line,{remote,Lr,M0,F},As0}, St0) ->
{[M1,F1|As1],St1} = expr_list([M0,F|As0], St0),
@@ -430,12 +422,11 @@ expr({call,Line,F,As0}, St0) ->
{{call,Line,Fun1,As1},St1};
expr({'try',Line,Es0,Scs0,Ccs0,As0}, St0) ->
{Es1,St1} = exprs(Es0, St0),
- {Scs1,St2} = icr_clauses(Scs0, St1),
- {Ccs1,St3} = icr_clauses(Ccs0, St2),
+ {Scs1,St2} = clauses(Scs0, St1),
+ {Ccs1,St3} = clauses(Ccs0, St2),
{As1,St4} = exprs(As0, St3),
{{'try',Line,Es1,Scs1,Ccs1,As1},St4};
expr({'catch',Line,E0}, St0) ->
- %% Catch exports no new variables.
{E,St1} = expr(E0, St0),
{{'catch',Line,E},St1};
expr({match,Line,P0,E0}, St0) ->
@@ -456,21 +447,6 @@ expr_list([E0|Es0], St0) ->
{[E|Es],St2};
expr_list([], St) -> {[],St}.
-%% icr_clauses([Clause], State) -> {[TransformedClause],State'}
-%% Be very careful here to return the variables that are really used
-%% and really new.
-
-icr_clauses([], St) -> {[],St};
-icr_clauses(Clauses, St) -> icr_clauses2(Clauses, St).
-
-icr_clauses2([{clause,Line,H0,G0,B0}|Cs0], St0) ->
- {H,St1} = head(H0, St0),
- {G,St2} = guard(G0, St1),
- {B,St3} = exprs(B0, St2),
- {Cs,St4} = icr_clauses2(Cs0, St3),
- {[{clause,Line,H,G,B}|Cs],St4};
-icr_clauses2([], St) -> {[],St}.
-
%% lc_tq(Line, Qualifiers, State) ->
%% {[TransQual],State'}
@@ -486,16 +462,9 @@ lc_tq(Line, [{b_generate,Lg,P0,G0}|Qs0], St0) ->
{Qs1,St3} = lc_tq(Line, Qs0, St2),
{[{b_generate,Lg,P1,G1}|Qs1],St3};
lc_tq(Line, [F0 | Qs0], St0) ->
- case erl_lint:is_guard_test(F0) of
- true ->
- {F1,St1} = guard_test(F0, St0),
- {Qs1,St2} = lc_tq(Line, Qs0, St1),
- {[F1|Qs1],St2};
- false ->
- {F1,St1} = expr(F0, St0),
- {Qs1,St2} = lc_tq(Line, Qs0, St1),
- {[F1 | Qs1],St2}
- end;
+ {F1,St1} = expr(F0, St0),
+ {Qs1,St2} = lc_tq(Line, Qs0, St1),
+ {[F1|Qs1],St2};
lc_tq(_Line, [], St0) ->
{[],St0}.
@@ -527,7 +496,7 @@ fun_tq(L, {function,M,F,A}, St) when is_atom(M), is_atom(F), is_integer(A) ->
fun_tq(Lf, {function,_,_,_}=ExtFun, St) ->
{{'fun',Lf,ExtFun},St};
fun_tq(Lf, {clauses,Cs0}, St0) ->
- {Cs1,St1} = fun_clauses(Cs0, St0),
+ {Cs1,St1} = clauses(Cs0, St0),
{Fname,St2} = new_fun_name(St1),
%% Set dummy values for Index and Uniq -- the real values will
%% be assigned by beam_asm.
@@ -535,18 +504,10 @@ fun_tq(Lf, {clauses,Cs0}, St0) ->
{{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},St2}.
fun_tq(Line, Cs0, St0, Name) ->
- {Cs1,St1} = fun_clauses(Cs0, St0),
+ {Cs1,St1} = clauses(Cs0, St0),
{Fname,St2} = new_fun_name(St1, Name),
{{named_fun,Line,Name,Cs1,{0,0,Fname}},St2}.
-fun_clauses([{clause,L,H0,G0,B0}|Cs0], St0) ->
- {H,St1} = head(H0, St0),
- {G,St2} = guard(G0, St1),
- {B,St3} = exprs(B0, St2),
- {Cs,St4} = fun_clauses(Cs0, St3),
- {[{clause,L,H,G,B}|Cs],St4};
-fun_clauses([], St) -> {[],St}.
-
%% new_fun_name(State) -> {FunName,State}.
new_fun_name(St) ->
@@ -571,7 +532,6 @@ pattern_element({bin_element,Line,Expr0,Size0,Type0}, {Es,St0}) ->
{[{bin_element,Line,Expr,Size,Type}|Es],St2}.
pat_bit_size(default, St) -> {default,St};
-pat_bit_size({atom,_La,all}=All, St) -> {All,St};
pat_bit_size({var,_Lv,_V}=Var, St) -> {Var,St};
pat_bit_size(Size, St) ->
Line = element(2, Size),
@@ -592,8 +552,7 @@ coerce_to_float({integer,L,I}=E, [float|_]) ->
try
{float,L,float(I)}
catch
- error:badarg -> E;
- error:badarith -> E
+ error:badarg -> E
end;
coerce_to_float(E, _) -> E.
@@ -647,25 +606,11 @@ string_to_conses(Line, Cs, Tail) ->
%% import(Line, Imports, State) ->
%% State'
-%% imported(Name, Arity, State) ->
-%% {yes,Module} | no
-%% Handle import declarations and test for imported functions. No need to
-%% check when building imports as code is correct.
+%% Handle import declarations.
-import({Mod,Fs}, St) ->
+import({Mod,Fs}, #expand{ctype=Ctype0}=St) ->
true = is_atom(Mod),
- Mfs = from_list(Fs),
- St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}.
-
-add_imports(Mod, [F|Fs], Is) ->
- add_imports(Mod, Fs, orddict:store(F, Mod, Is));
-add_imports(_, [], Is) -> Is.
-
-imported(F, A, St) ->
- case orddict:find({F,A}, St#expand.imports) of
- {ok,Mod} -> {yes,Mod};
- error -> no
- end.
-
-defined(F, A, St) ->
- gb_sets:is_element({F,A}, St#expand.defined).
+ Ctype = foldl(fun(F, A) ->
+ A#{F=>{imported,Mod}}
+ end, Ctype0, Fs),
+ St#expand{ctype=Ctype}.
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 34c67b16ca..4df1aadd0a 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -827,21 +827,24 @@ select_extract_bin([{var,Hd},{var,Tl}], Size0, Unit, Type, Flags, Vf,
{bs_save2,CtxReg,{Ctx,Tl}}],Int1}
end,
{Es,clear_dead(Aft, I, Vdb),St};
-select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf,
+select_extract_bin([{var,Hd}], Size, Unit, binary, Flags, Vf,
I, Vdb, Bef, Ctx, Body, St) ->
- SizeReg = get_bin_size_reg(Size0, Bef),
+ %% Match the last segment of a binary. We KNOW that the size
+ %% must be 'all'.
+ Size = {atom,all}, %Assertion.
{Es,Aft} =
case vdb_find(Hd, Vdb) of
{_,_,Lhd} when Lhd =< I ->
+ %% The result will not be used. Furthermore, since we
+ %% we are at the end of the binary, the position will
+ %% not be used again; thus, it is safe to do a cheaper
+ %% test of the unit.
CtxReg = fetch_var(Ctx, Bef),
- {case SizeReg =:= {atom,all} andalso is_context_unused(Body) of
- true when Unit =:= 1 ->
+ {case Unit of
+ 1 ->
[];
- true ->
- [{test,bs_test_unit,{f,Vf},[CtxReg,Unit]}];
- false ->
- [{test,bs_skip_bits2,{f,Vf},
- [CtxReg,SizeReg,Unit,{field_flags,Flags}]}]
+ _ ->
+ [{test,bs_test_unit,{f,Vf},[CtxReg,Unit]}]
end,Bef};
{_,_,_} ->
case is_context_unused(Body) of
@@ -853,7 +856,7 @@ select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf,
Name = bs_get_binary2,
Live = max_reg(Bef#sr.reg),
{[{test,Name,{f,Vf},Live,
- [CtxReg,SizeReg,Unit,{field_flags,Flags}],Rhd}],
+ [CtxReg,Size,Unit,{field_flags,Flags}],Rhd}],
Int1};
true ->
%% Since the matching context will not be used again,
@@ -868,7 +871,7 @@ select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf,
Name = bs_get_binary2,
Live = max_reg(Int1#sr.reg),
{[{test,Name,{f,Vf},Live,
- [CtxReg,SizeReg,Unit,{field_flags,Flags}],CtxReg}],
+ [CtxReg,Size,Unit,{field_flags,Flags}],CtxReg}],
Int1}
end
end,
@@ -1086,6 +1089,23 @@ protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) ->
%% test_cg(TestName, Args, Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
%% Generate test instruction. Use explicit fail label here.
+test_cg(is_map, [A], Fail, I, Vdb, Bef, St) ->
+ %% We must avoid creating code like this:
+ %%
+ %% move x(0) y(0)
+ %% is_map Fail [x(0)]
+ %% make_fun => x(0) %% Overwrite x(0)
+ %% put_map_assoc y(0) ...
+ %%
+ %% The code is safe, but beam_validator does not understand that.
+ %% Extending beam_validator to handle such (rare) code as the
+ %% above would make it slower for all programs. Instead, change
+ %% the code generator to always prefer the Y register for is_map()
+ %% and put_map_assoc() instructions, ensuring that they use the
+ %% same register.
+ Arg = cg_reg_arg_prefer_y(A, Bef),
+ Aft = clear_dead(Bef, I, Vdb),
+ {[{test,is_map,{f,Fail},[Arg]}],Aft,St};
test_cg(Test, As, Fail, I, Vdb, Bef, St) ->
Args = cg_reg_args(As, Bef),
Aft = clear_dead(Bef, I, Vdb),
@@ -1152,19 +1172,15 @@ call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->
%% Inside a guard. The only allowed function call is to
%% erlang:error/1,2. We will generate the following code:
%%
- %% jump FailureLabel
%% move {atom,ok} DestReg
- %%
- %% The 'move' instruction will never be executed, but we
- %% generate it anyway in case the beam_validator is run
- %% on unoptimized code.
+ %% jump FailureLabel
{remote,{atom,erlang},{atom,error}} = Func, %Assertion.
[{var,DestVar}] = Rs,
Int0 = clear_dead(Bef, Le#l.i, Vdb),
Reg = put_reg(DestVar, Int0#sr.reg),
Int = Int0#sr{reg=Reg},
Dst = fetch_reg(DestVar, Reg),
- {[{jump,{f,Fail}},{move,{atom,ok},Dst}],
+ {[{move,{atom,ok},Dst},{jump,{f,Fail}}],
clear_dead(Int, Le#l.i, Vdb),St0};
#cg{} ->
%% Ordinary function call in a function body.
@@ -1327,12 +1343,13 @@ bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
%% that we save any variable that will be live after this BIF call.
MayFail = not erl_bifs:is_safe(erlang, Bif, length(As)),
- {Sis,Int0} = case St0#cg.in_catch andalso
- St0#cg.bfail =:= 0 andalso
- MayFail of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} =
+ case MayFail of
+ true ->
+ maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0);
+ false ->
+ {[],Bef}
+ end,
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Reg = put_reg(V, Int1#sr.reg),
Int = Int1#sr{reg=Reg},
@@ -1363,11 +1380,7 @@ gc_bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
%% Currently, we are somewhat pessimistic in
%% that we save any variable that will be live after this BIF call.
- {Sis,Int0} =
- case St0#cg.in_catch andalso St0#cg.bfail =:= 0 of
- true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Bef}
- end,
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0),
Int1 = clear_dead(Int0, Le#l.i, Vdb),
Reg = put_reg(V, Int1#sr.reg),
@@ -1512,8 +1525,7 @@ set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
Ret = fetch_reg(R, Int1#sr.reg),
{[{put_list,S1,S2,Ret}], Int1, St};
-set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
- #cg{in_catch=InCatch, bfail=Bfail}=St) ->
+set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{bfail=Bfail}=St) ->
%% At run-time, binaries are constructed in three stages:
%% 1) First the size of the binary is calculated.
%% 2) Then the binary is allocated.
@@ -1532,28 +1544,19 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef,
%% First generate the code that constructs each field.
Fail = {f,Bfail},
PutCode = cg_bin_put(Segs, Fail, Bef),
- {Sis,Int1} =
- case InCatch of
- true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb);
- false -> {[],Int0}
- end,
+ {Sis,Int1} = maybe_adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb, St),
MaxRegs = max_reg(Bef#sr.reg),
Aft = clear_dead(Int1, Le#l.i, Vdb),
%% Now generate the complete code for constructing the binary.
Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a),
{Sis++Code,Aft,St};
-% Map single variable key
-set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, 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),
+%% Map: single variable key.
+set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef, St0) ->
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0),
+
+ SrcReg = cg_reg_arg_prefer_y(Map, Int0),
Line = line(Le#l.a),
List = [cg_reg_arg(K,Int0),cg_reg_arg(V,Int0)],
@@ -1565,26 +1568,17 @@ set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef,
Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)},
Target = fetch_reg(R, Aft#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};
+ {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0),
+ {Sis++Is,Aft,St1};
-% Map (possibly) multiple literal keys
-set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,
- #cg{in_catch=InCatch,bfail=Bfail}=St) ->
+%% Map: (possibly) multiple literal keys.
+set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, St0) ->
%% assert key literals
[] = [Var||{map_pair,{var,_}=Var,_} <- Es],
- 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),
+ {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0),
+ SrcReg = cg_reg_arg_prefer_y(Map, Int0),
Line = line(Le#l.a),
%% fetch registers for values to be put into the map
@@ -1598,11 +1592,10 @@ set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef,
Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)},
Target = fetch_reg(R, Aft#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};
+ {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0),
+ {Sis++Is,Aft,St1};
+
+%% Everything else.
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)},
@@ -1615,6 +1608,34 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
end,
{Ais,clear_dead(Int, Le#l.i, Vdb),St}.
+
+set_cg_map(Line, Op0, SrcReg, Target, Live, List, St0) ->
+ Bfail = St0#cg.bfail,
+ Fail = {f,St0#cg.bfail},
+ Op = case Op0 of
+ assoc -> put_map_assoc;
+ exact -> put_map_exact
+ end,
+ {OkLbl,St1} = new_label(St0),
+ {BadLbl,St2} = new_label(St1),
+ Is = if
+ Bfail =:= 0 orelse Op =:= put_map_assoc ->
+ [Line,{Op,{f,0},SrcReg,Target,Live,{list,List}}];
+ true ->
+ %% Ensure that Target is always set, even if
+ %% the map update operation fails. That is necessary
+ %% because Target may be included in a test_heap
+ %% instruction.
+ [Line,
+ {Op,{f,BadLbl},SrcReg,Target,Live,{list,List}},
+ {jump,{f,OkLbl}},
+ {label,BadLbl},
+ {move,{atom,ok},Target},
+ {jump,Fail},
+ {label,OkLbl}]
+ end,
+ {Is,St2}.
+
%%%
%%% Code generation for constructing binaries.
%%%
@@ -1857,6 +1878,9 @@ cg_reg_args(As, Bef) -> [cg_reg_arg(A, Bef) || A <- As].
cg_reg_arg({var,V}, Bef) -> fetch_var(V, Bef);
cg_reg_arg(Literal, _) -> Literal.
+cg_reg_arg_prefer_y({var,V}, Bef) -> fetch_var_prefer_y(V, Bef);
+cg_reg_arg_prefer_y(Literal, _) -> Literal.
+
%% cg_setup_call([Arg], Bef, Cur, Vdb) -> {[Instr],Aft}.
%% Do the complete setup for a call/enter.
@@ -2038,6 +2062,19 @@ trim_free([R|Rs0]) ->
end;
trim_free([]) -> [].
+%% maybe_adjust_stack(Bef, FirstBefore, LastFrom, Vdb, St) -> {[Ainstr],Aft}.
+%% Adjust the stack, but only if the code is inside a catch and not
+%% inside a guard. Use this funtion before instructions that may
+%% cause an exception.
+
+maybe_adjust_stack(Bef, Fb, Lf, Vdb, St) ->
+ case St of
+ #cg{in_catch=true,bfail=0} ->
+ adjust_stack(Bef, Fb, Lf, Vdb);
+ #cg{} ->
+ {[],Bef}
+ end.
+
%% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}.
%% Do complete stack adjustment by compressing stack and adding
%% variables to be saved. Try to optimise ordering on stack by
@@ -2085,6 +2122,12 @@ fetch_var(V, Sr) ->
error -> fetch_stack(V, Sr#sr.stk)
end.
+fetch_var_prefer_y(V, #sr{reg=Reg,stk=Stk}) ->
+ case find_stack(V, Stk) of
+ {ok,R} -> R;
+ error -> fetch_reg(V, Reg)
+ end.
+
load_vars(Vs, Regs) ->
foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
@@ -2158,11 +2201,11 @@ fetch_stack(Var, Stk) -> fetch_stack(Var, Stk, 0).
fetch_stack(V, [{V}|_], I) -> {yy,I};
fetch_stack(V, [_|Stk], I) -> fetch_stack(V, Stk, I+1).
-% find_stack(Var, Stk) -> find_stack(Var, Stk, 0).
+find_stack(Var, Stk) -> find_stack(Var, Stk, 0).
-% find_stack(V, [{V}|Stk], I) -> {ok,{yy,I}};
-% find_stack(V, [O|Stk], I) -> find_stack(V, Stk, I+1);
-% find_stack(V, [], I) -> error.
+find_stack(V, [{V}|_], I) -> {ok,{yy,I}};
+find_stack(V, [_|Stk], I) -> find_stack(V, Stk, I+1);
+find_stack(_, [], _) -> error.
on_stack(V, Stk) -> keymember(V, 1, Stk).
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 0941ad5dd5..d71411de80 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -469,7 +469,8 @@ unforce_tree([#iset{var=#c_var{name=V},arg=Arg0}|Es], D0) ->
unforce_tree(Es, D);
unforce_tree([#icall{}=Call], D) ->
unforce_tree_subst(Call, D);
-unforce_tree([Top], _) -> Top.
+unforce_tree([#c_var{name=V}], D) ->
+ gb_trees:get(V, D).
unforce_tree_subst(#icall{module=#c_literal{val=erlang},
name=#c_literal{val='=:='},
@@ -680,9 +681,36 @@ expr({match,L,P0,E0}, St0) ->
Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),
case P2 of
nomatch ->
- St = add_warning(L, nomatch, St5),
- {#icase{anno=#a{anno=Lanno},
- args=[E2],clauses=[],fc=Fc},Eps1++Eps2,St};
+ %% The pattern will not match. We must take care here to
+ %% bind all variables that the pattern would have bound
+ %% so that subsequent expressions do not refer to unbound
+ %% variables.
+ %%
+ %% As an example, this code:
+ %%
+ %% [X] = {Y} = E,
+ %% X + Y.
+ %%
+ %% will be rewritten to:
+ %%
+ %% error({badmatch,E}),
+ %% case E of
+ %% {[X],{Y}} ->
+ %% X + Y;
+ %% Other ->
+ %% error({badmatch,Other})
+ %% end.
+ %%
+ St6 = add_warning(L, nomatch, St5),
+ {Expr,Eps3,St7} = safe(E1, St6),
+ SanPat0 = sanitize(P1),
+ {SanPat,Eps4,St} = pattern(SanPat0, St7),
+ Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),
+ Fail = #iprimop{anno=#a{anno=Lanno},
+ name=#c_literal{val=match_fail},
+ args=[Badmatch]},
+ Eps = Eps3 ++ Eps4 ++ [Fail],
+ {#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St};
Other when not is_atom(Other) ->
{#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5}
end;
@@ -724,6 +752,32 @@ expr({op,L,Op,L0,R0}, St0) ->
module=#c_literal{anno=LineAnno,val=erlang},
name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}.
+
+%% sanitize(Pat) -> SanitizedPattern
+%% Rewrite Pat so that it will be accepted by pattern/2 and will
+%% bind the same variables as the original pattern.
+%%
+%% Here is an example of a pattern that would cause a pattern/2
+%% to generate a 'nomatch' exception:
+%%
+%% #{k:=X,k:=Y} = [Z]
+%%
+%% The sanitized pattern will look like:
+%%
+%% {{X,Y},[Z]}
+
+sanitize({match,L,P1,P2}) ->
+ {tuple,L,[sanitize(P1),sanitize(P2)]};
+sanitize({cons,L,H,T}) ->
+ {cons,L,sanitize(H),sanitize(T)};
+sanitize({tuple,L,Ps0}) ->
+ Ps = [sanitize(P) || P <- Ps0],
+ {tuple,L,Ps};
+sanitize({map,L,Ps0}) ->
+ Ps = [sanitize(V) || {map_field_exact,_,_,V} <- Ps0],
+ {tuple,L,Ps};
+sanitize(P) -> P.
+
make_bool_switch(L, E, V, T, F, #core{in_guard=true}) ->
make_bool_switch_guard(L, E, V, T, F);
make_bool_switch(L, E, V, T, F, #core{}) ->
@@ -783,7 +837,7 @@ badmap_term(_Map, #core{in_guard=true}) ->
%% since it is not user-visible.
#c_literal{val=badmap};
badmap_term(Map, #core{in_guard=false}) ->
- #c_tuple{es=[#c_literal{val=badmap},Map]}.
+ c_tuple([#c_literal{val=badmap},Map]).
map_build_pairs(Map, Es0, Ann, St0) ->
{Es,Pre,St1} = map_build_pairs_1(Es0, St0),
@@ -804,7 +858,7 @@ map_op(map_field_assoc) -> #c_literal{val=assoc};
map_op(map_field_exact) -> #c_literal{val=exact}.
is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true;
-is_valid_map_src(#c_var{}) -> true;
+is_valid_map_src(#c_var{}=Var) -> not cerl:is_c_fname(Var);
is_valid_map_src(_) -> false.
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
@@ -814,12 +868,16 @@ try_exception(Ecs0, St0) ->
{Evs,St1} = new_vars(3, St0), % Tag, Value, Info
{Ecs1,Ceps,St2} = clauses(Ecs0, St1),
[_,Value,Info] = Evs,
- Ec = #iclause{anno=#a{anno=[compiler_generated]},
+ LA = case Ecs1 of
+ [] -> [];
+ [C|_] -> get_lineno_anno(C)
+ end,
+ Ec = #iclause{anno=#a{anno=[compiler_generated|LA]},
pats=[c_tuple(Evs)],guard=[#c_literal{val=true}],
body=[#iprimop{anno=#a{}, %Must have an #a{}
name=#c_literal{val=raise},
args=[Info,Value]}]},
- Hs = [#icase{anno=#a{},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}],
+ Hs = [#icase{anno=#a{anno=LA},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}],
{Evs,Ceps++Hs,St2}.
try_after(As, St0) ->
@@ -1079,13 +1137,39 @@ bc_tq1(Line, E, [#igen{anno=GAnno,ceps=Ceps,
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},
- {atom,Bl,all},
- [binary,{unit,1}]}|Elements]}, St0),
+ bc_tq_build(Bl, [], AccVar, Elements, St0);
+bc_tq1(Line, E0, [], AccVar, St0) ->
+ BsFlags = [binary,{unit,1}],
+ BsSize = {atom,Line,all},
+ {E1,Pre0,St1} = safe(E0, St0),
+ case E1 of
+ #c_var{name=VarName} ->
+ Var = {var,Line,VarName},
+ Els = [{bin_element,Line,Var,BsSize,BsFlags}],
+ bc_tq_build(Line, Pre0, AccVar, Els, St1);
+ #c_literal{val=Val} when is_bitstring(Val) ->
+ Bits = bit_size(Val),
+ <<Int0:Bits>> = Val,
+ Int = {integer,Line,Int0},
+ Sz = {integer,Line,Bits},
+ Els = [{bin_element,Line,Int,Sz,[integer,{unit,1},big]}],
+ bc_tq_build(Line, Pre0, AccVar, Els, St1);
+ _ ->
+ %% Any other safe (cons, tuple, literal) is not a
+ %% bitstring. Force the evaluation to fail (and
+ %% generate a warning).
+ Els = [{bin_element,Line,{atom,Line,bad_value},BsSize,BsFlags}],
+ bc_tq_build(Line, Pre0, AccVar, Els, St1)
+ end.
+
+bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) ->
+ Elements = [{bin_element,Line,{var,Line,AccVar},{atom,Line,all},
+ [binary,{unit,1}]}|Elements0],
+ {E,Pre,St} = expr({bin,Line,Elements}, St0),
#a{anno=A} = Anno0 = get_anno(E),
Anno = Anno0#a{anno=[compiler_generated,single_use|A]},
- {set_anno(E, Anno),Pre,St}.
+ {set_anno(E, Anno),Pre0++Pre,St}.
+
%% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) ->
%% {Case,[PreExpr],State}.
@@ -1306,7 +1390,9 @@ bc_elem_size({bin,_,El}, St0) ->
Vs = [V || {_,#c_var{name=V}} <- Vars0],
{E,Pre,St} = bc_mul_pairs(F, #c_literal{val=Bits}, [], St0),
{E,Pre,Vs,St}
- end.
+ end;
+bc_elem_size(_, _) ->
+ throw(impossible).
bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) ->
{unit,U} = keyfind(unit, 1, Flags),
@@ -1652,10 +1738,12 @@ pat_alias_map_pairs_1([]) -> [].
pat_bin(Ps, St) -> [pat_segment(P, St) || P <- Ps].
-pat_segment({bin_element,_,Val,Size,[Type,{unit,Unit}|Flags]}, St) ->
+pat_segment({bin_element,L,Val,Size,[Type,{unit,Unit}|Flags]}, St) ->
+ Anno = lineno_anno(L, St),
{Pval,[],St1} = pattern(Val,St),
{Psize,[],_St2} = pattern(Size,St1),
- #c_bitstr{val=Pval,size=Psize,
+ #c_bitstr{anno=Anno,
+ val=Pval,size=Psize,
unit=#c_literal{val=Unit},
type=#c_literal{val=Type},
flags=#c_literal{val=Flags}}.
@@ -1852,27 +1940,22 @@ uguard(Pg, Gs0, Ks, St0) ->
%% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
- %% Optimise for simple set of unbound variable.
- case upattern(P0, Ks, St0) of
- {#c_var{},[],_Pvs,_Pus,_} ->
- %% Throw our work away and just set to iset.
+ case upat_is_new_var(P0, Ks) of
+ true ->
+ %% Assignment to a new variable.
uexprs([#iset{var=P0,arg=Arg}|Les], Ks, St0);
- _Other ->
- %% Throw our work away and set to icase.
- if
- Les =:= [] ->
- %% Need to explicitly return match "value", make
- %% safe for efficiency.
- {La0,Lps,St1} = force_safe(Arg, St0),
- La = mark_compiler_generated(La0),
- Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]},
- uexprs(Lps ++ [#icase{anno=A,
- args=[La0],clauses=[Mc],fc=Fc}], Ks, St1);
- true ->
- Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les},
- uexprs([#icase{anno=A,args=[Arg],
- clauses=[Mc],fc=Fc}], Ks, St0)
- end
+ false when Les =:= [] ->
+ %% Need to explicitly return match "value", make
+ %% safe for efficiency.
+ {La0,Lps,St1} = force_safe(Arg, St0),
+ La = mark_compiler_generated(La0),
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]},
+ uexprs(Lps ++ [#icase{anno=A,
+ args=[La0],clauses=[Mc],fc=Fc}], Ks, St1);
+ false ->
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les},
+ uexprs([#icase{anno=A,args=[Arg],
+ clauses=[Mc],fc=Fc}], Ks, St0)
end;
uexprs([Le0|Les0], Ks, St0) ->
{Le1,St1} = uexpr(Le0, Ks, St0),
@@ -1880,6 +1963,15 @@ uexprs([Le0|Les0], Ks, St0) ->
{[Le1|Les1],St2};
uexprs([], _, St) -> {[],St}.
+%% upat_is_new_var(Pattern, [KnownVar]) -> true|false.
+%% Test whether the pattern is a single, previously unknown
+%% variable.
+
+upat_is_new_var(#c_var{name=V}, Ks) ->
+ not is_element(V, Ks);
+upat_is_new_var(_, _) ->
+ false.
+
%% Mark a "safe" as compiler-generated.
mark_compiler_generated(#c_cons{anno=A,hd=H,tl=T}) ->
ann_c_cons([compiler_generated|A], mark_compiler_generated(H),
@@ -2010,7 +2102,8 @@ upattern(#c_var{name=V}=Var, Ks, St0) ->
true ->
{N,St1} = new_var_name(St0),
New = #c_var{name=N},
- Test = #icall{anno=#a{us=add_element(N, [V])},
+ LA = get_lineno_anno(Var),
+ Test = #icall{anno=#a{anno=LA,us=add_element(N, [V])},
module=#c_literal{val=erlang},
name=#c_literal{val='=:='},
args=[New,Var]},
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 7ee564683b..b4bbc5e739 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -117,7 +117,7 @@ copy_anno(Kdst, Ksrc) ->
fcount=0, %Fun counter
ds=cerl_sets:new() :: cerl_sets:set(), %Defined variables
funs=[], %Fun functions
- free=[], %Free variables
+ free=#{}, %Free variables
ws=[] :: [warning()], %Warnings.
guard_refc=0}). %> 0 means in guard
@@ -143,8 +143,11 @@ attributes([]) -> [].
include_attribute(type) -> false;
include_attribute(spec) -> false;
+include_attribute(callback) -> false;
include_attribute(opaque) -> false;
include_attribute(export_type) -> false;
+include_attribute(record) -> false;
+include_attribute(optional_callbacks) -> false;
include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
@@ -239,7 +242,7 @@ gexpr_test_add(Ke, St0) ->
expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) ->
%% A local in an expression.
%% For now, these are wrapped into a fun by reverse
- %% etha-conversion, but really, there should be exactly one
+ %% eta-conversion, but really, there should be exactly one
%% such "lambda function" for each escaping local name,
%% instead of one for each occurrence as done now.
Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
@@ -399,7 +402,7 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
Call = #c_call{anno=A,
module=#c_literal{val=erlang},
name=#c_literal{val=apply},
- args=[M0,F0,make_list(Cargs)]},
+ args=[M0,F0,cerl:make_list(Cargs)]},
expr(Call, Sub, St1);
_ ->
{[M1,F1|Kargs],Ap,St} = atomic_list([M0,F0|Cargs], Sub, St1),
@@ -494,7 +497,7 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
end.
translate_fc(Args) ->
- [#c_literal{val=function_clause},make_list(Args)].
+ [#c_literal{val=function_clause},cerl:make_list(Args)].
expr_map(A,Var0,Ces,Sub,St0) ->
{Var,Mps,St1} = expr(Var0, Sub, St0),
@@ -1837,14 +1840,17 @@ handle_reuse_anno_1(V, _St) -> V.
%% get_free(Name, Arity, State) -> [Free].
%% store_free(Name, Arity, [Free], State) -> State.
-get_free(F, A, St) ->
- case orddict:find({F,A}, St#kern.free) of
- {ok,Val} -> Val;
- error -> []
+get_free(F, A, #kern{free=FreeMap}) ->
+ Key = {F,A},
+ case FreeMap of
+ #{Key:=Val} -> Val;
+ _ -> []
end.
-store_free(F, A, Free, St) ->
- St#kern{free=orddict:store({F,A}, Free, St#kern.free)}.
+store_free(F, A, Free, #kern{free=FreeMap0}=St) ->
+ Key = {F,A},
+ FreeMap = FreeMap0#{Key=>Free},
+ St#kern{free=FreeMap}.
break_rets({break,Rs}) -> Rs;
break_rets(return) -> [].
@@ -1983,11 +1989,6 @@ pat_list_vars(Ps) ->
{union(Used0, Used),union(New0, New)} end,
{[],[]}, Ps).
-make_list(Es) ->
- foldr(fun(E, Acc) ->
- #c_cons{hd=E,tl=Acc}
- end, #c_literal{val=[]}, Es).
-
%% List of integers in interval [N,M]. Empty list if N > M.
integers(N, M) when N =< M ->
diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl
index 03b2eae006..5216a1a620 100644
--- a/lib/compiler/src/v3_kernel.hrl
+++ b/lib/compiler/src/v3_kernel.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index 03b034ae98..0b90f0a1e0 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index fa057ae211..1452b78d1d 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/compiler/src/v3_life.hrl b/lib/compiler/src/v3_life.hrl
index 29a08f8c72..9d03a86ccd 100644
--- a/lib/compiler/src/v3_life.hrl
+++ b/lib/compiler/src/v3_life.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.