aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/doc/src/Makefile2
-rw-r--r--lib/compiler/doc/src/notes.xml16
-rw-r--r--lib/compiler/internal_doc/cerl-notes.md75
-rw-r--r--lib/compiler/src/beam_listing.erl14
-rw-r--r--lib/compiler/src/cerl_clauses.erl2
-rw-r--r--lib/compiler/src/compile.erl14
-rw-r--r--lib/compiler/test/compile_SUITE.erl21
-rw-r--r--lib/compiler/test/compile_SUITE_data/deterministic_module.erl21
8 files changed, 155 insertions, 10 deletions
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index 254445c111..13210de040 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -39,7 +39,7 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = compile.xml
XML_PART_FILES =
-XML_CHAPTER_FILES = notes.xml notes_history.xml
+XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 433fc3b86e..2aec75a2aa 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -510,6 +510,22 @@
</section>
+
+<section><title>Compiler 6.0.3.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fail labels on guard BIFs weren't taken into account
+ during an optimization pass, and a bug in the validation
+ pass sometimes prevented this from being noticed when a
+ fault occurred.</p>
+ <p>
+ Own Id: OTP-14522 Aux Id: ERIERL-48 </p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>Compiler 6.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/internal_doc/cerl-notes.md b/lib/compiler/internal_doc/cerl-notes.md
new file mode 100644
index 0000000000..705fe8f42d
--- /dev/null
+++ b/lib/compiler/internal_doc/cerl-notes.md
@@ -0,0 +1,75 @@
+Some notes on the cerl modules
+==============================
+
+Maps in cerl_clauses:match/3
+----------------------------
+
+Not much optimization is done for maps in `cerl_clauses:match/3`.
+
+The reason is that the inliner (`cerl_inline`) was not designed for
+data types that depend on variables bound in the enclosing environment
+(for example, the keys for maps). If we attempt to extend the
+optimizations for maps similar to the optimizations for the other data
+types, the inliner will crash for certain code. Here is an example of
+code that would crash the inliner:
+
+ t() ->
+ f(key).
+
+ f(K) ->
+ case #{K => value} of
+ #{key := V} -> V
+ end.
+
+The reason for the crash is that the inliner works in several
+passes and calls `cerl_clauses:match/3` twice. The inliner will
+assume that the same result will be returned both times, but
+for this example different bindings will be returned.
+
+Here is the rough outline of what happens:
+
+* The first time `cerl_clauses:match/3` will be asked to match the
+pattern `#{K := V}` against `#{key => value}`. It cannot say more
+than that the pattern *may* match.
+
+* Now the inliner will add the bindings to body of the clause (which
+is simply `V`). In this case, the bindings are still empty, so
+nothing is added.
+
+* The inliner will now do some substitutions and renaming. The
+variable `K` will be replaced with `key`.
+
+* The next time `cerl_clauses:match/3` is called, it will be asked to
+match the pattern `#{key := V}` against `#{key => value#}`. In this
+case, there will be a match and the bindings can be extended with
+`{V,value}`.
+
+* The inliner will see that the entire case can be removed. Only
+the body for the clause needs to be kept.
+
+Thus, after inlining the function `t/0` will look like this:
+
+ t() ->
+ V.
+
+The problem here is that the inliner assumed that the bindings from
+the first and second call to `cer_clauses:match/3` would be the same.
+It used the empty bindings from the first call for the body.
+
+The correct way would be to add the bindings from the second call:
+
+ t() ->
+ let V = value in V.
+
+### How to fix this ###
+
+The inliner will need to call `cerl_clauses:match/3` after doing
+all substitutions and renaming. It is not clear to me how difficult
+that would be. I assume that the inliner is written the way it is
+for a good reason. That means that switching the order things are
+done would lead to correctness and/or performance problems.
+
+### What must also be done to fix this ###
+
+`cerl_inline:make_template/3` must be extended to create a template
+for maps. That is relatively straightforward.
diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl
index 9422f9a78a..d1d00017e9 100644
--- a/lib/compiler/src/beam_listing.erl
+++ b/lib/compiler/src/beam_listing.erl
@@ -23,6 +23,7 @@
-include("core_parse.hrl").
-include("v3_kernel.hrl").
+-include("beam_disasm.hrl").
-import(lists, [foreach/2]).
@@ -53,6 +54,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/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index 7d6518c3c6..fa5104c01b 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -353,6 +353,8 @@ match(P, E, Bs) ->
map ->
%% The most we can do is to say "definitely no match" if a
%% map pattern is matched against non-map data.
+ %% (Note: See the document internal_doc/cerl-notes.md for
+ %% information why we don't try to do more here.)
case E of
any ->
{false, Bs};
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index bc519264fc..f7beed430c 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.
@@ -1448,16 +1450,16 @@ beam_asm(Code0, #compile{ifile=File,extra_chunks=ExtraChunks,options=CompilerOpt
{ok,DebugInfo,Opts0} ->
Opts1 = [O || O <- Opts0, effects_code_generation(O)],
Chunks = [{<<"Dbgi">>, DebugInfo} | ExtraChunks],
- CompileInfo = compile_info(File, Opts1),
+ CompileInfo = compile_info(File, CompilerOpts, Opts1),
{ok,Code} = beam_asm:module(Code0, Chunks, CompileInfo, CompilerOpts),
{ok,Code,St#compile{abstract_code=[]}};
{error,Es} ->
{error,St#compile{errors=St#compile.errors ++ [{File,Es}]}}
end.
-compile_info(File, Opts) ->
- IsSlim = member(slim, Opts),
- IsDeterministic = member(deterministic, Opts),
+compile_info(File, CompilerOpts, Opts) ->
+ IsSlim = member(slim, CompilerOpts),
+ IsDeterministic = member(deterministic, CompilerOpts),
Info0 = proplists:get_value(compile_info, Opts, []),
Info1 =
case paranoid_absname(File) of
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index e88a132d59..daebbe9d9d 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -120,9 +120,19 @@ file_1(Config) when is_list(Config) ->
true = exists(Target),
passed = run(Target, test, []),
+ %% Test option 'deterministic' as a compiler attribute.
+ Det = deterministic_module,
+ {DetPath, DetTarget} = get_files(Config, Det, "det_target"),
+ {ok,Det,DetCode} = compile:file(DetPath, [binary]),
+ {module,Det} = code:load_binary(Det, "", DetCode),
+ [{version,_}] = Det:module_info(compile),
+ true = code:delete(Det),
+ false = code:purge(Det),
+
%% Cleanup.
ok = file:delete(Target),
ok = file:del_dir(filename:dirname(Target)),
+ ok = file:del_dir(filename:dirname(DetTarget)),
%% There should not be any messages in the messages.
receive
@@ -398,6 +408,7 @@ do_file_listings(DataDir, PrivDir, [File|Files]) ->
ok = file:delete(filename:join(Listings, File ++ ".core")),
do_listing(Simple, TargetDir, to_core, ".core"),
do_listing(Simple, TargetDir, to_kernel, ".kernel"),
+ do_listing(Simple, TargetDir, to_dis, ".dis"),
%% Final clean up.
lists:foreach(fun(F) -> ok = file:delete(F) end,
@@ -413,6 +424,7 @@ listings_big(Config) when is_list(Config) ->
do_listing(Big, TargetDir, 'E'),
do_listing(Big, TargetDir, 'P'),
do_listing(Big, TargetDir, dkern, ".kernel"),
+ do_listing(Big, TargetDir, to_dis, ".dis"),
TargetNoext = filename:rootname(Target, code:objfile_extension()),
{ok,big} = compile:file(TargetNoext, [from_asm,{outdir,TargetDir}]),
@@ -1318,10 +1330,13 @@ do_warnings_2([], Next, F) ->
%% pre-loads the modules that are used by a typical compilation.
pre_load_check(Config) ->
- case test_server:is_cover() of
- true ->
+ case {test_server:is_cover(),code:module_info(native)} of
+ {true,_} ->
{skip,"Cover is running"};
- false ->
+ {false,true} ->
+ %% Tracing won't work.
+ {skip,"'code' is native-compiled"};
+ {false,false} ->
try
do_pre_load_check(Config)
after
diff --git a/lib/compiler/test/compile_SUITE_data/deterministic_module.erl b/lib/compiler/test/compile_SUITE_data/deterministic_module.erl
new file mode 100644
index 0000000000..5e0e29c25e
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/deterministic_module.erl
@@ -0,0 +1,21 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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(deterministic_module).
+-compile([deterministic]).