diff options
Diffstat (limited to 'lib/compiler/src')
-rw-r--r-- | lib/compiler/src/Makefile | 4 | ||||
-rw-r--r-- | lib/compiler/src/beam_block.erl | 35 | ||||
-rw-r--r-- | lib/compiler/src/beam_dict.erl | 30 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 36 | ||||
-rw-r--r-- | lib/compiler/src/beam_validator.erl | 2 | ||||
-rw-r--r-- | lib/compiler/src/cerl.erl | 6 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 331 | ||||
-rw-r--r-- | lib/compiler/src/core_lint.erl | 25 | ||||
-rw-r--r-- | lib/compiler/src/rec_env.erl | 4 | ||||
-rw-r--r-- | lib/compiler/src/v3_codegen.erl | 6 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 78 | ||||
-rw-r--r-- | lib/compiler/src/v3_kernel.erl | 1 | ||||
-rw-r--r-- | lib/compiler/src/v3_kernel_pp.erl | 55 |
13 files changed, 437 insertions, 176 deletions
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 0f6d2f6193..9da9253f5b 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -118,7 +118,9 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) ifeq ($(NATIVE_LIBS_ENABLED),yes) ERL_COMPILE_FLAGS += +native endif -ERL_COMPILE_FLAGS += +inline +warn_unused_import -I../../stdlib/include -I$(EGEN) -W +ERL_COMPILE_FLAGS += +inline +warn_unused_import \ + +warnings_as_errors \ + -I../../stdlib/include -I$(EGEN) -W # ---------------------------------------------------- # Targets diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 9c6f835ab0..c45874597a 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -36,12 +36,13 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) -> %% Collect basic blocks and optimize them. Is2 = blockify(Is1), - Is3 = beam_utils:live_opt(Is2), - Is4 = opt_blocks(Is3), - Is5 = beam_utils:delete_live_annos(Is4), + Is3 = move_allocates(Is2), + Is4 = beam_utils:live_opt(Is3), + Is5 = opt_blocks(Is4), + Is6 = beam_utils:delete_live_annos(Is5), %% Optimize bit syntax. - {Is,Lc} = bsm_opt(Is5, Lc0), + {Is,Lc} = bsm_opt(Is6, Lc0), %% Done. {{function,Name,Arity,CLabel,Is},Lc} @@ -156,11 +157,7 @@ opt_blocks([I|Is]) -> opt_blocks([]) -> []. opt_block(Is0) -> - %% We explicitly move any allocate instruction upwards before optimising - %% moves, to avoid any potential problems with the calculation of live - %% registers. - Is1 = move_allocates(Is0), - Is = find_fixpoint(fun opt/1, Is1), + Is = find_fixpoint(fun opt/1, Is0), opt_alloc(Is). find_fixpoint(OptFun, Is0) -> @@ -170,11 +167,21 @@ find_fixpoint(OptFun, Is0) -> end. %% move_allocates(Is0) -> Is -%% Move allocates upwards in the instruction stream, in the hope of -%% getting more possibilities for optimizing away moves later. - -move_allocates(Is) -> - move_allocates_1(reverse(Is), []). +%% Move allocate instructions upwards in the instruction stream, in the +%% hope of getting more possibilities for optimizing away moves later. +%% +%% NOTE: Moving allocation instructions is only safe because it is done +%% immediately after code generation so that we KNOW that if {x,X} is +%% initialized, all x registers with lower numbers are also initialized. +%% That assumption may not be true after other optimizations, such as +%% the beam_utils:live_opt/1 optimization. + +move_allocates([{block,Bl0}|Is]) -> + Bl = move_allocates_1(reverse(Bl0), []), + [{block,Bl}|move_allocates(Is)]; +move_allocates([I|Is]) -> + [I|move_allocates(Is)]; +move_allocates([]) -> []. move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) -> {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0), diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 4ffe8bc606..a1f994dfbd 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -33,7 +33,7 @@ exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index} - strings = [] :: [string()], %String pool + strings = <<>> :: binary(), %String pool lambdas = [], %[{...}] literals = dict:new() :: dict(), %Format: {Literal,Number} next_atom = 1 :: pos_integer(), @@ -119,10 +119,11 @@ import(Mod0, Name0, Arity, #asm{imports=Imp0,next_import=NextIndex}=D0) string(Str, Dict) when is_list(Str) -> #asm{strings=Strings,string_offset=NextOffset} = Dict, - case old_string(Str, Strings) of + StrBin = list_to_binary(Str), + case old_string(StrBin, Strings) of none -> - NewDict = Dict#asm{strings=Strings++Str, - string_offset=NextOffset+length(Str)}, + NewDict = Dict#asm{strings = <<Strings/binary,StrBin/binary>>, + string_offset=NextOffset+byte_size(StrBin)}, {NextOffset,NewDict}; Offset when is_integer(Offset) -> {NextOffset-Offset,Dict} @@ -187,7 +188,7 @@ import_table(#asm{imports=Imp,next_import=NumImports}) -> ImpTab = [MFA || {MFA,_} <- Sorted], {NumImports,ImpTab}. --spec string_table(bdict()) -> {non_neg_integer(), [string()]}. +-spec string_table(bdict()) -> {non_neg_integer(), binary()}. string_table(#asm{strings=Strings,string_offset=Size}) -> {Size,Strings}. @@ -217,15 +218,12 @@ literal_table(#asm{literals=Tab,next_literal=NumLiterals}) -> my_term_to_binary(Term) -> term_to_binary(Term, [{minor_version,1}]). -%% Search for string Str in the string pool Pool. +%% Search for binary string Str in the binary string pool Pool. %% old_string(Str, Pool) -> none | Index --spec old_string(string(), [string()]) -> 'none' | pos_integer(). - -old_string([C|Str]=Str0, [C|Pool]) -> - case lists:prefix(Str, Pool) of - true -> length(Pool)+1; - false -> old_string(Str0, Pool) - end; -old_string([_|_]=Str, [_|Pool]) -> - old_string(Str, Pool); -old_string([_|_], []) -> none. +-spec old_string(binary(), binary()) -> 'none' | pos_integer(). + +old_string(Str, Pool) -> + case binary:match(Pool, Str) of + nomatch -> none; + {Start,_Length} -> byte_size(Pool) - Start + end. diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 761d4ffec0..45cdf8a659 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -407,16 +407,23 @@ check_liveness(R, [{bif,Op,{f,Fail},Ss,D}|Is], St0) -> Other -> Other end; -check_liveness(R, [{gc_bif,Op,{f,Fail},_,Ss,D}|Is], St0) -> - case check_liveness_fail(R, Op, Ss, Fail, St0) of - {killed,St} = Killed -> - case member(R, Ss) of - true -> {used,St}; - false when R =:= D -> Killed; - false -> check_liveness(R, Is, St) - end; - Other -> - Other +check_liveness(R, [{gc_bif,Op,{f,Fail},Live,Ss,D}|Is], St0) -> + case R of + {x,X} when X >= Live -> + {killed,St0}; + {x,_} -> + {used,St0}; + _ -> + case check_liveness_fail(R, Op, Ss, Fail, St0) of + {killed,St}=Killed -> + case member(R, Ss) of + true -> {used,St}; + false when R =:= D -> Killed; + false -> check_liveness(R, Is, St) + end; + Other -> + Other + end end; check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) -> case member(R, Ss) of @@ -482,10 +489,13 @@ check_liveness(R, [{bs_context_to_binary,S}|Is], St) -> S -> {used,St}; _ -> check_liveness(R, Is, St) end; -check_liveness(R, [{loop_rec,{f,_},{x,0}}|Is], St) -> +check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) -> case R of - {x,_} -> {killed,St}; - _ -> check_liveness(R, Is, St) + {x,_} -> + {killed,St}; + _ -> + %% y register. Rarely happens. Be very conversative. + {unknown,St} end; check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) -> check_liveness_at(R, Fail, St); diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index f3a2b01e04..fb267b35b6 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -20,6 +20,8 @@ -compile({no_auto_import,[min/2]}). +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import,[error/1]}). -export([file/1, files/1]). %% Interface for compiler. diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index d1fd9d40e2..4b74d60e9f 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -973,7 +973,7 @@ atom_name(Node) -> %% TODO: replace the use of the unofficial 'write_string/2'. --spec atom_lit(cerl()) -> string(). +-spec atom_lit(cerl()) -> nonempty_string(). atom_lit(Node) -> io_lib:write_string(atom_name(Node), $'). %' stupid Emacs. @@ -1079,7 +1079,7 @@ char_val(Node) -> %% %% @see c_char/1 --spec char_lit(c_literal()) -> string(). +-spec char_lit(c_literal()) -> nonempty_string(). char_lit(Node) -> io_lib:write_char(char_val(Node)). @@ -1178,7 +1178,7 @@ string_val(Node) -> %% %% @see c_string/1 --spec string_lit(c_literal()) -> string(). +-spec string_lit(c_literal()) -> nonempty_string(). string_lit(Node) -> io_lib:write_string(string_val(Node)). diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 4642fb68b3..ce8a5bf864 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,8 +41,7 @@ -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. --type line() :: integer(). --type err_info() :: {line(), module(), term()}. %% ErrorDescriptor +-type err_info() :: {erl_scan:line(), module(), term()}. %% ErrorDescriptor -type errors() :: [{file:filename(), [err_info()]}]. -type warnings() :: [{file:filename(), [err_info()]}]. -type mod_ret() :: {'ok', module()} @@ -70,7 +69,7 @@ file(File) -> file(File, ?DEFAULT_OPTIONS). --spec file(module() | file:filename(), [option()]) -> comp_ret(). +-spec file(module() | file:filename(), [option()] | option()) -> comp_ret(). file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts++env_default_opts()); @@ -88,6 +87,8 @@ forms(Forms, Opt) when is_atom(Opt) -> %% would have generated a Beam file, false otherwise (if only a binary or a %% listing file would have been generated). +-spec output_generated([option()]) -> boolean(). + output_generated(Opts) -> noenv_output_generated(Opts++env_default_opts()). @@ -96,6 +97,8 @@ output_generated(Opts) -> %% for default options. %% +-spec noenv_file(module() | file:filename(), [option()] | option()) -> comp_ret(). + noenv_file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts); noenv_file(File, Opt) -> @@ -106,10 +109,13 @@ noenv_forms(Forms, Opts) when is_list(Opts) -> noenv_forms(Forms, Opt) when is_atom(Opt) -> noenv_forms(Forms, [Opt|?DEFAULT_OPTIONS]). +-spec noenv_output_generated([option()]) -> boolean(). + noenv_output_generated(Opts) -> + {_,Passes} = passes(file, expand_opts(Opts)), any(fun ({save_binary,_F}) -> true; (_Other) -> false - end, passes(file, expand_opts(Opts))). + end, Passes). %% %% Local functions @@ -200,6 +206,9 @@ format_error(write_error) -> format_error({rename,From,To,Error}) -> io_lib:format("failed to rename ~s to ~s: ~s", [From,To,file:format_error(Error)]); +format_error({delete,File,Error}) -> + io_lib:format("failed to delete file ~s: ~s", + [File,file:format_error(Error)]); format_error({delete_temp,File,Error}) -> io_lib:format("failed to delete temporary file ~s: ~s", [File,file:format_error(Error)]); @@ -216,16 +225,16 @@ format_error({module_name,Mod,Filename}) -> [Mod,Filename]). %% The compile state record. --record(compile, {filename="", - dir="", - base="", - ifile="", - ofile="", +-record(compile, {filename="" :: file:filename(), + dir="" :: file:filename(), + base="" :: file:filename(), + ifile="" :: file:filename(), + ofile="" :: file:filename(), module=[], code=[], core_code=[], abstract_code=[], %Abstract code for debugger. - options=[], + options=[] :: [option()], errors=[], warnings=[]}). @@ -235,26 +244,12 @@ internal(Master, Input, Opts) -> end}. internal({forms,Forms}, Opts) -> - Ps = passes(forms, Opts), + {_,Ps} = passes(forms, Opts), internal_comp(Ps, "", "", #compile{code=Forms,options=Opts}); internal({file,File}, Opts) -> - Ps = passes(file, Opts), + {Ext,Ps} = passes(file, Opts), Compile = #compile{options=Opts}, - case member(from_core, Opts) of - true -> internal_comp(Ps, File, ".core", Compile); - false -> - case member(from_beam, Opts) of - true -> - internal_comp(Ps, File, ".beam", Compile); - false -> - case member(from_asm, Opts) orelse member(asm, Opts) of - true -> - internal_comp(Ps, File, ".S", Compile); - false -> - internal_comp(Ps, File, ".erl", Compile) - end - end - end. + internal_comp(Ps, File, Ext, Compile). internal_comp(Passes, File, Suffix, St0) -> Dir = filename:dirname(File), @@ -362,42 +357,52 @@ mpf(Ms) -> [{File,[M || {F,M} <- Ms, F =:= File]} || File <- lists:usort([F || {F,_} <- Ms])]. -%% passes(form|file, [Option]) -> [{Name,PassFun}] -%% Figure out which passes that need to be run. - -passes(forms, Opts) -> - case member(from_core, Opts) of - true -> - select_passes(core_passes(), Opts); - false -> - select_passes(standard_passes(), Opts) +%% passes(forms|file, [Option]) -> {Extension,[{Name,PassFun}]} +%% Figure out the extension of the input file and which passes +%% that need to be run. + +passes(Type, Opts) -> + {Ext,Passes0} = passes_1(Opts), + Passes1 = case Type of + file -> Passes0; + forms -> tl(Passes0) + end, + Passes = select_passes(Passes1, Opts), + + %% If the last pass saves the resulting binary to a file, + %% insert a first pass to remove the file (unless the + %% source file is a BEAM file). + {Ext,case last(Passes) of + {save_binary,_Fun} -> + case Passes of + [{read_beam_file,_}|_] -> + %% The BEAM is both input and output. + %% Don't remove it. + Passes; + _ -> + [?pass(remove_file)|Passes] + end; + _ -> + Passes + end}. + +passes_1([Opt|Opts]) -> + case pass(Opt) of + {_,_}=Res -> Res; + none -> passes_1(Opts) end; -passes(file, Opts) -> - case member(from_beam, Opts) of - true -> - Ps = [?pass(read_beam_file)|binary_passes()], - select_passes(Ps, Opts); - false -> - Ps = case member(from_asm, Opts) orelse member(asm, Opts) of - true -> - [?pass(beam_consult_asm)|asm_passes()]; - false -> - case member(from_core, Opts) of - true -> - [?pass(parse_core)|core_passes()]; - false -> - [?pass(parse_module)|standard_passes()] - end - end, - Fs = select_passes(Ps, Opts), - - %% If the last pass saves the resulting binary to a file, - %% insert a first pass to remove the file. - case last(Fs) of - {save_binary,_Fun} -> [?pass(remove_file)|Fs]; - _Other -> Fs - end - end. +passes_1([]) -> + {".erl",[?pass(parse_module)|standard_passes()]}. + +pass(from_core) -> + {".core",[?pass(parse_core)|core_passes()]}; +pass(from_asm) -> + {".S",[?pass(beam_consult_asm)|asm_passes()]}; +pass(asm) -> + pass(from_asm); +pass(from_beam) -> + {".beam",[?pass(read_beam_file)|binary_passes()]}; +pass(_) -> none. %% select_passes([Command], Opts) -> [{Name,Function}] %% Interpret the lists of commands to return a pure list of passes. @@ -430,6 +435,8 @@ passes(file, Opts) -> %% file will be Ext. (Ext should not contain %% a period.) No more passes will be run. %% +%% done End compilation at this point. +%% %% {done,Ext} End compilation at this point. Produce a listing %% as with {listing,Ext}, unless 'binary' is %% specified, in which case the current @@ -463,6 +470,8 @@ select_passes([{src_listing,Ext}|_], _Opts) -> [{listing,fun (St) -> src_listing(Ext, St) end}]; select_passes([{listing,Ext}|_], _Opts) -> [{listing,fun (St) -> listing(Ext, St) end}]; +select_passes([done|_], _Opts) -> + []; select_passes([{done,Ext}|_], Opts) -> select_passes([{unless,binary,{listing,Ext}}], Opts); select_passes([{iff,Flag,Pass}|Ps], Opts) -> @@ -545,6 +554,13 @@ select_list_passes_1([], _, Acc) -> standard_passes() -> [?pass(transform_module), + + {iff,makedep,[ + ?pass(makedep), + {unless,binary,?pass(makedep_output)} + ]}, + {iff,makedep,done}, + {iff,'dpp',{listing,"pp"}}, ?pass(lint_module), {iff,'P',{src_listing,"P"}}, @@ -819,6 +835,10 @@ foldl_transform(St, [T|Ts]) -> {'EXIT',R} -> Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}], {error,St#compile{errors=St#compile.errors ++ Es}}; + {warning, Forms, Ws} -> + foldl_transform( + St#compile{code=Forms, + warnings=St#compile.warnings ++ Ws}, Ts); Forms -> foldl_transform(St#compile{code=Forms}, Ts) end; @@ -828,7 +848,6 @@ get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts]. core_transforms(St) -> %% The options field holds the complete list of options at this - Ts = get_core_transforms(St#compile.options), foldl_core_transforms(St, Ts). @@ -893,6 +912,184 @@ core_lint_module(St) -> errors=St#compile.errors ++ Es}} end. +makedep(#compile{code=Code,options=Opts}=St) -> + Ifile = St#compile.ifile, + Ofile = St#compile.ofile, + + %% Get the target of the Makefile rule. + Target0 = + case proplists:get_value(makedep_target, Opts) of + undefined -> + %% The target is derived from the output filename: possibly + %% remove the current working directory to obtain a relative + %% path. + shorten_filename(Ofile); + T -> + %% The caller specified one. + T + end, + + %% Quote the target is the called asked for this. + Target1 = case proplists:get_value(makedep_quote_target, Opts) of + true -> + %% For now, only "$" is replaced by "$$". + Fun = fun + ($$) -> "$$"; + (C) -> C + end, + map(Fun, Target0); + _ -> + Target0 + end, + Target = Target1 ++ ":", + + %% List the dependencies (includes) for this target. + {MainRule,PhonyRules} = makedep_add_headers( + Ifile, % The input file name. + Code, % The parsed source. + [], % The list of dependencies already added. + length(Target), % The current line length. + Target, % The target. + "", % Phony targets. + Opts), + + %% Prepare the content of the Makefile. For instance: + %% hello.erl: hello.hrl common.hrl + %% + %% Or if phony targets are enabled: + %% hello.erl: hello.hrl common.hrl + %% + %% hello.hrl: + %% + %% common.hrl: + Makefile = case proplists:get_value(makedep_phony, Opts) of + true -> MainRule ++ PhonyRules; + _ -> MainRule + end, + {ok,St#compile{code=iolist_to_binary([Makefile,"\n"])}}. + +makedep_add_headers(Ifile, [{attribute,_,file,{File,_}}|Rest], + Included, LineLen, MainTarget, Phony, Opts) -> + %% The header "File" exists, add it to the dependencies. + {Included1,LineLen1,MainTarget1,Phony1} = + makedep_add_header(Ifile, Included, LineLen, MainTarget, Phony, File), + makedep_add_headers(Ifile, Rest, Included1, LineLen1, + MainTarget1, Phony1, Opts); +makedep_add_headers(Ifile, [{error,{_,epp,{include,file,File}}}|Rest], + Included, LineLen, MainTarget, Phony, Opts) -> + %% The header "File" doesn't exist, do we add it to the dependencies? + case proplists:get_value(makedep_add_missing, Opts) of + true -> + {Included1,LineLen1,MainTarget1,Phony1} = + makedep_add_header(Ifile, Included, LineLen, MainTarget, + Phony, File), + makedep_add_headers(Ifile, Rest, Included1, LineLen1, + MainTarget1, Phony1, Opts); + _ -> + makedep_add_headers(Ifile, Rest, Included, LineLen, + MainTarget, Phony, Opts) + end; +makedep_add_headers(Ifile, [_|Rest], Included, LineLen, + MainTarget, Phony, Opts) -> + makedep_add_headers(Ifile, Rest, Included, + LineLen, MainTarget, Phony, Opts); +makedep_add_headers(_Ifile, [], _Included, _LineLen, + MainTarget, Phony, _Opts) -> + {MainTarget,Phony}. + +makedep_add_header(Ifile, Included, LineLen, MainTarget, Phony, File) -> + case member(File, Included) of + true -> + %% This file was already listed in the dependencies, skip it. + {Included,LineLen,MainTarget,Phony}; + false -> + Included1 = [File|Included], + + %% Remove "./" in front of the dependency filename. + File1 = case File of + "./" ++ File0 -> File0; + _ -> File + end, + + %% Prepare the phony target name. + Phony1 = case File of + Ifile -> Phony; + _ -> Phony ++ "\n\n" ++ File1 ++ ":" + end, + + %% Add the file to the dependencies. Lines longer than 76 columns + %% are splitted. + if + LineLen + 1 + length(File1) > 76 -> + LineLen1 = 2 + length(File1), + MainTarget1 = MainTarget ++ " \\\n " ++ File1, + {Included1,LineLen1,MainTarget1,Phony1}; + true -> + LineLen1 = LineLen + 1 + length(File1), + MainTarget1 = MainTarget ++ " " ++ File1, + {Included1,LineLen1,MainTarget1,Phony1} + end + end. + +makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) -> + %% Write this Makefile (Code) to the selected output. + %% If no output is specified, the default is to write to a file named after + %% the output file. + Output0 = case proplists:get_value(makedep_output, Opts) of + undefined -> + %% Prepare the default filename. + outfile(filename:basename(Ofile, ".beam"), "Pbeam", Opts); + O -> + O + end, + + %% If the caller specified an io_device(), there's nothing to do. If he + %% specified a filename, we must create it. Furthermore, this created file + %% must be closed before returning. + Ret = case Output0 of + _ when is_list(Output0) -> + case file:delete(Output0) of + Ret2 when Ret2 =:= ok; Ret2 =:= {error,enoent} -> + case file:open(Output0, [write]) of + {ok,IODev} -> + {ok,IODev,true}; + {error,Reason2} -> + {error,open,Reason2} + end; + {error,Reason1} -> + {error,delete,Reason1} + end; + _ -> + {ok,Output0,false} + end, + + case Ret of + {ok,Output1,CloseOutput} -> + try + %% Write the Makefile. + io:fwrite(Output1, "~s", [Code]), + %% Close the file if relevant. + if + CloseOutput -> file:close(Output1); + true -> ok + end, + {ok,St} + catch + exit:_ -> + %% Couldn't write to output Makefile. + Err = {St#compile.ifile,[{none,?MODULE,write_error}]}, + {error,St#compile{errors=St#compile.errors++[Err]}} + end; + {error,open,Reason} -> + %% Couldn't open output Makefile. + Err = {St#compile.ifile,[{none,?MODULE,{open,Reason}}]}, + {error,St#compile{errors=St#compile.errors++[Err]}}; + {error,delete,Reason} -> + %% Couldn't open output Makefile. + Err = {St#compile.ifile,[{none,?MODULE,{delete,Output0,Reason}}]}, + {error,St#compile{errors=St#compile.errors++[Err]}} + end. + %% expand_module(State) -> State' %% Do the common preprocessing of the input forms. @@ -1172,12 +1369,12 @@ write_binary(Name, Bin, St) -> %% report_errors(State) -> ok %% report_warnings(State) -> ok -report_errors(St) -> - case member(report_errors, St#compile.options) of +report_errors(#compile{options=Opts,errors=Errors}) -> + case member(report_errors, Opts) of true -> foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds); ({F,Eds}) -> list_errors(F, Eds) end, - St#compile.errors); + Errors); false -> ok end. diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl index b633f568c9..b513a8965c 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -65,7 +65,8 @@ | {'return_mismatch', fa()} | {'undefined_function', fa()} | {'duplicate_var', cerl:var_name(), fa()} | {'unbound_var', cerl:var_name(), fa()} - | {'undefined_function', fa(), fa()}. + | {'undefined_function', fa(), fa()} + | {'tail_segment_not_at_end', fa()}. -type error() :: {module(), err_desc()}. -type warning() :: {module(), term()}. @@ -116,7 +117,9 @@ format_error({duplicate_var,N,{F,A}}) -> format_error({unbound_var,N,{F,A}}) -> io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]); format_error({undefined_function,{F1,A1},{F2,A2}}) -> - io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]). + io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]); +format_error({tail_segment_not_at_end,{F,A}}) -> + io_lib:format("binary tail segment not at end in ~w/~w", [F,A]). -type ret() :: {'ok', [{module(), [warning(),...]}]} | {'error', [{module(), [error(),...]}], @@ -450,7 +453,8 @@ pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) -> pattern_list([H,T], Def, Ps, St); pattern(#c_tuple{es=Es}, Def, Ps, St) -> pattern_list(Es, Def, Ps, St); -pattern(#c_binary{segments=Ss}, Def, Ps, St) -> +pattern(#c_binary{segments=Ss}, Def, Ps, St0) -> + St = pat_bin_tail_check(Ss, St0), pat_bin(Ss, Def, Ps, St); pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) -> {Vvs,St1} = variable(V, Ps, St0), @@ -482,6 +486,19 @@ pat_segment(#c_bitstr{val=V,size=S,type=T}, Def0, Ps0, St0) -> pat_segment(_, Def, Ps, St) -> {Ps,Def,add_error({not_bs_pattern,St#lint.func}, St)}. +%% pat_bin_tail_check([Elem], State) -> State. +%% There must be at most one tail segment (a size-less segment of +%% type binary) and it must occur at the end. + +pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}], St) -> + %% Size-less field is OK at the end of the list of segments. + St; +pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}|_], St) -> + add_error({tail_segment_not_at_end,St#lint.func}, St); +pat_bin_tail_check([_|Ss], St) -> + pat_bin_tail_check(Ss, St); +pat_bin_tail_check([], St) -> St. + %% pat_bit_expr(SizePat, Type, Defined, State) -> State. %% Check the Size pattern, this is an input! Because of optimizations, %% we must allow any kind of constant and literal here. diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl index 77005a6f9d..31a1f8b0b7 100644 --- a/lib/compiler/src/rec_env.erl +++ b/lib/compiler/src/rec_env.erl @@ -34,8 +34,6 @@ -export_type([environment/0]). --import(erlang, [max/2]). - -ifdef(DEBUG). -export([test/1, test_custom/1, test_custom/2]). -endif. @@ -588,7 +586,7 @@ new_key(N, R, _T, F, Env) -> new_key(generate(N, R1), R1, 0, F, Env). start_range(Env) -> - max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE). + erlang:max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE). %% The previous key might or might not be used to compute the next key %% to be tried. It is currently not used. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 948937c438..f24a46c5a9 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -235,7 +235,7 @@ match_cg(M, Rs, Le, Vdb, Bef, St0) -> I = Le#l.i, {Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb), {B,St1} = new_label(St0), - {Mis,Int1,St2} = match_cg(M, St0#cg.ultimate_failure, + {Mis,Int1,St2} = match_cg(M, St1#cg.ultimate_failure, Int0, St1#cg{break=B}), %% Put return values in registers. Reg = load_vars(Rs, Int1#sr.reg), @@ -1523,7 +1523,9 @@ cg_binary_size_1([], Bits, Acc) -> [{1,_}|_] -> {bs_init_bits,cg_binary_bytes_to_bits(Sizes, [])}; [{8,_}|_] -> - {bs_init2,[E || {8,E} <- Sizes]} + {bs_init2,[E || {8,E} <- Sizes]}; + [] -> + {bs_init_bits,[]} end. cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) -> diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index f6bb45787c..2da24b2908 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -892,25 +892,22 @@ lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) -> lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) -> {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), {Name,St1} = new_fun_name("blc", St0), - {Tname,St2} = new_var_name(St1), - LA = lineno_anno(Line, St2), + LA = lineno_anno(Line, St1), LAnno = #a{anno=LA}, - HeadBinPattern = pattern(P,St2), - #c_binary{segments=Ps} = HeadBinPattern, - {EPs,St3} = emasculate_segments(Ps,St2), - Tail = #c_var{anno=LA,name=Tname}, - TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all}, - unit=#c_literal{val=1}, - type=#c_literal{val=binary}, - flags=#c_literal{val=[big,unsigned]}}, - Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]}, - EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]}, + HeadBinPattern = pattern(P, St1), + #c_binary{segments=Ps0} = HeadBinPattern, + {Ps,Tail,St2} = append_tail_segment(Ps0, St1), + {EPs,St3} = emasculate_segments(Ps, St2), + Pattern = HeadBinPattern#c_binary{segments=Ps}, + EPattern = HeadBinPattern#c_binary{segments=EPs}, {Arg,St4} = new_var(St3), {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat! + Tname = Tail#c_var.name, {Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5), {Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6), {Gc,Gps,St10} = safe(G, St7), %Will be a function argument! Fc = function_clause([Arg], LA, {Name,1}), + {TailSegList,_,St} = append_tail_segment([], St10), Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]}, pats=[Pattern], guard=Guardc, @@ -922,14 +919,14 @@ lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) -> op=#c_var{anno=LA,name={Name,1}}, args=[Tail]}]}, #iclause{anno=LAnno, - pats=[#c_binary{anno=LA, segments=[TailSegment]}],guard=[], + pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[], body=[Mc]}], Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc}, {#iletrec{anno=LAnno,defs=[{{Name,1},Fun}], body=Gps ++ [#iapply{anno=LAnno, op=#c_var{anno=LA,name={Name,1}}, args=[Gc]}]}, - [],St10}; + [],St}; lc_tq(Line, E, [Fil0|Qs0], Mc, St0) -> %% Special case sequences guard tests. LA = lineno_anno(Line, St0), @@ -1037,26 +1034,24 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0), {Name,St1} = new_fun_name("lbc", St0), LA = lineno_anno(Line, St1), - {[Tail,AccVar],St2} = new_vars(LA, 2, St1), + {AccVar,St2} = new_var(LA, St1), LAnno = #a{anno=LA}, HeadBinPattern = pattern(P, St2), - #c_binary{segments=Ps} = HeadBinPattern, - {EPs,St3} = emasculate_segments(Ps, St2), - TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all}, - unit=#c_literal{val=1}, - type=#c_literal{val=binary}, - flags=#c_literal{val=[big,unsigned]}}, - Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]}, - EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]}, - {Arg,St4} = new_var(St3), + #c_binary{segments=Ps0} = HeadBinPattern, + {Ps,Tail,St3} = append_tail_segment(Ps0, St2), + {EPs,St4} = emasculate_segments(Ps, St3), + Pattern = HeadBinPattern#c_binary{segments=Ps}, + EPattern = HeadBinPattern#c_binary{segments=EPs}, + {Arg,St5} = new_var(St4), NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name}, {var,Lg,AccVar#c_var.name}]}, - {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat! - {Bc,Bps,St6} = bc_tq1(Line, E, Qs1, AccVar, St5), - {Nc,Nps,St7} = expr(NewMore, St6), - {Gc,Gps,St8} = safe(G, St7), %Will be a function argument! + {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat! + {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6), + {Nc,Nps,St8} = expr(NewMore, St7), + {Gc,Gps,St9} = safe(G, St8), %Will be a function argument! Fc = function_clause([Arg,AccVar], LA, {Name,2}), Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc], + {TailSegList,_,St} = append_tail_segment([], St9), Cs = [#iclause{anno=LAnno, pats=[Pattern,AccVar], guard=Guardc, @@ -1066,7 +1061,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> guard=[], body=Nps ++ [Nc]}, #iclause{anno=LAnno, - pats=[#c_binary{anno=LA,segments=[TailSegment]},AccVar], + pats=[#c_binary{anno=LA,segments=TailSegList},AccVar], guard=[], body=[AccVar]}], Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc}, @@ -1074,7 +1069,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) -> body=Gps ++ [#iapply{anno=LAnno, op=#c_var{anno=LA,name={Name,2}}, args=[Gc,AccExpr]}]}, - [],St8}; + [],St}; bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) -> %% Special case sequences guard tests. LA = lineno_anno(Line, St0), @@ -1120,6 +1115,29 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> %%Anno = Anno0#a{anno=[compiler_generated|A]}, {set_anno(E, Anno),Pre,St}. +append_tail_segment(Segs, St) -> + app_tail_seg(Segs, St, []). + +app_tail_seg([#c_bitstr{val=Var0,size=#c_literal{val=all}}=Seg0]=L, + St0, Acc) -> + case Var0 of + #c_var{name='_'} -> + {Var,St} = new_var(St0), + Seg = Seg0#c_bitstr{val=Var}, + {reverse(Acc, [Seg]),Var,St}; + #c_var{} -> + {reverse(Acc, L),Var0,St0} + end; +app_tail_seg([H|T], St, Acc) -> + app_tail_seg(T, St, [H|Acc]); +app_tail_seg([], St0, Acc) -> + {Var,St} = new_var(St0), + Tail = #c_bitstr{val=Var,size=#c_literal{val=all}, + unit=#c_literal{val=1}, + type=#c_literal{val=binary}, + flags=#c_literal{val=[unsigned,big]}}, + {reverse(Acc, [Tail]),Var,St}. + emasculate_segments(Segs, St) -> emasculate_segments(Segs, St, []). diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index fbe4d8617e..3b33a08cf7 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -147,6 +147,7 @@ attributes([]) -> []. include_attribute(type) -> false; include_attribute(spec) -> false; include_attribute(opaque) -> false; +include_attribute(export_type) -> false; include_attribute(_) -> true. function({#c_var{name={F,Arity}=FA},Body}, St0) -> diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index a300dd283f..e363a5387a 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-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,10 +20,12 @@ -module(v3_kernel_pp). --include("v3_kernel.hrl"). - -export([format/1]). +%%-define(INCLUDE_ANNOTATIONS, 1). + +-include("v3_kernel.hrl"). + %% These are "internal" structures in sys_kernel which are here for %% debugging purposes. -record(iset, {anno=[],vars,arg,body}). @@ -50,28 +52,33 @@ format(Node) -> format(Node, #ctxt{}). format(Node, Ctxt) -> case canno(Node) of -%% [] -> -%% format_1(Node, Ctxt); -%% [L,{file,_}] when is_integer(L) -> -%% format_1(Node, Ctxt); -%% #k{a=Anno}=K when Anno =/= [] -> -%% format(setelement(2, Node, K#k{a=[]}), Ctxt); -%% List -> -%% format_anno(List, Ctxt, fun (Ctxt1) -> -%% format_1(Node, Ctxt1) -%% end); - _ -> - format_1(Node, Ctxt) + [] -> + format_1(Node, Ctxt); + [L,{file,_}] when is_integer(L) -> + format_1(Node, Ctxt); + #k{a=Anno}=K when Anno =/= [] -> + format(setelement(2, Node, K#k{a=[]}), Ctxt); + List -> + format_anno(List, Ctxt, fun (Ctxt1) -> + format_1(Node, Ctxt1) + end) end. -%% format_anno(Anno, Ctxt0, ObjFun) -> -%% Ctxt1 = ctxt_bump_indent(Ctxt0, 1), -%% ["( ", -%% ObjFun(Ctxt0), -%% nl_indent(Ctxt1), -%% "-| ",io_lib:write(Anno), -%% " )"]. - + +-ifndef(INCLUDE_ANNOTATIONS). +%% Don't include annotations (for readability). +format_anno(_Anno, Ctxt, ObjFun) -> + ObjFun(Ctxt). +-else. +%% Include annotations (for debugging of annotations). +format_anno(Anno, Ctxt0, ObjFun) -> + Ctxt1 = ctxt_bump_indent(Ctxt0, 1), + ["( ", + ObjFun(Ctxt0), + nl_indent(Ctxt1), + "-| ",io_lib:write(Anno), + " )"]. +-endif. %% format_1(Kexpr, Context) -> string(). @@ -107,6 +114,8 @@ format_1(#k_bin_int{size=Sz,unit=U,flags=Fs,val=Val,next=Next}, Ctxt) -> [format_bin_seg_1(S, Ctxt), format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))]; format_1(#k_bin_end{}, _Ctxt) -> "#<>#"; +format_1(#k_literal{val=Term}, _Ctxt) -> + io_lib:format("~p", [Term]); format_1(#k_local{name=N,arity=A}, Ctxt) -> "local " ++ format_fa_pair({N,A}, Ctxt); format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) -> |