hipe.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,594 行 · 第 1/4 页
ERL
1,594 行
%% Reason = term()%%%% @doc Direct compiler interface, for advanced use. This just compiles%% the named function or module, reading BEAM code from the%% corresponding "Module<code>.beam</code>" file in the system path.%% Returns <code>{ok, Binary}</code> if successful, or <code>{error,%% Reason}</code> otherwise. By default, it does <em>not</em> load the%% binary to memory (the <code>load</code> option can be used to%% activate automatic loading). <code>File</code> can be either a file%% name or a binary containing the BEAM code for the module.%%%% @see c/2%% @see compile/1%% @see compile/3%% @see file/2%% @see load/2compile(Name, Options) -> compile(Name, beam_file(Name), Options).beam_file({M,F,A}) when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> beam_file(M);beam_file(Module) when is_atom(Module) -> case code:which(Module) of non_existing -> ?error_msg("Cannot find ~w.beam file.",[Module]), ?EXIT({cant_find_beam_file,Module}); File -> File end.%% @spec compile(Name, File, options()) -> {ok, Binary} | {error, Reason}%% Name = mod() | mfa()%% File = filename() | binary()%% Binary = binary()%% Reason = term()%% %% @doc Like <code>compile/2</code>, but reads BEAM code from the%% specified <code>File</code>.%%%% @see compile/2compile(Name, File, Opts0) -> Opts1 = expand_kt2(Opts0), Opts = case Name of {_Mod, _Fun, _Arity} -> [no_concurrent_comp|Opts1]; _ -> Opts1 end, case proplists:get_value(core, Opts) of true when is_binary(File) -> ?error_msg("Cannot get Core Erlang code from BEAM binary.",[]), ?EXIT({cant_compile_core_from_binary}); true -> case filename:find_src(filename:rootname(File, ".beam")) of {error, _} -> ?error_msg("Cannot find source code for ~p.",[File]), ?EXIT({cant_find_source_code}); {Source, CompOpts} -> CoreOpts = [X || X = {core_transform, _} <- Opts], %%io:format("Using: ~w\n", [CoreOpts]), case compile:file(Source, CoreOpts ++ [to_core, binary|CompOpts]) of {ok, _, Core} -> compile_core(Name, Core, File, Opts); Error -> ?error_msg("Error compiling ~p:\n~p.",[File, Error]), ?EXIT({cant_compile_source_code}) end end; {src_file, Source} -> CoreOpts1 = [X || X = {core_transform, _} <-Opts], CoreOpts2 = [report_errors, to_core, binary, {i,"../include"}|CoreOpts1], %% io:format("Using: ~w\n", [CoreOpts2]), case compile:file(Source, CoreOpts2) of {ok, _, Core} -> compile_core(Name, Core, File, Opts); Error -> ?error_msg("Error compiling ~p:\n~p\n",[Source, Error]), ?EXIT({cant_compile_source_code, Error}) end; Other when Other =:= false; Other =:= undefined -> NewOpts = case proplists:get_value(use_callgraph, Opts) of No when No =:= false; No =:= undefined -> Opts; _ -> case Name of {_M,_F,_A} -> %% There is no point in using the callgraph when %% analyzing just one function. or to use concurrent comp [no_use_callgraph,no_concurrent_comp|Opts]; _ -> Opts end end, DisasmFun = fun (_) -> disasm(File) end, IcodeFun = fun (Code, Opts_) -> get_beam_icode(Name, Code, File, Opts_) end, run_compiler(Name, DisasmFun, IcodeFun, NewOpts) end.compile_core(Name, Core0, File, Opts) -> Core = cerl:from_records(Core0), Core1 = case (erlang:system_info(heap_type) =:= hybrid) andalso proplists:get_bool(hybrid, Opts) of true -> cerl_hybrid_transform:transform(Core, Opts); false -> Core end, compile(Name, Core1, File, Opts).%% @spec compile(Name, Core, File, options()) ->%% {ok, Binary} | {error, Reason}%% Name = mod()%% Core = coreErlang() | []%% File = filename() | binary()%% Binary = binary()%% Reason = term()%% %% @doc Like <code>compile/3</code>, but unless <code>Core</code> is%% <code>[]</code>, low-level code is generated from the given Core%% Erlang code instead of from the BEAM code.%%%% <p>Note that only whole modules can be compiled with this%% function.</p>%%%% @see compile/3compile(Name, [], File, Opts) -> compile(Name, File, Opts);compile({M,F,A}, _Core, _File, _Opts) -> ?WARNING_MSG("Cannot compile single functions from source code. (~p}.\n",[{M,F,A}]), {ok, <<>>}; % dummy% compile(M, Core, File, Opts);compile(Name, Core, File, Opts) when is_atom(Name) -> DisasmFun = fun (_) -> {false, []} end, IcodeFun = fun (_, Opts) -> get_core_icode(Name, Core, File, Opts) end, run_compiler(Name, DisasmFun, IcodeFun, Opts).%% @spec file(File) -> {ok, Name, Binary} | {error, Reason}%% File = filename() | binary()%% Name = mod() | mfa()%% Binary = binary()%% Reason = term()%% %% @equiv file(File, [])file(File) -> file(File, []).%% @spec file(File, options()) -> {ok, Name, Binary} | {error, Reason}%% File = filename() | binary()%% Name = mod() | mfa()%% Binary = binary()%% Reason = term()%% %% @doc Like <code>compile/2</code>, but takes the module name from the%% specified <code>File</code>. Returns both the name and the final%% binary if successful.%%%% @see file/1%% @see compile/2file(File, Options) when is_atom(File) -> case beam_lib:info(File) of L when is_list(L) -> {value,{module,Mod}} = lists:keysearch(module,1,L), case compile(Mod, File, Options) of {ok, Bin} -> {ok, Mod, Bin}; Other -> Other end; Error -> Error end.%%-----------------------------------------------------------------------%% The rest are internal functions:%%-----------------------------------------------------------------------%% @doc%% Get BEAM code from `.beam' files or direct from binaries.%% File is either a file name or a binary containing the BEAM code.disasm(File) -> case beam_disasm:file(File) of {beam_file,DisasmBeam} -> {value,{code,BeamCode}} = lists:keysearch(code,1,DisasmBeam), {value,{exports,BeamExports}} = lists:keysearch(exports,1,DisasmBeam), {value,{comp_info,CompInfo}} = lists:keysearch(comp_info,1,DisasmBeam), {value,{options,CompOpts}} = lists:keysearch(options,1,CompInfo), HCompOpts = case lists:keysearch(hipe,1,CompOpts) of {value,{hipe,L}} when is_list(L) -> L; {value,{hipe,X}} -> [X]; _ -> [] end, Exports = fix_beam_exports(BeamExports), {{BeamCode, Exports}, HCompOpts}; Error -> io:format("~s\n", [beam_lib:format_error(Error)]), ?EXIT(no_beam_code) end.fix_beam_exports(BeamExports) -> fix_beam_exports(BeamExports, []).fix_beam_exports([{F,A,_}|BeamExports], Exports) -> fix_beam_exports(BeamExports, [{F,A} | Exports]);fix_beam_exports([], Exports) -> Exports.get_beam_icode({M,_F,_A} = MFA, {BeamCode, Exports}, _File, Options) -> ?option_time({ok,Icode} = (catch {ok, hipe_beam_to_icode:mfa(BeamCode, MFA, Options)}), "BEAM-to-Icode", Options), {{M, Exports, Icode}, false};get_beam_icode(Mod, {BeamCode, Exports}, File, Options) -> ?option_time({ok, Icode} = (catch {ok, hipe_beam_to_icode:module(BeamCode, Options)}), "BEAM-to-Icode", Options), BeamBin = get_beam_code(File), {{Mod, Exports, Icode}, BeamBin}.get_core_icode(Mod, Core, File, Options) -> ?option_time({ok, Icode} = (catch {ok, cerl_to_icode:module(Core, Options)}), "BEAM-to-Icode", Options), NeedBeamCode = not proplists:get_bool(load, Options), BeamBin = case NeedBeamCode of true -> []; false -> get_beam_code(File) end, Exports = [cerl:var_name(V) || V <- cerl:module_exports(Core)], {{Mod, Exports, Icode}, BeamBin}.get_beam_code(Bin) when is_binary(Bin) -> Bin;get_beam_code(FileName) -> case erl_prim_loader:get_file(FileName) of {ok,Bin,_} -> Bin; error -> ?EXIT(no_beam_file) end.%% ---------------------------------------------------------------------%% All compilations go through this function. Note that it receives only%% "basic" options. Name is just used for verbosity. The DisasmFun and%% IcodeFun only collect the Icode; most of the real work is done in the%% 'finalize' function.run_compiler(Name, DisasmFun, IcodeFun, Opts0) -> Opts = expand_basic_options(Opts0 ++ ?COMPILE_DEFAULTS), ?when_option(verbose, Opts, ?debug_msg("Compiling: ~p\n",[Name])), ?option_start_time("Compile", Opts), Res = run_compiler_1(DisasmFun, IcodeFun, Opts), ?option_stop_time("Compile", Opts), Res.run_compiler_1(DisasmFun, IcodeFun, Options) -> Parent = self(), {trap_exit,TrapExit} = process_info(Parent, trap_exit), %% Spawn a compilation process CompProc. In case this process gets %% killed, the trap_exit flag is restored to that of the Parent process. process_flag(trap_exit, true), CompProc = spawn_link(fun () -> %% Compiler process set_architecture(Options), pre_init(Options), %% The full option expansion is not done %% until the DisasmFun returns. {Code, CompOpts} = DisasmFun(Options), Opts = expand_options(Options ++ CompOpts), check_options(Opts), ?when_option(verbose, Options, ?debug_msg("Options: ~p.\n",[Opts])), init(Opts), {Icode, WholeModule} = IcodeFun(Code, Opts), CompRes = compile_finish(Icode, WholeModule, Opts), compiler_return(CompRes, Parent) end), Timeout = case proplists:get_value(timeout, Options) of N when is_integer(N), N >= 0 -> N; undefined -> ?DEFAULT_TIMEOUT; infinity -> infinity; Other -> ?WARNING_MSG("Bad timeout value: ~P\n" "Using default timeout limit.\n", [Other, 5]), ?DEFAULT_TIMEOUT end, receive {'EXIT', CompProc, normal} -> ok; {'EXIT', CompProc, Reason} -> exit(Reason) after Timeout -> %% Kill the compilation process exit(CompProc, kill), receive {'EXIT', CompProc, _} -> ok end, flush(), ?error_msg("ERROR: Compilation timed out.\n",[]), exit(timed_out) end, Result = receive {CompProc, Res} -> Res end, process_flag(trap_exit, TrapExit), Result.flush() -> receive _ -> flush() after 0 -> ok end.compiler_return(Res, Client) -> Client ! {self(), Res}.compile_finish({Mod, Exports, Icode}, WholeModule, Options) -> Res = finalize(Icode, Mod, Exports, WholeModule, Options), post(Res, Icode, Options).%% ---------------------------------------------------------------------%% finalize/4 compiles, assembles, and optionally loads a list of `{MFA,%% Icode}' pairs, and returns `{ok, Binary}' or `{error, Reason}'.%% TODO: make the Exports info accessible to the compilation passes.finalize(OrigList, Mod, Exports, WholeModule, Opts0) -> Opts = [{exports, Exports} |Opts0], List = icode_multret(OrigList, Mod, Opts, Exports), {T1Compile,_} = erlang:statistics(runtime), CompiledCode = case proplists:get_value(use_callgraph, Opts) of true -> %% Compiling the functions bottom-up by using a call graph CallGraph = hipe_icode_callgraph:construct(List), OrdList = hipe_icode_callgraph:to_list(CallGraph), finalize_fun(OrdList, Opts); _ -> %% Compiling the functions bottom-up by reversing the list OrdList = lists:reverse(List), finalize_fun(OrdList, Opts) end, {T2Compile,_} = erlang:statistics(runtime), ?when_option(verbose, Opts, ?debug_msg("Compiled ~p in ~.2f s\n", [Mod,(T2Compile-T1Compile)/1000])), case (proplists:get_bool(to_rtl, Opts) orelse proplists:get_bool(dialyzer, Opts)) of true -> {ok, CompiledCode}; false -> Closures = [MFA || {MFA, Icode} <- List, hipe_icode:icode_is_closure(Icode)], {T1,_} = erlang:statistics(runtime), ?when_option(verbose, Opts, ?debug_msg("Assembling ~w",[Mod])), case catch assemble(CompiledCode, Closures, Exports, Opts) of {'EXIT',Error} -> {error,Error}; Bin -> {T2,_} = erlang:statistics(runtime), ?when_option(verbose, Opts, ?debug_untagged_msg(" in ~.2f s\n", [(T2-T1)/1000])), maybe_load(Mod, Bin, WholeModule, Opts), TargetArch = get(hipe_target_arch), {ok, {TargetArch,Bin}} end end.finalize_fun(MfaIcodeList, Opts) -> case proplists:get_value(concurrent_comp, Opts) of FalseVal when (FalseVal =:= undefined) or (FalseVal =:= false) -> [finalize_fun_sequential({MFA, Icode}, Opts) || {MFA, Icode} <- MfaIcodeList]; TrueVal when (TrueVal =:= true) or (TrueVal =:= debug) -> finalize_fun_concurrent(MfaIcodeList, Opts) end.finalize_fun_concurrent(MfaIcodeList, Opts) -> Self = self(), case MfaIcodeList of [{{M,_,_},_}|_] -> Exports = proplists:get_value(exports,Opts), CallGraph = hipe_icode_callgraph:construct_callgraph(MfaIcodeList), Closures = [{MFA,true} || {MFA, Icode} <- MfaIcodeList, hipe_icode:icode_is_closure(Icode)], Exported = [{{M,F,A},false} || {F,A} <- Exports, not ((F =:= module_info) and ((A =:= 1) or (A =:= 0)))], NonEscaping = [{M,F,A} || {{M,F,A}, Icode} <- MfaIcodeList, not lists:member({F,A},Exports), not hipe_icode:icode_is_closure(Icode)], Escaping = Closures++Exported, TypeServerFun =
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?