aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src')
-rw-r--r--lib/compiler/src/beam_asm.erl9
-rw-r--r--lib/compiler/src/beam_listing.erl14
-rw-r--r--lib/compiler/src/beam_type.erl26
-rw-r--r--lib/compiler/src/beam_utils.erl61
-rw-r--r--lib/compiler/src/beam_validator.erl32
-rw-r--r--lib/compiler/src/compile.erl34
-rw-r--r--lib/compiler/src/sys_core_fold.erl6
7 files changed, 109 insertions, 73 deletions
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index c35efdfc9d..f7c838e392 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -182,7 +182,8 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, ExtraChunks, SourceFile, Opts,
Essentials1 = [iolist_to_binary(C) || C <- Essentials0],
MD5 = module_md5(Essentials1),
Essentials = finalize_fun_table(Essentials1, MD5),
- {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, MD5),
+ {Attributes,Compile} = build_attributes(Opts, CompilerOpts, SourceFile,
+ Attr, MD5),
AttrChunk = chunk(<<"Attr">>, Attributes),
CompileChunk = chunk(<<"CInf">>, Compile),
@@ -264,16 +265,16 @@ flatten_exports(Exps) ->
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) ->
+build_attributes(Opts, CompilerOpts, SourceFile, Attr, MD5) ->
Misc0 = case SourceFile of
[] -> [];
[_|_] -> [{source,SourceFile}]
end,
- Misc = case member(slim, Opts) of
+ Misc = case member(slim, CompilerOpts) of
false -> Misc0;
true -> []
end,
- Compile = case member(deterministic, Opts) of
+ Compile = case member(deterministic, CompilerOpts) of
false ->
[{options,Opts},{version,?COMPILER_VSN}|Misc];
true ->
diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl
index 94b47cf568..836378727b 100644
--- a/lib/compiler/src/beam_listing.erl
+++ b/lib/compiler/src/beam_listing.erl
@@ -24,6 +24,7 @@
-include("core_parse.hrl").
-include("v3_kernel.hrl").
-include("v3_life.hrl").
+-include("beam_disasm.hrl").
-import(lists, [foreach/2]).
@@ -59,6 +60,19 @@ module(Stream, {Mod,Exp,Attr,Code,NumLabels}) ->
[Name, Arity, Entry]),
io:put_chars(Stream, format_asm(Asm))
end, Code);
+module(Stream, Code) when is_binary(Code) ->
+ #beam_file{ module = Module, compile_info = CInfo } = beam_disasm:file(Code),
+ Loaded = code:is_loaded(Module),
+ Sticky = code:is_sticky(Module),
+ [code:unstick_mod(Module) || Sticky],
+
+ {module, Module} = code:load_binary(Module, proplists:get_value(source, CInfo), Code),
+ ok = erts_debug:df(Stream, Module),
+
+ %% Restore loaded module
+ _ = [{module, Module} = code:load_file(Module) || Loaded =/= false],
+ [code:stick_mod(Module) || Sticky],
+ ok;
module(Stream, [_|_]=Fs) ->
%% Form-based abstract format.
foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs).
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 3d842a6fd3..fc2c7a991b 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -117,14 +117,6 @@ simplify_basic_1([{test,is_tuple,_,[R]}=I|Is], Ts, Acc) ->
{tuple,_,_} -> simplify_basic_1(Is, Ts, Acc);
_ -> simplify_basic_1(Is, Ts, [I|Acc])
end;
-simplify_basic_1([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,Arity,_} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc])
- end;
simplify_basic_1([{test,is_map,_,[R]}=I|Is], Ts0, Acc) ->
case tdb_find(R, Ts0) of
map -> simplify_basic_1(Is, Ts0, Acc);
@@ -147,14 +139,6 @@ simplify_basic_1([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Acc0) -
end,
Ts = update(I, Ts0),
simplify_basic_1(Is0, Ts, Acc);
-simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0, Acc) ->
- case tdb_find(R, Ts0) of
- {tuple,Arity,[Tag]} ->
- simplify_basic_1(Is, Ts0, Acc);
- _Other ->
- 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} ->
@@ -367,6 +351,8 @@ flt_need_heap_2({set,_,_,get_list}, H, Fl) ->
{[],H,Fl};
flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) ->
{[],H,Fl};
+flt_need_heap_2({set,_,_,init}, H, Fl) ->
+ {[],H,Fl};
%% All other instructions should cause the insertion of an allocation
%% instruction if needed.
flt_need_heap_2(_, H, Fl) ->
@@ -928,10 +914,10 @@ 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, {integer,_}) ->
+ integer;
+merge_type_info({integer,_}, integer) ->
+ integer;
merge_type_info({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) ->
{integer,{max(Min1, Min2),min(Max1, Max2)}};
merge_type_info(NewType, _) ->
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index cc6e54ca16..e39fbdc3b7 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -789,39 +789,48 @@ live_opt([{recv_mark,_}=I|Is], Regs, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,Ds,Ss,Op}=I0|Is], Regs0, D, Acc) ->
+live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) ->
Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
- {I,Regs} = case Op of
- {alloc,Live0,Alloc} ->
- %% The life-time analysis used by the code generator
- %% is sometimes too conservative, so it may be
- %% possible to lower the number of live registers
- %% based on the exact liveness information.
- %% The main benefit is that more optimizations that
- %% depend on liveness information (such as the
- %% beam_bool and beam_dead passes) may be applied.
- Live = live_regs(Regs1),
- true = Live =< Live0, %Assertion.
- I1 = {set,Ds,Ss,{alloc,Live,Alloc}},
- {I1,live_call(Live)};
- _ ->
- {I0,Regs1}
- end,
+ {Op, Regs} = live_opt_block_op(Op0, Regs1, D),
+ I = {set, Ds, Ss, Op},
+
case Ds of
- [{x,X}] ->
- case (not is_live(X, Regs0)) andalso Op =:= move of
- true ->
- live_opt_block(Is, Regs0, D, Acc);
- false ->
- live_opt_block(Is, Regs, D, [I|Acc])
- end;
- _ ->
- live_opt_block(Is, Regs, D, [I|Acc])
+ [{x,X}] ->
+ case (not is_live(X, Regs0)) andalso Op =:= move of
+ true ->
+ live_opt_block(Is, Regs0, D, Acc);
+ false ->
+ live_opt_block(Is, Regs, D, [I|Acc])
+ end;
+ _ ->
+ live_opt_block(Is, Regs, D, [I|Acc])
end;
live_opt_block([{'%live',_,_}|Is], Regs, D, Acc) ->
live_opt_block(Is, Regs, D, Acc);
live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
+live_opt_block_op({alloc,Live0,AllocOp}, Regs0, D) ->
+ Regs =
+ case AllocOp of
+ {Kind, _N, Fail} when Kind =:= gc_bif; Kind =:= put_map ->
+ live_join_label(Fail, D, Regs0);
+ _ ->
+ Regs0
+ end,
+
+ %% The life-time analysis used by the code generator is sometimes too
+ %% conservative, so it may be possible to lower the number of live
+ %% registers based on the exact liveness information. The main benefit is
+ %% that more optimizations that depend on liveness information (such as the
+ %% beam_bool and beam_dead passes) may be applied.
+ Live = live_regs(Regs),
+ true = Live =< Live0,
+ {{alloc,Live,AllocOp}, live_call(Live)};
+live_opt_block_op({bif,_N,Fail} = Op, Regs, D) ->
+ {Op, live_join_label(Fail, D, Regs)};
+live_opt_block_op(Op, Regs, _D) ->
+ {Op, Regs}.
+
live_join_labels([{f,L}|T], D, Regs0) when L =/= 0 ->
Regs = gb_trees:get(L, D) bor Regs0,
live_join_labels(T, D, Regs);
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index f726625510..ea38969814 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -529,9 +529,10 @@ valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
Type = bif_type(Op, Src, Vst),
set_type_reg(Type, Dst, Vst);
valfun_4({gc_bif,Op,{f,Fail},Live,Src,Dst}, #vst{current=St0}=Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
St = kill_heap_allocation(St0),
Vst1 = Vst0#vst{current=St},
- verify_live(Live, Vst1),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
validate_src(Src, Vst),
@@ -685,6 +686,7 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -697,6 +699,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -709,6 +712,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
assert_term(Bits, Vst0),
assert_term(Bin, Vst0),
Vst1 = heap_alloc(Heap, Vst0),
@@ -928,9 +932,9 @@ verify_call_match_context(Lbl, Ctx, #vst{ft=Ft}) ->
error({unsuitable_bs_start_match2,I})
end.
-allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst0) ->
+allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}}=Vst0) ->
verify_live(Live, Vst0),
- Vst = prune_x_regs(Live, Vst0),
+ Vst = #vst{current=St} = prune_x_regs(Live, Vst0),
Ys = init_regs(Stk, case Zero of
true -> initialized;
false -> uninitialized
@@ -944,6 +948,7 @@ deallocate(#vst{current=St}=Vst) ->
test_heap(Heap, Live, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
heap_alloc(Heap, Vst).
@@ -1324,7 +1329,12 @@ branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
Vst = branch_state(L, Vst1),
branch_arities(T, Tuple, Vst#vst{current=St}).
-branch_state(0, #vst{}=Vst) -> Vst;
+branch_state(0, #vst{}=Vst) ->
+ %% If the instruction fails, the stack may be scanned
+ %% looking for a catch tag. Therefore the Y registers
+ %% must be initialized at this point.
+ verify_y_init(Vst),
+ Vst;
branch_state(L, #vst{current=St,branched=B}=Vst) ->
Vst#vst{
branched=case gb_trees:is_defined(L, B) of
@@ -1430,13 +1440,13 @@ merge_types(bool, {atom,A}) ->
merge_bool(A);
merge_types({atom,A}, bool) ->
merge_bool(A);
-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(_, #ms{}=M) ->
- M;
+merge_types(#ms{id=Id1,valid=B1,slots=Slots1},
+ #ms{id=Id2,valid=B2,slots=Slots2}) ->
+ Id = if
+ Id1 =:= Id2 -> Id1;
+ true -> make_ref()
+ end,
+ #ms{id=Id,valid=B1 band B2,slots=min(Slots1, Slots2)};
merge_types(T1, T2) when T1 =/= T2 ->
%% Too different. All we know is that the type is a 'term'.
term.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index aa2d224bb4..bdb52a02ad 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -787,8 +787,10 @@ asm_passes() ->
| binary_passes()].
binary_passes() ->
- [{native_compile,fun test_native/1,fun native_compile/2},
- {unless,binary,?pass(save_binary,not_werror)}].
+ [{iff,'to_dis',{listing,"dis"}},
+ {native_compile,fun test_native/1,fun native_compile/2},
+ {unless,binary,?pass(save_binary,not_werror)}
+ ].
%%%
%%% Compiler passes.
@@ -1406,16 +1408,30 @@ encrypt_debug_info(DebugInfo, Key, Opts) ->
end.
cleanup_compile_options(Opts) ->
- lists:filter(fun keep_compile_option/1, Opts).
-
+ IsDeterministic = lists:member(deterministic, Opts),
+ lists:filter(fun(Opt) ->
+ keep_compile_option(Opt, IsDeterministic)
+ end, Opts).
+
+%% Include paths and current directory don't affect compilation, but they might
+%% be helpful so we include them unless we're doing a deterministic build.
+keep_compile_option({i, _}, Deterministic) ->
+ not Deterministic;
+keep_compile_option({cwd, _}, Deterministic) ->
+ not Deterministic;
%% We are storing abstract, not asm or core.
-keep_compile_option(from_asm) -> false;
-keep_compile_option(from_core) -> false;
+keep_compile_option(from_asm, _Deterministic) ->
+ false;
+keep_compile_option(from_core, _Deterministic) ->
+ false;
%% Parse transform and macros have already been applied.
-keep_compile_option({parse_transform, _}) -> false;
-keep_compile_option({d, _, _}) -> false;
+keep_compile_option({parse_transform, _}, _Deterministic) ->
+ false;
+keep_compile_option({d, _, _}, _Deterministic) ->
+ false;
%% Do not affect compilation result on future calls.
-keep_compile_option(Option) -> effects_code_generation(Option).
+keep_compile_option(Option, _Deterministic) ->
+ effects_code_generation(Option).
start_crypto() ->
try crypto:start() of
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index e0cd6da06f..d73060fb7e 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -395,10 +395,10 @@ expr(#c_receive{clauses=Cs0,timeout=T0,action=A0}=Recv, Ctxt, Sub) ->
expr(#c_apply{anno=Anno,op=Op0,args=As0}=App, _, Sub) ->
Op1 = expr(Op0, value, Sub),
As1 = expr_list(As0, value, Sub),
- case Op1 of
- #c_var{} ->
+ case cerl:is_data(Op1) of
+ false ->
App#c_apply{op=Op1,args=As1};
- _ ->
+ true ->
add_warning(App, invalid_call),
Err = #c_call{anno=Anno,
module=#c_literal{val=erlang},