mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 22:35:41 +00:00
Estimated hours taken: 25 Branches: main Provide the direct reuse analysis part of the structure reuse analysis (which itself is part of the CTGC system). compiler/ctgc.datastruct.m: compiler/ctgc.util.m: Additional predicates. compiler/ctgc.m: Add structure reuse module. compiler/handle_options.m: compiler/options.m: Add new options "structure_reuse_analysis" and related ones. compiler/handle_options.m: compiler/hlds_out.m: Add dump option "R" to dump structure reuse related information in the hlds_dump files. compiler/hlds_goal.m: Types to record structure reuse information at the level of each goal. Additional "case_get_goal" function to extract the goal from an case. compiler/mercury_compile.m: Add structure reuse analysis as a new compiler stage. compiler/structure_reuse.analysis.m: The top level analysis predicates. compiler/structure_reuse.direct.m: compiler/structure_reuse.direct.choose_reuse.m: compiler/structure_reuse.direct.detect_garbage.m: Direct reuse analysis is split into 2 steps: determining when and how data structures become garbage, and then choosing how these dead data structures might best be reused. compiler/structure_reuse.domain.m: The abstract domain for keeping track of reuse conditions, the main domain in the structure reuse analysis. compiler/structure_reuse.lbu.m: compiler/structure_reuse.lfu.m: To determine whether data structures become dead or not, one needs to know which variables in a goal are needed with respect to forward execution (lfu = local forward use), and backward execution, i.e. backtracking (lbu = local backward use). These two modules provide the necessary functionality to pre-annotate the goals with lfu and lbu information. compiler/structure_sharing.analysis.m: compiler/structure_sharing.domain.m: Remove the structure sharing table from the interface of the analysis predicate in structure_sharing.analysis.m; Move predicates to structure_sharing.domain.m so that they become more easily accessible for the structure_reuse modules. compiler/prog_data.m: New types "dead_var", "live_var" and alike.
4641 lines
177 KiB
Mathematica
4641 lines
177 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2006 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% File: mercury_compile.m.
|
|
% Main authors: fjh, zs.
|
|
|
|
% This is the top-level of the Mercury compiler.
|
|
|
|
% This module invokes the different passes of the compiler as appropriate.
|
|
% The constraints on pass ordering are documented in
|
|
% compiler/notes/compiler_design.html.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module top_level.mercury_compile.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
% This is the main entry point for the Mercury compiler.
|
|
% It is called from top_level.main.
|
|
%
|
|
:- pred real_main(io::di, io::uo) is det.
|
|
|
|
% main(Args).
|
|
%
|
|
:- pred main(list(string)::in, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
%
|
|
% the main compiler passes (mostly in order of execution)
|
|
%
|
|
|
|
% semantic analysis
|
|
:- import_module parse_tree.prog_foreign.
|
|
:- import_module parse_tree.prog_io.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.modules.
|
|
:- import_module parse_tree.source_file_map.
|
|
:- import_module parse_tree.module_qual.
|
|
:- import_module parse_tree.equiv_type.
|
|
:- import_module hlds.make_hlds.
|
|
:- import_module check_hlds.typecheck.
|
|
:- import_module check_hlds.purity.
|
|
:- import_module check_hlds.polymorphism.
|
|
:- import_module check_hlds.modes.
|
|
:- import_module check_hlds.mode_constraints.
|
|
:- import_module check_hlds.switch_detection.
|
|
:- import_module check_hlds.cse_detection.
|
|
:- import_module check_hlds.det_analysis.
|
|
:- import_module check_hlds.unique_modes.
|
|
:- import_module check_hlds.stratify.
|
|
:- import_module check_hlds.simplify.
|
|
|
|
% high-level HLDS transformations
|
|
:- import_module check_hlds.check_typeclass.
|
|
:- import_module transform_hlds.intermod.
|
|
:- import_module transform_hlds.trans_opt.
|
|
:- import_module transform_hlds.equiv_type_hlds.
|
|
:- import_module transform_hlds.table_gen.
|
|
:- import_module transform_hlds.complexity.
|
|
:- import_module transform_hlds.lambda.
|
|
:- import_module transform_hlds.closure_analysis.
|
|
:- import_module transform_hlds.termination.
|
|
:- import_module transform_hlds.term_constr_main.
|
|
:- import_module transform_hlds.exception_analysis.
|
|
:- import_module transform_hlds.trailing_analysis.
|
|
:- import_module transform_hlds.higher_order.
|
|
:- import_module transform_hlds.accumulator.
|
|
:- import_module transform_hlds.tupling.
|
|
:- import_module transform_hlds.untupling.
|
|
:- import_module transform_hlds.inlining.
|
|
:- import_module transform_hlds.loop_inv.
|
|
:- import_module transform_hlds.deforest.
|
|
:- import_module transform_hlds.dead_proc_elim.
|
|
:- import_module transform_hlds.delay_construct.
|
|
:- import_module transform_hlds.unused_args.
|
|
:- import_module transform_hlds.unneeded_code.
|
|
:- import_module transform_hlds.lco.
|
|
:- import_module transform_hlds.ctgc.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.analysis.
|
|
:- import_module transform_hlds.ctgc.structure_sharing.
|
|
:- import_module transform_hlds.ctgc.structure_sharing.analysis.
|
|
:- import_module transform_hlds.size_prof.
|
|
:- import_module ll_backend.deep_profiling.
|
|
|
|
% the LLDS back-end
|
|
:- import_module ll_backend.continuation_info.
|
|
:- import_module ll_backend.dupproc.
|
|
:- import_module ll_backend.follow_code.
|
|
:- import_module ll_backend.global_data.
|
|
:- import_module ll_backend.liveness.
|
|
:- import_module ll_backend.live_vars.
|
|
:- import_module ll_backend.llds_out.
|
|
:- import_module ll_backend.optimize.
|
|
:- import_module ll_backend.proc_gen.
|
|
:- import_module ll_backend.saved_vars.
|
|
:- import_module ll_backend.stack_alloc.
|
|
:- import_module ll_backend.stack_layout.
|
|
:- import_module ll_backend.stack_opt.
|
|
:- import_module ll_backend.store_alloc.
|
|
:- import_module ll_backend.transform_llds.
|
|
|
|
% the bytecode back-end
|
|
:- import_module bytecode_backend.bytecode_gen.
|
|
:- import_module bytecode_backend.bytecode.
|
|
|
|
% the MLDS back-end
|
|
:- import_module ml_backend.add_trail_ops. % HLDS -> HLDS
|
|
:- import_module ml_backend.add_heap_ops. % HLDS -> HLDS
|
|
:- import_module ml_backend.mark_static_terms. % HLDS -> HLDS
|
|
:- import_module ml_backend.mlds. % MLDS data structure
|
|
:- import_module ml_backend.ml_code_gen.
|
|
:- import_module ml_backend.rtti_to_mlds. % HLDS/RTTI -> MLDS
|
|
:- import_module ml_backend.ml_elim_nested. % MLDS -> MLDS
|
|
:- import_module ml_backend.ml_tailcall. % MLDS -> MLDS
|
|
:- import_module ml_backend.ml_optimize. % MLDS -> MLDS
|
|
:- import_module ml_backend.mlds_to_c. % MLDS -> C
|
|
:- import_module ml_backend.mlds_to_java. % MLDS -> Java
|
|
:- import_module ml_backend.mlds_to_ilasm. % MLDS -> IL assembler
|
|
:- import_module ml_backend.maybe_mlds_to_gcc. % MLDS -> GCC back-end
|
|
:- import_module ml_backend.ml_util. % MLDS utility predicates
|
|
|
|
% miscellaneous compiler modules
|
|
:- import_module check_hlds.goal_path.
|
|
:- import_module hlds.arg_info.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module ll_backend.layout.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module make.
|
|
:- import_module make.options_file.
|
|
:- import_module make.util.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.mercury_to_mercury.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_item.
|
|
:- import_module parse_tree.prog_util.
|
|
:- import_module recompilation.
|
|
:- import_module recompilation.check.
|
|
:- import_module recompilation.usage.
|
|
:- import_module transform_hlds.dependency_graph.
|
|
|
|
% inter-module analysis framework
|
|
:- import_module analysis.
|
|
:- import_module transform_hlds.mmc_analysis.
|
|
|
|
% compiler library modules
|
|
:- import_module backend_libs.base_typeclass_info.
|
|
:- import_module backend_libs.compile_target_code.
|
|
:- import_module backend_libs.export.
|
|
:- import_module backend_libs.foreign.
|
|
:- import_module backend_libs.name_mangle.
|
|
:- import_module backend_libs.proc_label.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module backend_libs.type_class_info.
|
|
:- import_module backend_libs.type_ctor_info.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.handle_options.
|
|
:- import_module libs.options.
|
|
:- import_module libs.timestamp.
|
|
:- import_module libs.trace_params.
|
|
|
|
% library modules
|
|
:- import_module assoc_list.
|
|
:- import_module benchmarking.
|
|
:- import_module bool.
|
|
:- import_module dir.
|
|
:- import_module gc.
|
|
:- import_module getopt_io.
|
|
:- import_module int.
|
|
:- import_module library.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module pprint.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
:- import_module unit.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
real_main(!IO) :-
|
|
gc_init(!IO),
|
|
|
|
% All messages go to stderr
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.set_output_stream(StdErr, _, !IO),
|
|
io.command_line_arguments(Args0, !IO),
|
|
|
|
% read_args_file and globals.io_printing_usage may attempt
|
|
% to look up options, so we need to initialize the globals.
|
|
handle_options([], _, _, _, _, !IO),
|
|
|
|
( Args0 = ["--arg-file", ArgFile] ->
|
|
%
|
|
% All the configuration and options file options
|
|
% are passed in the given file, which is created
|
|
% by the parent `mmc --make' process.
|
|
%
|
|
|
|
options_file.read_args_file(ArgFile, MaybeArgs1, !IO),
|
|
(
|
|
MaybeArgs1 = yes(Args1),
|
|
process_options(Args1, OptionArgs, NonOptionArgs, _, !IO),
|
|
MaybeMCFlags = yes([])
|
|
;
|
|
MaybeArgs1 = no,
|
|
OptionArgs = [],
|
|
NonOptionArgs = [],
|
|
MaybeMCFlags = yes([])
|
|
),
|
|
Variables = options_variables_init,
|
|
Link = no
|
|
;
|
|
%
|
|
% Find out which options files to read.
|
|
%
|
|
handle_options(Args0, Errors0, OptionArgs, NonOptionArgs, Link, !IO),
|
|
(
|
|
Errors0 = [_ | _],
|
|
usage_errors(Errors0, !IO),
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = no
|
|
;
|
|
Errors0 = [],
|
|
read_options_files(options_variables_init, MaybeVariables0, !IO),
|
|
(
|
|
MaybeVariables0 = yes(Variables0),
|
|
lookup_mmc_options(Variables0, MaybeMCFlags0, !IO),
|
|
(
|
|
MaybeMCFlags0 = yes(MCFlags0),
|
|
real_main_2(MCFlags0, MaybeMCFlags, Args0,
|
|
Variables0, Variables, !IO)
|
|
|
|
;
|
|
MaybeMCFlags0 = no,
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = no
|
|
)
|
|
;
|
|
MaybeVariables0 = no,
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = no
|
|
)
|
|
)
|
|
),
|
|
(
|
|
MaybeMCFlags = yes(MCFlags),
|
|
handle_options(MCFlags ++ OptionArgs, Errors, _, _, _, !IO),
|
|
|
|
%
|
|
% When computing the option arguments to pass
|
|
% to `--make', only include the command-line
|
|
% arguments, not the contents of DEFAULT_MCFLAGS.
|
|
%
|
|
main_2(Errors, Variables, OptionArgs, NonOptionArgs, Link, !IO)
|
|
;
|
|
MaybeMCFlags = no,
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
:- pred real_main_2(list(string)::in, maybe(list(string))::out,
|
|
list(string)::in, options_variables::in, options_variables::out,
|
|
io::di, io::uo) is det.
|
|
|
|
real_main_2(MCFlags0, MaybeMCFlags, Args0, Variables0, Variables, !IO) :-
|
|
%
|
|
% Process the options again to find out
|
|
% which configuration file to read.
|
|
%
|
|
handle_options(MCFlags0 ++ Args0, Errors, _, _, _, !IO),
|
|
(
|
|
Errors = [_ | _],
|
|
usage_errors(Errors, !IO),
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = no
|
|
;
|
|
Errors = [],
|
|
globals.io_lookup_maybe_string_option(config_file,
|
|
MaybeConfigFile, !IO),
|
|
(
|
|
MaybeConfigFile = yes(ConfigFile),
|
|
read_options_file(ConfigFile, Variables0, MaybeVariables, !IO),
|
|
(
|
|
MaybeVariables = yes(Variables),
|
|
lookup_mmc_options(Variables, MaybeMCFlags, !IO)
|
|
;
|
|
MaybeVariables = no,
|
|
MaybeMCFlags = no,
|
|
Variables = options_variables_init
|
|
)
|
|
;
|
|
MaybeConfigFile = no,
|
|
Variables = options_variables_init,
|
|
lookup_mmc_options(Variables, MaybeMCFlags, !IO)
|
|
)
|
|
).
|
|
|
|
main(Args, !IO) :-
|
|
main_2([], options_variables_init, [], Args, no, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred gc_init(io::di, io::uo) is det.
|
|
|
|
% This version is only used if there is no matching foreign_proc version.
|
|
gc_init(!IO).
|
|
|
|
:- pragma foreign_proc("C",
|
|
gc_init(_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, tabled_for_io],
|
|
"
|
|
#ifdef MR_BOEHM_GC
|
|
/*
|
|
** Explicitly force the initial heap size to be at least 4 Mb.
|
|
**
|
|
** This works around a bug in the Boehm collector (for versions up
|
|
** to at least 6.2) where the collector would sometimes abort with
|
|
** the message `unexpected mark stack overflow' (e.g. in grade hlc.gc
|
|
** on dec-alpha-osf3.2).
|
|
**
|
|
** Doing this should also improve performance slightly by avoiding
|
|
** frequent garbage collection during start-up.
|
|
*/
|
|
GC_expand_hp(4 * 1024 * 1024);
|
|
#endif
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred main_2(list(string)::in, options_variables::in, list(string)::in,
|
|
list(string)::in, bool::in, io::di, io::uo) is det.
|
|
|
|
main_2(Errors @ [_ | _], _, _, _, _, !IO) :-
|
|
usage_errors(Errors, !IO).
|
|
main_2([], OptionVariables, OptionArgs, Args, Link, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, version, Version),
|
|
globals.lookup_bool_option(Globals, help, Help),
|
|
globals.lookup_bool_option(Globals, generate_source_file_mapping,
|
|
GenerateMapping),
|
|
globals.lookup_bool_option(Globals, output_grade_string,
|
|
OutputGrade),
|
|
globals.lookup_bool_option(Globals, output_link_command,
|
|
OutputLinkCommand),
|
|
globals.lookup_bool_option(Globals, output_shared_lib_link_command,
|
|
OutputShLibLinkCommand),
|
|
globals.lookup_bool_option(Globals, filenames_from_stdin,
|
|
FileNamesFromStdin),
|
|
globals.lookup_bool_option(Globals, make, Make),
|
|
( Version = yes ->
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.set_output_stream(Stdout, OldOutputStream, !IO),
|
|
display_compiler_version(!IO),
|
|
io.set_output_stream(OldOutputStream, _, !IO)
|
|
; Help = yes ->
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.set_output_stream(Stdout, OldOutputStream, !IO),
|
|
long_usage(!IO),
|
|
io.set_output_stream(OldOutputStream, _, !IO)
|
|
; OutputGrade = yes ->
|
|
% When Mmake asks for the grade, it really wants
|
|
% the directory component to use. This is consistent
|
|
% with scripts/canonical_grade.
|
|
grade_directory_component(Globals, Grade),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, Grade, !IO),
|
|
io.write_string(Stdout, "\n", !IO)
|
|
; OutputLinkCommand = yes ->
|
|
globals.lookup_string_option(Globals, link_executable_command,
|
|
LinkCommand),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, LinkCommand, !IO),
|
|
io.write_string(Stdout, "\n", !IO)
|
|
; OutputShLibLinkCommand = yes ->
|
|
globals.lookup_string_option(Globals, link_shared_lib_command,
|
|
LinkCommand),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, LinkCommand, !IO),
|
|
io.write_string(Stdout, "\n", !IO)
|
|
; GenerateMapping = yes ->
|
|
source_file_map.write_source_file_map(Args, !IO)
|
|
; Make = yes ->
|
|
make_process_args(OptionVariables, OptionArgs, Args, !IO)
|
|
; Args = [], FileNamesFromStdin = no ->
|
|
usage(!IO)
|
|
;
|
|
process_all_args(OptionVariables, OptionArgs,
|
|
Args, ModulesToLink, FactTableObjFiles, !IO),
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( ExitStatus = 0 ->
|
|
(
|
|
Link = yes,
|
|
ModulesToLink = [FirstModule | _]
|
|
->
|
|
file_name_to_module_name(FirstModule,
|
|
MainModuleName),
|
|
globals.get_target(Globals, Target),
|
|
( Target = java ->
|
|
% For Java, at the "link" step we just
|
|
% generate a shell script; the actual
|
|
% linking will be done at runtime by
|
|
% the Java interpreter.
|
|
create_java_shell_script(MainModuleName, Succeeded, !IO)
|
|
;
|
|
compile_with_module_options(MainModuleName,
|
|
OptionVariables, OptionArgs,
|
|
link_module_list(ModulesToLink, FactTableObjFiles),
|
|
Succeeded, !IO)
|
|
),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
% If we found some errors, but the user didn't enable
|
|
% the `-E' (`--verbose-errors') option, give them a
|
|
% hint about it. Of course, we should only output the
|
|
% hint when we have further information to give the user.
|
|
|
|
globals.lookup_bool_option(Globals, verbose_errors,
|
|
VerboseErrors),
|
|
globals.get_extra_error_info(Globals, ExtraErrorInfo),
|
|
(
|
|
VerboseErrors = no,
|
|
(
|
|
ExtraErrorInfo = yes,
|
|
io.write_string("For more information, " ++
|
|
"recompile with `-E'.\n", !IO)
|
|
;
|
|
ExtraErrorInfo = no
|
|
)
|
|
;
|
|
VerboseErrors = yes
|
|
)
|
|
),
|
|
globals.lookup_bool_option(Globals, statistics, Statistics),
|
|
(
|
|
Statistics = yes,
|
|
io.report_stats("full_memory_stats", !IO)
|
|
;
|
|
Statistics = no
|
|
)
|
|
).
|
|
|
|
:- pred process_all_args(options_variables::in, list(string)::in,
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_all_args(OptionVariables, OptionArgs, Args, ModulesToLink,
|
|
FactTableObjFiles, !IO) :-
|
|
% Because of limitations in the GCC back-end,
|
|
% we can only call the GCC back-end once (per process),
|
|
% to generate a single assembler file, rather than
|
|
% calling it multiple times to generate individual
|
|
% assembler files for each module.
|
|
% So if we're generating code using the GCC back-end,
|
|
% we need to call run_gcc_backend here at the top level.
|
|
globals.io_get_globals(Globals, !IO),
|
|
( compiling_to_asm(Globals) ->
|
|
( Args = [FirstArg | OtherArgs] ->
|
|
globals.lookup_bool_option(Globals, smart_recompilation, Smart),
|
|
(
|
|
Smart = yes,
|
|
(
|
|
OtherArgs = [],
|
|
% With smart recompilation we need to delay
|
|
% starting the gcc backend to avoid overwriting
|
|
% the output assembler file even if
|
|
% recompilation is found to be unnecessary.
|
|
process_args(OptionVariables, OptionArgs, Args,
|
|
ModulesToLink, FactTableObjFiles, !IO)
|
|
;
|
|
OtherArgs = [_ | _],
|
|
Msg = "Sorry, not implemented: " ++
|
|
"`--target asm' with `--smart-recompilation' " ++
|
|
"with more than one module to compile.",
|
|
write_error_pieces_plain([words(Msg)], !IO),
|
|
io.set_exit_status(1, !IO),
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
)
|
|
;
|
|
Smart = no,
|
|
compile_using_gcc_backend(OptionVariables, OptionArgs,
|
|
string_to_file_or_module(FirstArg),
|
|
process_args_no_fact_table(OptionVariables, OptionArgs,
|
|
Args),
|
|
ModulesToLink, !IO),
|
|
FactTableObjFiles = []
|
|
)
|
|
;
|
|
Msg = "Sorry, not implemented: `--target asm' " ++
|
|
"with `--filenames-from-stdin",
|
|
write_error_pieces_plain([words(Msg)], !IO),
|
|
io.set_exit_status(1, !IO),
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
)
|
|
;
|
|
% If we're NOT using the GCC back-end,
|
|
% then we can just call process_args directly,
|
|
% rather than via GCC.
|
|
process_args(OptionVariables, OptionArgs, Args, ModulesToLink,
|
|
FactTableObjFiles, !IO)
|
|
).
|
|
|
|
:- pred compiling_to_asm(globals::in) is semidet.
|
|
|
|
compiling_to_asm(Globals) :-
|
|
globals.get_target(Globals, asm),
|
|
% even if --target asm is specified,
|
|
% it can be overridden by other options:
|
|
OptionList = [convert_to_mercury, generate_dependencies,
|
|
generate_dependency_file, make_interface,
|
|
make_short_interface, make_private_interface,
|
|
make_optimization_interface, make_transitive_opt_interface,
|
|
make_analysis_registry,
|
|
typecheck_only, errorcheck_only],
|
|
BoolList = list.map((func(Opt) = Bool :-
|
|
globals.lookup_bool_option(Globals, Opt, Bool)),
|
|
OptionList),
|
|
bool.or_list(BoolList) = no.
|
|
|
|
:- pred compile_using_gcc_backend(options_variables::in, list(string)::in,
|
|
file_or_module::in,
|
|
frontend_callback(list(string))::in(frontend_callback),
|
|
list(string)::out, io::di, io::uo) is det.
|
|
|
|
compile_using_gcc_backend(OptionVariables, OptionArgs, FirstFileOrModule,
|
|
CallBack, ModulesToLink, !IO) :-
|
|
% The name of the assembler file that we generate
|
|
% is based on name of the first module named
|
|
% on the command line. (Mmake requires this.)
|
|
%
|
|
% There's two cases:
|
|
% (1) If the argument ends in ".m", we assume
|
|
% that the argument is a file name.
|
|
% To find the corresponding module name,
|
|
% we would need to read in the file
|
|
% (at least up to the first item);
|
|
% this is needed to handle the case where
|
|
% the module name does not match the file
|
|
% name, e.g. file "browse.m" containing
|
|
% ":- module mdb.browse." as its first item.
|
|
% Rather than reading in the source file here,
|
|
% we just pick a name
|
|
% for the asm file based on the file name argument,
|
|
% (e.g. "browse.s") and if necessary rename it later
|
|
% (e.g. to "mdb.browse.s").
|
|
%
|
|
% (2) If the argument doesn't end in `.m',
|
|
% then we assume it is a module name.
|
|
% (Is it worth checking that the name doesn't
|
|
% contain directory separators, and issuing
|
|
% a warning or error in that case?)
|
|
%
|
|
(
|
|
FirstFileOrModule = file(FirstFileName),
|
|
file_name_to_module_name(FirstFileName, FirstModuleName)
|
|
;
|
|
FirstFileOrModule = module(FirstModuleName)
|
|
),
|
|
|
|
% Invoke run_gcc_backend. It will call us back,
|
|
% and then we'll continue with the normal work of
|
|
% the compilation, which will be done by the callback
|
|
% function (`process_args').
|
|
maybe_mlds_to_gcc.run_gcc_backend(FirstModuleName, CallBack,
|
|
ModulesToLink, !IO),
|
|
|
|
% Now we know what the real module name was, so we
|
|
% can rename the assembler file if needed (see above).
|
|
( ModulesToLink = [Module | _] ->
|
|
file_name_to_module_name(Module, ModuleName),
|
|
globals.io_lookup_bool_option(pic, Pic, !IO),
|
|
AsmExt = (Pic = yes -> ".pic_s" ; ".s"),
|
|
module_name_to_file_name(ModuleName, AsmExt, yes, AsmFile, !IO),
|
|
( ModuleName \= FirstModuleName ->
|
|
module_name_to_file_name(FirstModuleName, AsmExt, no, FirstAsmFile,
|
|
!IO),
|
|
do_rename_file(FirstAsmFile, AsmFile, Result, !IO)
|
|
;
|
|
Result = ok
|
|
),
|
|
|
|
% Invoke the assembler to produce an object file,
|
|
% if needed.
|
|
globals.io_lookup_bool_option(target_code_only, TargetCodeOnly, !IO),
|
|
(
|
|
Result = ok,
|
|
TargetCodeOnly = no
|
|
->
|
|
io.output_stream(OutputStream, !IO),
|
|
get_linked_target_type(TargetType, !IO),
|
|
get_object_code_type(TargetType, PIC, !IO),
|
|
compile_with_module_options(ModuleName, OptionVariables,
|
|
OptionArgs,
|
|
compile_target_code.assemble(OutputStream, PIC, ModuleName),
|
|
AssembleOK, !IO),
|
|
maybe_set_exit_status(AssembleOK, !IO)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
% This can happen if smart recompilation decided
|
|
% that nothing needed to be compiled.
|
|
true
|
|
).
|
|
|
|
:- pred do_rename_file(string::in, string::in, io.res::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_rename_file(OldFileName, NewFileName, Result, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(very_verbose, VeryVerbose, !IO),
|
|
maybe_write_string(Verbose, "% Renaming `", !IO),
|
|
maybe_write_string(Verbose, OldFileName, !IO),
|
|
maybe_write_string(Verbose, "' as `", !IO),
|
|
maybe_write_string(Verbose, NewFileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.rename_file(OldFileName, NewFileName, Result0, !IO),
|
|
( Result0 = error(Error0) ->
|
|
maybe_write_string(VeryVerbose, " failed.\n", !IO),
|
|
maybe_flush_output(VeryVerbose, !IO),
|
|
io.error_message(Error0, ErrorMsg0),
|
|
% On some systems, we need to remove the existing file
|
|
% first, if any. So try again that way.
|
|
maybe_write_string(VeryVerbose, "% Removing `", !IO),
|
|
maybe_write_string(VeryVerbose, OldFileName, !IO),
|
|
maybe_write_string(VeryVerbose, "'...", !IO),
|
|
maybe_flush_output(VeryVerbose, !IO),
|
|
io.remove_file(NewFileName, Result1, !IO),
|
|
( Result1 = error(Error1) ->
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(Error1, ErrorMsg1),
|
|
string.append_list(["can't rename file `", OldFileName,
|
|
"' as `", NewFileName, "': ", ErrorMsg0,
|
|
"; and can't remove file `", NewFileName, "': ", ErrorMsg1],
|
|
Message),
|
|
report_error(Message, !IO),
|
|
Result = Result1
|
|
;
|
|
maybe_write_string(VeryVerbose, " done.\n", !IO),
|
|
maybe_write_string(VeryVerbose, "% Renaming `", !IO),
|
|
maybe_write_string(VeryVerbose, OldFileName, !IO),
|
|
maybe_write_string(VeryVerbose, "' as `", !IO),
|
|
maybe_write_string(VeryVerbose, NewFileName, !IO),
|
|
maybe_write_string(VeryVerbose, "' again...", !IO),
|
|
maybe_flush_output(VeryVerbose, !IO),
|
|
io.rename_file(OldFileName, NewFileName, Result2, !IO),
|
|
( Result2 = error(Error2) ->
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(Error2, ErrorMsg),
|
|
string.append_list(
|
|
["can't rename file `", OldFileName, "' as `", NewFileName,
|
|
"': ", ErrorMsg], Message),
|
|
report_error(Message, !IO)
|
|
;
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
),
|
|
Result = Result2
|
|
)
|
|
;
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
Result = Result0
|
|
).
|
|
|
|
:- pred process_args_no_fact_table(options_variables::in, list(string)::in,
|
|
list(string)::in, list(string)::out, io::di, io::uo) is det.
|
|
|
|
process_args_no_fact_table(OptionVariables, OptionArgs, Args, ModulesToLink,
|
|
!IO) :-
|
|
process_args(OptionVariables, OptionArgs, Args, ModulesToLink,
|
|
_FactTableObjFiles, !IO).
|
|
|
|
:- pred process_args(options_variables::in, list(string)::in, list(string)::in,
|
|
list(string)::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
process_args(OptionVariables, OptionArgs, Args, ModulesToLink,
|
|
FactTableObjFiles, !IO) :-
|
|
globals.io_lookup_bool_option(filenames_from_stdin, FileNamesFromStdin,
|
|
!IO),
|
|
(
|
|
FileNamesFromStdin = yes,
|
|
process_stdin_arg_list(OptionVariables, OptionArgs,
|
|
[], ModulesToLink, [], FactTableObjFiles, !IO)
|
|
;
|
|
FileNamesFromStdin = no,
|
|
process_arg_list(OptionVariables, OptionArgs,
|
|
Args, ModulesToLink, FactTableObjFiles, !IO)
|
|
).
|
|
|
|
:- pred process_stdin_arg_list(options_variables::in, list(string)::in,
|
|
list(string)::in, list(string)::out,
|
|
list(string)::in, list(string)::out, io::di, io::uo) is det.
|
|
|
|
process_stdin_arg_list(OptionVariables, OptionArgs, !Modules,
|
|
!FactTableObjFiles, !IO) :-
|
|
(
|
|
!.Modules = [_ | _],
|
|
garbage_collect(!IO)
|
|
;
|
|
!.Modules = []
|
|
),
|
|
io.read_line_as_string(FileResult, !IO),
|
|
(
|
|
FileResult = ok(Line),
|
|
Arg = string.rstrip(Line),
|
|
process_arg(OptionVariables, OptionArgs, Arg, Module,
|
|
FactTableObjFileList, !IO),
|
|
list.append(Module, !Modules),
|
|
list.append(FactTableObjFileList, !FactTableObjFiles),
|
|
process_stdin_arg_list(OptionVariables, OptionArgs,
|
|
!Modules, !FactTableObjFiles, !IO)
|
|
;
|
|
FileResult = eof
|
|
;
|
|
FileResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.write_string("Error reading module name: ", !IO),
|
|
io.write_string(Msg, !IO),
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
:- pred process_arg_list(options_variables::in, list(string)::in,
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_arg_list(OptionVariables, OptionArgs, Args, Modules, FactTableObjFiles,
|
|
!IO) :-
|
|
process_arg_list_2(OptionVariables, OptionArgs, Args, ModulesList,
|
|
FactTableObjFileLists, !IO),
|
|
list.condense(ModulesList, Modules),
|
|
list.condense(FactTableObjFileLists, FactTableObjFiles).
|
|
|
|
:- pred process_arg_list_2(options_variables::in, list(string)::in,
|
|
list(string)::in, list(list(string))::out, list(list(string))::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_arg_list_2(_, _, [], [], [], !IO).
|
|
process_arg_list_2(OptionVariables, OptionArgs, [Arg | Args],
|
|
[Modules | ModulesList],
|
|
[FactTableObjFiles | FactTableObjFileLists], !IO) :-
|
|
process_arg(OptionVariables, OptionArgs, Arg, Modules, FactTableObjFiles,
|
|
!IO),
|
|
(
|
|
Args = [_ | _],
|
|
garbage_collect(!IO)
|
|
;
|
|
Args = []
|
|
),
|
|
process_arg_list_2(OptionVariables, OptionArgs, Args, ModulesList,
|
|
FactTableObjFileLists, !IO).
|
|
|
|
% Figure out whether the argument is a module name or a file name.
|
|
% Open the specified file or module, and process it.
|
|
% Return the list of modules (including sub-modules,
|
|
% if they were compiled to seperate object files)
|
|
% that should be linked into the final executable.
|
|
|
|
:- pred process_arg(options_variables::in, list(string)::in, string::in,
|
|
list(string)::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
process_arg(OptionVariables, OptionArgs, Arg, ModulesToLink, FactTableObjFiles,
|
|
!IO) :-
|
|
FileOrModule = string_to_file_or_module(Arg),
|
|
globals.io_lookup_bool_option(invoked_by_mmc_make, InvokedByMake, !IO),
|
|
(
|
|
InvokedByMake = no,
|
|
build_with_module_options(file_or_module_to_module_name(FileOrModule),
|
|
OptionVariables, OptionArgs, [],
|
|
process_arg_build(FileOrModule, OptionVariables, OptionArgs),
|
|
_, [], MaybePair, !IO),
|
|
(
|
|
MaybePair = yes(ModulesToLink - FactTableObjFiles)
|
|
;
|
|
MaybePair = no,
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
)
|
|
;
|
|
InvokedByMake = yes,
|
|
% `mmc --make' has already set up the options.
|
|
process_arg_2(OptionVariables, OptionArgs, FileOrModule,
|
|
ModulesToLink, FactTableObjFiles, !IO)
|
|
).
|
|
|
|
:- pred process_arg_build(file_or_module::in, options_variables::in,
|
|
list(string)::in, list(string)::in, bool::out,
|
|
list(string)::in, pair(list(string))::out, io::di, io::uo) is det.
|
|
|
|
process_arg_build(FileOrModule, OptionVariables, OptionArgs, _, yes,
|
|
_, Modules - FactTableObjFiles, !IO) :-
|
|
process_arg_2(OptionVariables, OptionArgs, FileOrModule, Modules,
|
|
FactTableObjFiles, !IO).
|
|
|
|
:- pred process_arg_2(options_variables::in, list(string)::in,
|
|
file_or_module::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_arg_2(OptionVariables, OptionArgs, FileOrModule, ModulesToLink,
|
|
FactTableObjFiles, !IO) :-
|
|
globals.io_lookup_bool_option(generate_dependencies, GenerateDeps, !IO),
|
|
( GenerateDeps = yes,
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = [],
|
|
(
|
|
FileOrModule = file(FileName),
|
|
generate_file_dependencies(FileName, !IO)
|
|
;
|
|
FileOrModule = module(ModuleName),
|
|
generate_module_dependencies(ModuleName, !IO)
|
|
)
|
|
; GenerateDeps = no,
|
|
globals.io_lookup_bool_option(generate_dependency_file,
|
|
GenerateDepFile, !IO),
|
|
( GenerateDepFile = yes,
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = [],
|
|
(
|
|
FileOrModule = file(FileName),
|
|
generate_file_dependency_file(FileName, !IO)
|
|
;
|
|
FileOrModule = module(ModuleName),
|
|
generate_module_dependency_file(ModuleName, !IO)
|
|
)
|
|
; GenerateDepFile = no,
|
|
process_module(OptionVariables, OptionArgs,
|
|
FileOrModule, ModulesToLink, FactTableObjFiles, !IO)
|
|
)
|
|
).
|
|
|
|
:- type file_or_module
|
|
---> file(file_name)
|
|
; module(module_name).
|
|
|
|
:- func string_to_file_or_module(string) = file_or_module.
|
|
|
|
string_to_file_or_module(String) = FileOrModule :-
|
|
( string.remove_suffix(String, ".m", FileName) ->
|
|
% If the argument name ends in `.m', then we assume it is
|
|
% a file name.
|
|
FileOrModule = file(FileName)
|
|
;
|
|
% If it doesn't end in `.m', then we assume it is
|
|
% a module name. (Is it worth checking that the
|
|
% name doesn't contain directory separators, and issuing
|
|
% a warning or error in that case?)
|
|
file_name_to_module_name(String, ModuleName),
|
|
FileOrModule = module(ModuleName)
|
|
).
|
|
|
|
:- func file_or_module_to_module_name(file_or_module) = module_name.
|
|
|
|
file_or_module_to_module_name(file(FileName)) = ModuleName :-
|
|
% Assume the module name matches the file name.
|
|
file_name_to_module_name(FileName, ModuleName).
|
|
file_or_module_to_module_name(module(ModuleName)) = ModuleName.
|
|
|
|
:- pred read_module(file_or_module::in, bool::in, module_name::out,
|
|
file_name::out, maybe(timestamp)::out, item_list::out,
|
|
module_error::out, read_modules::in, read_modules::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module(module(ModuleName), ReturnTimestamp, ModuleName, FileName,
|
|
MaybeTimestamp, Items, Error, !ReadModules, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
maybe_write_string(Verbose, "% Parsing module `", !IO),
|
|
sym_name_to_string(ModuleName, ModuleNameString),
|
|
maybe_write_string(Verbose, ModuleNameString, !IO),
|
|
maybe_write_string(Verbose, "' and imported interfaces...\n", !IO),
|
|
(
|
|
% Avoid rereading the module if it was already read
|
|
% by recompilation_version.m.
|
|
find_read_module(!.ReadModules, ModuleName, ".m", ReturnTimestamp,
|
|
Items0, MaybeTimestamp0, Error0, FileName0)
|
|
->
|
|
map.delete(!.ReadModules, ModuleName - ".m", !:ReadModules),
|
|
FileName = FileName0,
|
|
Items = Items0,
|
|
Error = Error0,
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
;
|
|
% We don't search `--search-directories' for source files
|
|
% because that can result in the generated interface files
|
|
% being created in the wrong directory.
|
|
Search = no,
|
|
read_mod(ModuleName, ".m", "Reading module", Search, ReturnTimestamp,
|
|
Items, Error, FileName, MaybeTimestamp, !IO)
|
|
),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
read_module(file(FileName), ReturnTimestamp, ModuleName, SourceFileName,
|
|
MaybeTimestamp, Items, Error, !ReadModules, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
maybe_write_string(Verbose, "% Parsing file `", !IO),
|
|
maybe_write_string(Verbose, FileName, !IO),
|
|
maybe_write_string(Verbose, "' and imported interfaces...\n", !IO),
|
|
|
|
file_name_to_module_name(FileName, DefaultModuleName),
|
|
(
|
|
% Avoid rereading the module if it was already read
|
|
% by recompilation_version.m.
|
|
find_read_module(!.ReadModules, DefaultModuleName, ".m",
|
|
ReturnTimestamp, Items0, MaybeTimestamp0, Error0, _)
|
|
->
|
|
map.delete(!.ReadModules, ModuleName - ".m", !:ReadModules),
|
|
ModuleName = DefaultModuleName,
|
|
Items = Items0,
|
|
Error = Error0,
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
;
|
|
% We don't search `--search-directories' for source files
|
|
% because that can result in the generated interface files
|
|
% being created in the wrong directory.
|
|
Search = no,
|
|
read_mod_from_file(FileName, ".m", "Reading file", Search,
|
|
ReturnTimestamp, Items, Error, ModuleName, MaybeTimestamp, !IO),
|
|
|
|
%
|
|
% XXX If the module name doesn't match the file name
|
|
% the compiler won't be able to find the `.used'
|
|
% file (the name of the `.used' file is derived from
|
|
% the module name not the file name).
|
|
% This will be fixed when mmake functionality
|
|
% is moved into the compiler.
|
|
%
|
|
globals.io_lookup_bool_option(smart_recompilation, Smart, !IO),
|
|
(
|
|
Smart = yes,
|
|
ModuleName \= DefaultModuleName
|
|
->
|
|
globals.io_lookup_bool_option(warn_smart_recompilation, Warn,
|
|
!IO),
|
|
(
|
|
Warn = yes,
|
|
Msg1 = "Warning: module name does not " ++ "match file name: ",
|
|
ModuleNameStr = describe_sym_name(ModuleName),
|
|
Msg2 = FileName ++ " contains module `" ++ ModuleNameStr ++
|
|
"'.",
|
|
Msg3 = "Smart recompilation will not work unless " ++
|
|
"a module name to file name mapping is created using " ++
|
|
"`mmc -f *.m'.",
|
|
write_error_pieces_plain([words(Msg1), nl, words(Msg2), nl,
|
|
words(Msg3)], !IO),
|
|
record_warning(!IO)
|
|
;
|
|
Warn = no
|
|
),
|
|
globals.io_set_option(smart_recompilation, bool(no), !IO)
|
|
;
|
|
true
|
|
)
|
|
),
|
|
globals.io_lookup_bool_option(detailed_statistics, Stats, !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
string.append(FileName, ".m", SourceFileName).
|
|
|
|
:- pred process_module(options_variables::in, list(string)::in,
|
|
file_or_module::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_module(OptionVariables, OptionArgs, FileOrModule, ModulesToLink,
|
|
FactTableObjFiles, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, halt_at_syntax_errors,
|
|
HaltSyntax),
|
|
globals.lookup_bool_option(Globals, make_interface, MakeInterface),
|
|
globals.lookup_bool_option(Globals, make_short_interface,
|
|
MakeShortInterface),
|
|
globals.lookup_bool_option(Globals, make_private_interface,
|
|
MakePrivateInterface),
|
|
globals.lookup_bool_option(Globals, convert_to_mercury,
|
|
ConvertToMercury),
|
|
globals.lookup_bool_option(Globals, generate_item_version_numbers,
|
|
GenerateVersionNumbers),
|
|
(
|
|
( MakeInterface = yes ->
|
|
ProcessModule = make_interface,
|
|
ReturnTimestamp = GenerateVersionNumbers
|
|
; MakeShortInterface = yes ->
|
|
ProcessModule = make_short_interface,
|
|
ReturnTimestamp = no
|
|
; MakePrivateInterface = yes ->
|
|
ProcessModule = make_private_interface,
|
|
ReturnTimestamp = GenerateVersionNumbers
|
|
;
|
|
fail
|
|
)
|
|
->
|
|
read_module(FileOrModule, ReturnTimestamp, ModuleName, FileName,
|
|
MaybeTimestamp, Items, Error, map.init, _, !IO),
|
|
( halt_at_module_error(HaltSyntax, Error) ->
|
|
true
|
|
;
|
|
split_into_submodules(ModuleName, Items, SubModuleList, !IO),
|
|
list.foldl(apply_process_module(ProcessModule,
|
|
FileName, ModuleName, MaybeTimestamp), SubModuleList, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
;
|
|
ConvertToMercury = yes
|
|
->
|
|
read_module(FileOrModule, no, ModuleName, _, _,
|
|
Items, Error, map.init, _, !IO),
|
|
( halt_at_module_error(HaltSyntax, Error) ->
|
|
true
|
|
;
|
|
module_name_to_file_name(ModuleName, ".ugly", yes, OutputFileName,
|
|
!IO),
|
|
convert_to_mercury(ModuleName, OutputFileName, Items, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
;
|
|
globals.lookup_bool_option(Globals, smart_recompilation,
|
|
Smart),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Smart = yes,
|
|
(
|
|
FileOrModule = module(ModuleName)
|
|
;
|
|
FileOrModule = file(FileName),
|
|
% XXX This won't work if the module name
|
|
% doesn't match the file name -- such
|
|
% modules will always be recompiled.
|
|
%
|
|
% This problem will be fixed when mmake
|
|
% functionality is moved into the compiler.
|
|
% The file_name->module_name mapping
|
|
% will be explicitly recorded.
|
|
file_name_to_module_name(FileName, ModuleName)
|
|
),
|
|
|
|
find_smart_recompilation_target_files(ModuleName, Globals,
|
|
FindTargetFiles),
|
|
find_timestamp_files(ModuleName, Globals, FindTimestampFiles),
|
|
recompilation.check.should_recompile(ModuleName, FindTargetFiles,
|
|
FindTimestampFiles, ModulesToRecompile0, ReadModules, !IO),
|
|
(
|
|
Target = asm,
|
|
ModulesToRecompile0 = some_modules([_ | _])
|
|
->
|
|
%
|
|
% With `--target asm', if one module
|
|
% needs to be recompiled, all need to be
|
|
% recompiled because they are all compiled
|
|
% into a single object file.
|
|
%
|
|
ModulesToRecompile = all_modules
|
|
;
|
|
ModulesToRecompile = ModulesToRecompile0
|
|
)
|
|
;
|
|
Smart = no,
|
|
map.init(ReadModules),
|
|
ModulesToRecompile = all_modules
|
|
),
|
|
( ModulesToRecompile = some_modules([]) ->
|
|
% XXX Currently smart recompilation is disabled
|
|
% if mmc is linking the executable because it
|
|
% doesn't know how to check whether all the
|
|
% necessary intermediate files are present
|
|
% and up-to-date.
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
;
|
|
(
|
|
Target = asm,
|
|
Smart = yes
|
|
->
|
|
% See the comment in process_all_args.
|
|
compile_using_gcc_backend(OptionVariables,
|
|
OptionArgs, FileOrModule,
|
|
process_module_2_no_fact_table(FileOrModule,
|
|
ModulesToRecompile, ReadModules),
|
|
ModulesToLink, !IO),
|
|
FactTableObjFiles = []
|
|
;
|
|
process_module_2(FileOrModule, ModulesToRecompile, ReadModules,
|
|
ModulesToLink, FactTableObjFiles, !IO)
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred apply_process_module(
|
|
pred(file_name, module_name, maybe(timestamp),
|
|
pair(module_name, item_list), io, io)::
|
|
in(pred(in, in, in, in, di, uo) is det),
|
|
file_name::in, module_name::in, maybe(timestamp)::in,
|
|
pair(module_name, item_list)::in, io::di, io::uo) is det.
|
|
|
|
apply_process_module(ProcessModule, FileName, ModuleName, MaybeTimestamp,
|
|
SubModule, !IO) :-
|
|
ProcessModule(FileName, ModuleName, MaybeTimestamp, SubModule, !IO).
|
|
|
|
:- pred process_module_2_no_fact_table(file_or_module::in,
|
|
modules_to_recompile::in, read_modules::in, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_module_2_no_fact_table(FileOrModule, MaybeModulesToRecompile,
|
|
ReadModules0, ModulesToLink, !IO) :-
|
|
process_module_2(FileOrModule, MaybeModulesToRecompile, ReadModules0,
|
|
ModulesToLink, _FactTableObjFiles, !IO).
|
|
|
|
:- pred process_module_2(file_or_module::in, modules_to_recompile::in,
|
|
read_modules::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_module_2(FileOrModule, MaybeModulesToRecompile, ReadModules0,
|
|
ModulesToLink, FactTableObjFiles, !IO) :-
|
|
read_module(FileOrModule, yes, ModuleName, FileName,
|
|
MaybeTimestamp, Items, Error, ReadModules0, ReadModules, !IO),
|
|
globals.io_lookup_bool_option(halt_at_syntax_errors, HaltSyntax, !IO),
|
|
( halt_at_module_error(HaltSyntax, Error) ->
|
|
ModulesToLink = [],
|
|
FactTableObjFiles = []
|
|
;
|
|
split_into_submodules(ModuleName, Items, SubModuleList0, !IO),
|
|
( MaybeModulesToRecompile = some_modules(ModulesToRecompile) ->
|
|
ToRecompile = (pred((SubModule - _)::in) is semidet :-
|
|
list.member(SubModule, ModulesToRecompile)
|
|
),
|
|
list.filter(ToRecompile, SubModuleList0, SubModuleListToCompile)
|
|
;
|
|
SubModuleListToCompile = SubModuleList0
|
|
),
|
|
assoc_list.keys(SubModuleList0, NestedSubModules0),
|
|
list.delete_all(NestedSubModules0, ModuleName, NestedSubModules),
|
|
|
|
globals.io_get_globals(Globals, !IO),
|
|
find_timestamp_files(ModuleName, Globals, FindTimestampFiles),
|
|
|
|
globals.io_lookup_bool_option(trace_prof, TraceProf, !IO),
|
|
|
|
(
|
|
non_traced_mercury_builtin_module(ModuleName),
|
|
not (
|
|
mercury_profiling_builtin_module(ModuleName),
|
|
TraceProf = yes
|
|
)
|
|
->
|
|
% Some predicates in the builtin modules are missing
|
|
% typeinfo arguments, which means that execution
|
|
% tracing will not work on them. Predicates defined
|
|
% there should never be part of an execution trace
|
|
% anyway; they are effectively language primitives.
|
|
% (They may still be parts of stack traces.)
|
|
globals.io_lookup_bool_option(trace_stack_layout, TSL, !IO),
|
|
globals.io_get_trace_level(TraceLevel, !IO),
|
|
|
|
globals.io_set_option(trace_stack_layout, bool(no), !IO),
|
|
globals.io_set_trace_level_none(!IO),
|
|
|
|
compile_all_submodules(FileName, ModuleName, NestedSubModules,
|
|
MaybeTimestamp, ReadModules, FindTimestampFiles,
|
|
SubModuleListToCompile, ModulesToLink, FactTableObjFiles, !IO),
|
|
|
|
globals.io_set_option(trace_stack_layout, bool(TSL), !IO),
|
|
globals.io_set_trace_level(TraceLevel, !IO)
|
|
;
|
|
compile_all_submodules(FileName, ModuleName, NestedSubModules,
|
|
MaybeTimestamp, ReadModules, FindTimestampFiles,
|
|
SubModuleListToCompile, ModulesToLink, FactTableObjFiles, !IO)
|
|
)
|
|
).
|
|
|
|
% For the MLDS->C and LLDS->C back-ends, we currently
|
|
% compile each sub-module to its own C file.
|
|
% XXX it would be better to do something like
|
|
%
|
|
% list.map2_foldl(compile_to_llds, SubModuleList,
|
|
% LLDS_FragmentList),
|
|
% merge_llds_fragments(LLDS_FragmentList, LLDS),
|
|
% output_pass(LLDS_FragmentList)
|
|
%
|
|
% i.e. compile nested modules to a single C file.
|
|
|
|
:- pred compile_all_submodules(string::in, module_name::in,
|
|
list(module_name)::in, maybe(timestamp)::in, read_modules::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
list(pair(module_name, item_list))::in, list(string)::out,
|
|
list(string)::out, io::di, io::uo) is det.
|
|
|
|
compile_all_submodules(FileName, SourceFileModuleName, NestedSubModules,
|
|
MaybeTimestamp, ReadModules, FindTimestampFiles,
|
|
SubModuleList, ModulesToLink, FactTableObjFiles, !IO) :-
|
|
list.map_foldl(compile(FileName, SourceFileModuleName, NestedSubModules,
|
|
MaybeTimestamp, ReadModules, FindTimestampFiles),
|
|
SubModuleList, FactTableObjFileLists, !IO),
|
|
list.map(module_to_link, SubModuleList, ModulesToLink),
|
|
list.condense(FactTableObjFileLists, FactTableObjFiles).
|
|
|
|
:- pred make_interface(file_name::in, module_name::in, maybe(timestamp)::in,
|
|
pair(module_name, item_list)::in, io::di, io::uo) is det.
|
|
|
|
make_interface(SourceFileName, SourceFileModuleName, MaybeTimestamp,
|
|
ModuleName - Items, !IO) :-
|
|
make_interface(SourceFileName, SourceFileModuleName,
|
|
ModuleName, MaybeTimestamp, Items, !IO).
|
|
|
|
:- pred make_short_interface(file_name::in, module_name::in,
|
|
maybe(timestamp)::in, pair(module_name, item_list)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
make_short_interface(SourceFileName, _, _, ModuleName - Items, !IO) :-
|
|
make_short_interface(SourceFileName, ModuleName, Items, !IO).
|
|
|
|
:- pred make_private_interface(file_name::in, module_name::in,
|
|
maybe(timestamp)::in, pair(module_name, item_list)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
make_private_interface(SourceFileName, SourceFileModuleName,
|
|
MaybeTimestamp, ModuleName - Items, !IO) :-
|
|
make_private_interface(SourceFileName, SourceFileModuleName,
|
|
ModuleName, MaybeTimestamp, Items, !IO).
|
|
|
|
:- pred halt_at_module_error(bool::in, module_error::in) is semidet.
|
|
|
|
halt_at_module_error(_, fatal_module_errors).
|
|
halt_at_module_error(HaltSyntax, some_module_errors) :- HaltSyntax = yes.
|
|
|
|
:- pred module_to_link(pair(module_name, item_list)::in, string::out) is det.
|
|
|
|
module_to_link(ModuleName - _Items, ModuleToLink) :-
|
|
module_name_to_file_name(ModuleName, ModuleToLink).
|
|
|
|
:- type compile == pred(bool, io, io).
|
|
:- inst compile == (pred(out, di, uo) is det).
|
|
|
|
:- pred compile_with_module_options(module_name::in, options_variables::in,
|
|
list(string)::in, compile::in(compile), bool::out, io::di, io::uo)
|
|
is det.
|
|
|
|
compile_with_module_options(ModuleName, OptionVariables, OptionArgs,
|
|
Compile, Succeeded, !IO) :-
|
|
globals.io_lookup_bool_option(invoked_by_mmc_make, InvokedByMake, !IO),
|
|
(
|
|
InvokedByMake = yes,
|
|
% `mmc --make' has already set up the options.
|
|
Compile(Succeeded, !IO)
|
|
;
|
|
InvokedByMake = no,
|
|
build_with_module_options(ModuleName, OptionVariables, OptionArgs, [],
|
|
(pred(_::in, Succeeded0::out, X::in, X::out,
|
|
IO0::di, IO::uo) is det :-
|
|
Compile(Succeeded0, IO0, IO)
|
|
), Succeeded, unit, _, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Return a closure which will work out what the target files
|
|
% are for a module, so recompilation_check.m can check that
|
|
% they are up-to-date which deciding whether compilation is
|
|
% necessary.
|
|
% Note that `--smart-recompilation' only works with
|
|
% `--target-code-only', which is always set when the
|
|
% compiler is invoked by mmake. Using smart recompilation
|
|
% without using mmake is not a sensible thing to do.
|
|
% handle_options.m will disable smart recompilation if
|
|
% `--target-code-only' is not set.
|
|
:- pred find_smart_recompilation_target_files(module_name::in, globals::in,
|
|
find_target_file_names::out(find_target_file_names)) is det.
|
|
|
|
find_smart_recompilation_target_files(TopLevelModuleName,
|
|
Globals, FindTargetFiles) :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
( CompilationTarget = c, TargetSuffix = ".c"
|
|
; CompilationTarget = il, TargetSuffix = ".il"
|
|
; CompilationTarget = java, TargetSuffix = ".java"
|
|
; CompilationTarget = asm, TargetSuffix = ".s"
|
|
),
|
|
FindTargetFiles = usual_find_target_files(CompilationTarget,
|
|
TargetSuffix, TopLevelModuleName).
|
|
|
|
:- pred usual_find_target_files(compilation_target::in, string::in,
|
|
module_name::in, module_name::in, list(file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
usual_find_target_files(CompilationTarget, TargetSuffix, TopLevelModuleName,
|
|
ModuleName, TargetFiles, !IO) :-
|
|
% XXX Should we check the generated header files?
|
|
(
|
|
CompilationTarget = asm,
|
|
ModuleName \= TopLevelModuleName
|
|
->
|
|
% With `--target asm' all the nested sub-modules are placed
|
|
% in the `.s' file of the top-level module.
|
|
TargetFiles = []
|
|
;
|
|
module_name_to_file_name(ModuleName, TargetSuffix, yes, FileName, !IO),
|
|
TargetFiles = [FileName]
|
|
).
|
|
|
|
:- pred find_timestamp_files(module_name::in, globals::in,
|
|
find_timestamp_file_names::out(find_timestamp_file_names)) is det.
|
|
|
|
find_timestamp_files(TopLevelModuleName, Globals, FindTimestampFiles) :-
|
|
globals.lookup_bool_option(Globals, pic, Pic),
|
|
globals.get_target(Globals, CompilationTarget),
|
|
(
|
|
CompilationTarget = c,
|
|
TimestampSuffix = ".c_date"
|
|
;
|
|
CompilationTarget = il,
|
|
TimestampSuffix = ".il_date"
|
|
;
|
|
CompilationTarget = java,
|
|
TimestampSuffix = ".java_date"
|
|
;
|
|
CompilationTarget = asm,
|
|
TimestampSuffix = (Pic = yes -> ".pic_s_date" ; ".s_date")
|
|
),
|
|
FindTimestampFiles = find_timestamp_files_2(CompilationTarget,
|
|
TimestampSuffix, TopLevelModuleName).
|
|
|
|
:- pred find_timestamp_files_2(compilation_target::in, string::in,
|
|
module_name::in, module_name::in, list(file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
find_timestamp_files_2(CompilationTarget, TimestampSuffix,
|
|
TopLevelModuleName, ModuleName, TimestampFiles, !IO) :-
|
|
(
|
|
CompilationTarget = asm,
|
|
ModuleName \= TopLevelModuleName
|
|
->
|
|
% With `--target asm' all the nested
|
|
% sub-modules are placed in the `.s' file
|
|
% of the top-level module.
|
|
TimestampFiles = []
|
|
;
|
|
module_name_to_file_name(ModuleName, TimestampSuffix, yes, FileName,
|
|
!IO),
|
|
TimestampFiles = [FileName]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a fully expanded module (i.e. a module name and a list
|
|
% of all the items in the module and any of its imports),
|
|
% compile it.
|
|
|
|
% Stage number assignments:
|
|
%
|
|
% 1 to 99 front end pass
|
|
% 100 to 299 middle pass
|
|
% 300 to 399 LLDS back end pass
|
|
% 400 to 499 MLDS back end pass
|
|
% 500 to 599 bytecode back end pass
|
|
%
|
|
% The initial arrangement has the stage numbers increasing by five
|
|
% so that new stages can be slotted in without too much trouble.
|
|
|
|
:- pred compile(file_name::in, module_name::in, list(module_name)::in,
|
|
maybe(timestamp)::in, read_modules::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
pair(module_name, item_list)::in, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
compile(SourceFileName, SourceFileModuleName, NestedSubModules0,
|
|
MaybeTimestamp, ReadModules, FindTimestampFiles,
|
|
ModuleName - Items, FactTableObjFiles, !IO) :-
|
|
check_for_no_exports(Items, ModuleName, !IO),
|
|
( ModuleName = SourceFileModuleName ->
|
|
NestedSubModules = NestedSubModules0
|
|
;
|
|
NestedSubModules = []
|
|
),
|
|
grab_imported_modules(SourceFileName, SourceFileModuleName, ModuleName,
|
|
NestedSubModules, ReadModules, MaybeTimestamp, Items, Module, Error2,
|
|
!IO),
|
|
( Error2 \= fatal_module_errors ->
|
|
mercury_compile(Module, NestedSubModules, FindTimestampFiles,
|
|
FactTableObjFiles, no_prev_dump, _, !IO)
|
|
;
|
|
FactTableObjFiles = []
|
|
).
|
|
|
|
:- pred mercury_compile(module_imports::in, list(module_name)::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
list(string)::out, dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
mercury_compile(Module, NestedSubModules, FindTimestampFiles,
|
|
FactTableObjFiles, !DumpInfo, !IO) :-
|
|
module_imports_get_module_name(Module, ModuleName),
|
|
% If we are only typechecking or error checking, then we should not
|
|
% modify any files, this includes writing to .d files.
|
|
globals.io_lookup_bool_option(typecheck_only, TypeCheckOnly, !IO),
|
|
globals.io_lookup_bool_option(errorcheck_only, ErrorCheckOnly, !IO),
|
|
bool.or(TypeCheckOnly, ErrorCheckOnly, DontWriteDFile),
|
|
pre_hlds_pass(Module, DontWriteDFile, HLDS1, QualInfo, MaybeTimestamps,
|
|
UndefTypes, UndefModes, Errors1, !DumpInfo, !IO),
|
|
frontend_pass(QualInfo, UndefTypes, UndefModes, Errors1, Errors2,
|
|
HLDS1, HLDS20, !DumpInfo, !IO),
|
|
(
|
|
Errors1 = no,
|
|
Errors2 = no
|
|
->
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
maybe_write_dependency_graph(Verbose, Stats, HLDS20, HLDS21, !IO),
|
|
globals.io_lookup_bool_option(make_optimization_interface,
|
|
MakeOptInt, !IO),
|
|
globals.io_lookup_bool_option(make_transitive_opt_interface,
|
|
MakeTransOptInt, !IO),
|
|
globals.io_lookup_bool_option(make_analysis_registry,
|
|
MakeAnalysisRegistry, !IO),
|
|
( TypeCheckOnly = yes ->
|
|
FactTableObjFiles = []
|
|
; ErrorCheckOnly = yes ->
|
|
% we may still want to run `unused_args' so that we get
|
|
% the appropriate warnings
|
|
globals.io_lookup_bool_option(warn_unused_args, UnusedArgs, !IO),
|
|
(
|
|
UnusedArgs = yes,
|
|
globals.io_set_option(optimize_unused_args, bool(no), !IO),
|
|
maybe_unused_args(Verbose, Stats, HLDS21, _HLDS22, !IO)
|
|
;
|
|
UnusedArgs = no
|
|
),
|
|
FactTableObjFiles = []
|
|
; MakeOptInt = yes ->
|
|
% only run up to typechecking when making the .opt file
|
|
FactTableObjFiles = []
|
|
; MakeTransOptInt = yes ->
|
|
output_trans_opt_file(HLDS21, !DumpInfo, !IO),
|
|
FactTableObjFiles = []
|
|
; MakeAnalysisRegistry = yes ->
|
|
output_analysis_file(ModuleName, HLDS21, !DumpInfo, !IO),
|
|
FactTableObjFiles = []
|
|
;
|
|
mercury_compile_after_front_end(NestedSubModules,
|
|
FindTimestampFiles, MaybeTimestamps, ModuleName, HLDS21,
|
|
FactTableObjFiles, !DumpInfo, !IO)
|
|
)
|
|
;
|
|
% If the number of errors is > 0, make sure that the compiler
|
|
% exits with a non-zero exit status.
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( ExitStatus = 0 ->
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
true
|
|
),
|
|
FactTableObjFiles = []
|
|
).
|
|
|
|
:- pred mercury_compile_after_front_end(list(module_name)::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
maybe(module_timestamps)::in, module_name::in, module_info::in,
|
|
list(string)::out, dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
mercury_compile_after_front_end(NestedSubModules, FindTimestampFiles,
|
|
MaybeTimestamps, ModuleName, !.HLDS, FactTableBaseFiles, !DumpInfo,
|
|
!IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
maybe_output_prof_call_graph(Verbose, Stats, !HLDS, !IO),
|
|
middle_pass(ModuleName, !HLDS, !DumpInfo, !IO),
|
|
globals.io_lookup_bool_option(highlevel_code, HighLevelCode, !IO),
|
|
globals.io_get_target(Target, !IO),
|
|
globals.io_lookup_bool_option(target_code_only, TargetCodeOnly, !IO),
|
|
|
|
%
|
|
% Remove any existing `.used' file before writing the
|
|
% output file file. This avoids leaving the old `used'
|
|
% file lying around if compilation is interrupted after
|
|
% the new output file is written but before the new
|
|
% `.used' file is written.
|
|
%
|
|
module_name_to_file_name(ModuleName, ".used", no, UsageFileName, !IO),
|
|
io.remove_file(UsageFileName, _, !IO),
|
|
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( NumErrors = 0 ->
|
|
(
|
|
( Target = c
|
|
; Target = asm
|
|
)
|
|
->
|
|
%
|
|
% Produce the grade independent header file
|
|
% <module>.mh containing function prototypes
|
|
% for the `:- pragma export'ed procedures.
|
|
%
|
|
export.get_foreign_export_decls(!.HLDS, ExportDecls),
|
|
export.produce_header_file(ExportDecls, ModuleName, !IO)
|
|
;
|
|
true
|
|
),
|
|
( Target = il ->
|
|
mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
|
|
(
|
|
TargetCodeOnly = yes,
|
|
mlds_to_il_assembler(MLDS, !IO)
|
|
;
|
|
TargetCodeOnly = no,
|
|
HasMain = mlds_has_main(MLDS),
|
|
mlds_to_il_assembler(MLDS, !IO),
|
|
io.output_stream(OutputStream, !IO),
|
|
compile_target_code.il_assemble(OutputStream, ModuleName,
|
|
HasMain, Succeeded, !IO),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
),
|
|
FactTableBaseFiles = []
|
|
; Target = java ->
|
|
mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
|
|
mlds_to_java(!.HLDS, MLDS, !IO),
|
|
(
|
|
TargetCodeOnly = yes
|
|
;
|
|
TargetCodeOnly = no,
|
|
io.output_stream(OutputStream, !IO),
|
|
module_name_to_file_name(ModuleName, ".java", no, JavaFile,
|
|
!IO),
|
|
compile_target_code.compile_java_file(OutputStream, JavaFile,
|
|
Succeeded, !IO),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
),
|
|
FactTableBaseFiles = []
|
|
; Target = asm ->
|
|
% compile directly to assembler using the gcc back-end
|
|
mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
|
|
maybe_mlds_to_gcc(MLDS, ContainsCCode, !IO),
|
|
(
|
|
TargetCodeOnly = yes
|
|
;
|
|
TargetCodeOnly = no,
|
|
% We don't invoke the assembler to produce an object file yet
|
|
% -- that is done at the top level.
|
|
%
|
|
% But if the module contained `pragma c_code', then we will
|
|
% have compiled that to a separate C file. We need to invoke
|
|
% the C compiler on that.
|
|
(
|
|
ContainsCCode = yes,
|
|
mercury_compile_asm_c_code(ModuleName, !IO)
|
|
;
|
|
ContainsCCode = no
|
|
)
|
|
),
|
|
FactTableBaseFiles = []
|
|
; HighLevelCode = yes ->
|
|
mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
|
|
mlds_to_high_level_c(MLDS, !IO),
|
|
(
|
|
TargetCodeOnly = yes
|
|
;
|
|
TargetCodeOnly = no,
|
|
module_name_to_file_name(ModuleName, ".c", no, C_File, !IO),
|
|
get_linked_target_type(TargetType, !IO),
|
|
get_object_code_type(TargetType, PIC, !IO),
|
|
maybe_pic_object_file_extension(PIC, Obj, !IO),
|
|
module_name_to_file_name(ModuleName, Obj, yes, O_File, !IO),
|
|
io.output_stream(OutputStream, !IO),
|
|
compile_target_code.compile_c_file(OutputStream, PIC, C_File,
|
|
O_File, CompileOK, !IO),
|
|
maybe_set_exit_status(CompileOK, !IO)
|
|
),
|
|
FactTableBaseFiles = []
|
|
;
|
|
backend_pass(!HLDS, GlobalData, LLDS, !DumpInfo, !IO),
|
|
output_pass(!.HLDS, GlobalData, LLDS, ModuleName,
|
|
_CompileErrors, FactTableBaseFiles, !IO)
|
|
),
|
|
recompilation.usage.write_usage_file(!.HLDS, NestedSubModules,
|
|
MaybeTimestamps, !IO),
|
|
FindTimestampFiles(ModuleName, TimestampFiles, !IO),
|
|
list.foldl(touch_datestamp, TimestampFiles, !IO)
|
|
;
|
|
% If the number of errors is > 0, make sure that the compiler
|
|
% exits with a non-zero exit status.
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( ExitStatus = 0 ->
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
true
|
|
),
|
|
FactTableBaseFiles = []
|
|
).
|
|
|
|
:- pred mercury_compile_asm_c_code(module_name::in, io::di, io::uo) is det.
|
|
|
|
mercury_compile_asm_c_code(ModuleName, !IO) :-
|
|
get_linked_target_type(TargetType, !IO),
|
|
get_object_code_type(TargetType, PIC, !IO),
|
|
maybe_pic_object_file_extension(PIC, Obj, !IO),
|
|
module_name_to_file_name(ModuleName, ".c", no, CCode_C_File, !IO),
|
|
ForeignModuleName = foreign_language_module_name(ModuleName, c),
|
|
module_name_to_file_name(ForeignModuleName, Obj, yes, CCode_O_File, !IO),
|
|
io.output_stream(OutputStream, !IO),
|
|
compile_target_code.compile_c_file(OutputStream, PIC,
|
|
CCode_C_File, CCode_O_File, CompileOK, !IO),
|
|
maybe_set_exit_status(CompileOK, !IO),
|
|
% add this object file to the list
|
|
% of extra object files to link in
|
|
globals.io_lookup_accumulating_option(link_objects, LinkObjects, !IO),
|
|
globals.io_set_option(link_objects,
|
|
accumulating([CCode_O_File | LinkObjects]), !IO).
|
|
|
|
% return `yes' iff this module defines the main/2 entry point.
|
|
:- func mlds_has_main(mlds) = has_main.
|
|
|
|
mlds_has_main(MLDS) =
|
|
(
|
|
MLDS = mlds(_, _, _, Defns, _, _),
|
|
defns_contain_main(Defns)
|
|
->
|
|
has_main
|
|
;
|
|
no_main
|
|
).
|
|
|
|
:- pred get_linked_target_type(linked_target_type::out, io::di, io::uo) is det.
|
|
|
|
get_linked_target_type(LinkedTargetType, !IO) :-
|
|
globals.io_lookup_bool_option(compile_to_shared_lib, MakeSharedLib, !IO),
|
|
( MakeSharedLib = yes ->
|
|
LinkedTargetType = shared_library
|
|
;
|
|
LinkedTargetType = executable
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred pre_hlds_pass(module_imports::in, bool::in, module_info::out,
|
|
make_hlds_qual_info::out, maybe(module_timestamps)::out,
|
|
bool::out, bool::out, bool::out, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
pre_hlds_pass(ModuleImports0, DontWriteDFile0, HLDS1, QualInfo,
|
|
MaybeTimestamps, UndefTypes, UndefModes, FoundError, !DumpInfo, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
globals.lookup_bool_option(Globals, invoked_by_mmc_make, MMCMake),
|
|
DontWriteDFile1 = DontWriteDFile0 `or` MMCMake,
|
|
|
|
% Don't write the `.d' file when making the `.opt' file because
|
|
% we can't work out the full transitive implementation dependencies.
|
|
globals.lookup_bool_option(Globals, make_optimization_interface,
|
|
MakeOptInt),
|
|
DontWriteDFile = DontWriteDFile1 `or` MakeOptInt,
|
|
|
|
module_imports_get_module_name(ModuleImports0, Module),
|
|
(
|
|
DontWriteDFile = yes,
|
|
% The only time the TransOptDeps are required is when
|
|
% creating the .trans_opt file. If DontWriteDFile is yes,
|
|
% then error check only or type-check only is enabled, so
|
|
% we cant be creating the .trans_opt file.
|
|
MaybeTransOptDeps = no
|
|
;
|
|
DontWriteDFile = no,
|
|
maybe_read_dependency_file(Module, MaybeTransOptDeps, !IO)
|
|
),
|
|
|
|
% Errors in .opt and .trans_opt files result in software errors.
|
|
maybe_grab_optfiles(ModuleImports0, Verbose, MaybeTransOptDeps,
|
|
ModuleImports1, IntermodError, !IO),
|
|
|
|
module_imports_get_items(ModuleImports1, Items1),
|
|
MaybeTimestamps = ModuleImports1 ^ maybe_timestamps,
|
|
|
|
module_qualify_items(Items1, Items2, Module, Verbose, Stats, MQInfo0, _,
|
|
UndefTypes0, UndefModes0, !IO),
|
|
|
|
mq_info_get_recompilation_info(MQInfo0, RecompInfo0),
|
|
expand_equiv_types(Module, Items2, Verbose, Stats, Items, CircularTypes,
|
|
EqvMap, RecompInfo0, RecompInfo, !IO),
|
|
mq_info_set_recompilation_info(RecompInfo, MQInfo0, MQInfo),
|
|
bool.or(UndefTypes0, CircularTypes, UndefTypes1),
|
|
|
|
make_hlds(Module, Items, MQInfo, EqvMap, Verbose, Stats, HLDS0, QualInfo,
|
|
UndefTypes2, UndefModes2, FoundError, !IO),
|
|
|
|
bool.or(UndefTypes1, UndefTypes2, UndefTypes),
|
|
bool.or(UndefModes0, UndefModes2, UndefModes),
|
|
|
|
maybe_dump_hlds(HLDS0, 1, "initial", !DumpInfo, !IO),
|
|
|
|
(
|
|
DontWriteDFile = yes
|
|
;
|
|
DontWriteDFile = no,
|
|
module_info_get_all_deps(HLDS0, AllDeps),
|
|
write_dependency_file(ModuleImports0, AllDeps, MaybeTransOptDeps, !IO),
|
|
globals.lookup_bool_option(Globals,
|
|
generate_mmc_make_module_dependencies, OutputMMCMakeDeps),
|
|
(
|
|
OutputMMCMakeDeps = yes,
|
|
make_write_module_dep_file(ModuleImports0, !IO)
|
|
;
|
|
OutputMMCMakeDeps = no
|
|
)
|
|
),
|
|
|
|
% Only stop on syntax errors in .opt files.
|
|
(
|
|
( FoundError = yes
|
|
; IntermodError = yes
|
|
)
|
|
->
|
|
module_info_incr_errors(HLDS0, HLDS1)
|
|
;
|
|
HLDS1 = HLDS0
|
|
).
|
|
|
|
:- pred module_qualify_items(item_list::in, item_list::out, module_name::in,
|
|
bool::in, bool::in, mq_info::out, int::out, bool::out, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
module_qualify_items(Items0, Items, ModuleName, Verbose, Stats, MQInfo,
|
|
NumErrors, UndefTypes, UndefModes, !IO) :-
|
|
maybe_write_string(Verbose, "% Module qualifying items...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
module_qual.module_qualify_items(Items0, Items, ModuleName, yes,
|
|
MQInfo, NumErrors, UndefTypes, UndefModes, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_grab_optfiles(module_imports::in, bool::in,
|
|
maybe(list(module_name))::in, module_imports::out, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_grab_optfiles(Imports0, Verbose, MaybeTransOptDeps, Imports, Error,
|
|
!IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, intermodule_optimization,
|
|
IntermodOpt),
|
|
globals.lookup_bool_option(Globals, use_opt_files, UseOptInt),
|
|
globals.lookup_bool_option(Globals, make_optimization_interface,
|
|
MakeOptInt),
|
|
globals.lookup_bool_option(Globals, transitive_optimization, TransOpt),
|
|
globals.lookup_bool_option(Globals, make_transitive_opt_interface,
|
|
MakeTransOptInt),
|
|
(
|
|
( UseOptInt = yes
|
|
; IntermodOpt = yes
|
|
),
|
|
MakeOptInt = no
|
|
->
|
|
maybe_write_string(Verbose, "% Reading .opt files...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
intermod.grab_optfiles(Imports0, Imports1, Error1, !IO),
|
|
maybe_write_string(Verbose, "% Done.\n", !IO)
|
|
;
|
|
Imports1 = Imports0,
|
|
Error1 = no
|
|
),
|
|
( MakeTransOptInt = yes ->
|
|
(
|
|
MaybeTransOptDeps = yes(TransOptDeps),
|
|
% When creating the trans_opt file, only import the
|
|
% trans_opt files which are lower in the ordering.
|
|
trans_opt.grab_optfiles(TransOptDeps, Imports1, Imports, Error2,
|
|
!IO)
|
|
;
|
|
MaybeTransOptDeps = no,
|
|
Imports = Imports1,
|
|
Error2 = no,
|
|
module_imports_get_module_name(Imports, ModuleName),
|
|
globals.lookup_bool_option(Globals, warn_missing_trans_opt_deps,
|
|
WarnNoTransOptDeps),
|
|
(
|
|
WarnNoTransOptDeps = yes,
|
|
sym_name_to_string(ModuleName, ModuleString),
|
|
Msg1 = "Warning: cannot read trans-opt dependencies " ++
|
|
"for module `" ++ ModuleString ++ "'.",
|
|
Msg2 = "You need to remake the dependencies.",
|
|
write_error_pieces_plain([words(Msg1), nl, words(Msg2)], !IO),
|
|
record_warning(!IO)
|
|
;
|
|
WarnNoTransOptDeps = no
|
|
)
|
|
)
|
|
; MakeOptInt = yes ->
|
|
% If we're making the `.opt' file, then we can't
|
|
% read any `.trans_opt' files, since `.opt' files
|
|
% aren't allowed to depend on `.trans_opt' files.
|
|
Imports = Imports1,
|
|
Error2 = no
|
|
;
|
|
(
|
|
TransOpt = yes,
|
|
% If transitive optimization is enabled, but we are
|
|
% not creating the .opt or .trans opt file, then import
|
|
% the trans_opt files for all the modules that are
|
|
% imported (or used), and for all ancestor modules.
|
|
list.condense([Imports0 ^ parent_deps,
|
|
Imports0 ^ int_deps, Imports0 ^ impl_deps], TransOptFiles),
|
|
trans_opt.grab_optfiles(TransOptFiles, Imports1, Imports, Error2,
|
|
!IO)
|
|
;
|
|
TransOpt = no,
|
|
Imports = Imports1,
|
|
Error2 = no
|
|
)
|
|
),
|
|
bool.or(Error1, Error2, Error).
|
|
|
|
:- pred expand_equiv_types(module_name::in, item_list::in, bool::in, bool::in,
|
|
item_list::out, bool::out, eqv_map::out,
|
|
maybe(recompilation_info)::in, maybe(recompilation_info)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
expand_equiv_types(ModuleName, Items0, Verbose, Stats, Items, CircularTypes,
|
|
EqvMap, RecompInfo0, RecompInfo, !IO) :-
|
|
maybe_write_string(Verbose, "% Expanding equivalence types...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
equiv_type.expand_eqv_types(ModuleName, Items0, Items, CircularTypes,
|
|
EqvMap, RecompInfo0, RecompInfo, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred make_hlds(module_name::in, item_list::in, mq_info::in,
|
|
eqv_map::in, bool::in, bool::in, module_info::out,
|
|
make_hlds_qual_info::out, bool::out, bool::out, bool::out, io::di, io::uo)
|
|
is det.
|
|
|
|
make_hlds(Module, Items, MQInfo, EqvMap, Verbose, Stats, HLDS, QualInfo,
|
|
UndefTypes, UndefModes, FoundSemanticError, !IO) :-
|
|
maybe_write_string(Verbose, "% Converting parse tree to hlds...\n", !IO),
|
|
Prog = module(Module, Items),
|
|
parse_tree_to_hlds(Prog, MQInfo, EqvMap, HLDS, QualInfo,
|
|
UndefTypes, UndefModes, !IO),
|
|
module_info_get_num_errors(HLDS, NumErrors),
|
|
io.get_exit_status(Status, !IO),
|
|
(
|
|
( Status \= 0
|
|
; NumErrors > 0
|
|
)
|
|
->
|
|
FoundSemanticError = yes,
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundSemanticError = no
|
|
),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred frontend_pass(make_hlds_qual_info::in, bool::in, bool::in,
|
|
bool::in, bool::out, module_info::in, module_info::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
frontend_pass(QualInfo0, FoundUndefTypeError, FoundUndefModeError, !FoundError,
|
|
!HLDS, !DumpInfo, !IO) :-
|
|
% We can't continue after an undefined type error, since typecheck
|
|
% would get internal errors.
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
(
|
|
FoundUndefTypeError = yes,
|
|
!:FoundError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains undefined type error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundUndefTypeError = no,
|
|
maybe_write_string(Verbose, "% Checking typeclasses...\n", !IO),
|
|
check_typeclass.check_typeclasses(QualInfo0, QualInfo, !HLDS,
|
|
FoundTypeclassError, !IO),
|
|
maybe_dump_hlds(!.HLDS, 5, "typeclass", !DumpInfo, !IO),
|
|
set_module_recomp_info(QualInfo, !HLDS),
|
|
|
|
% We can't continue after a typeclass error, since typecheck
|
|
% can get internal errors.
|
|
(
|
|
FoundTypeclassError = yes,
|
|
!:FoundError = yes
|
|
;
|
|
FoundTypeclassError = no,
|
|
frontend_pass_no_type_error(FoundUndefModeError, !FoundError,
|
|
!HLDS, !DumpInfo, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred frontend_pass_no_type_error(bool::in, bool::in, bool::out,
|
|
module_info::in, module_info::out, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
frontend_pass_no_type_error(FoundUndefModeError, !FoundError, !HLDS, !DumpInfo,
|
|
!IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
globals.lookup_bool_option(Globals, intermodule_optimization, Intermod),
|
|
globals.lookup_bool_option(Globals, use_opt_files, UseOptFiles),
|
|
globals.lookup_bool_option(Globals, make_optimization_interface,
|
|
MakeOptInt),
|
|
(
|
|
( Intermod = yes
|
|
; UseOptFiles = yes
|
|
),
|
|
MakeOptInt = no
|
|
->
|
|
% Eliminate unnecessary clauses from `.opt' files,
|
|
% to speed up compilation. This must be done after
|
|
% typeclass instances have been checked, since that
|
|
% fills in which pred_ids are needed by instance decls.
|
|
maybe_write_string(Verbose, "% Eliminating dead predicates... ", !IO),
|
|
dead_pred_elim(!HLDS),
|
|
maybe_write_string(Verbose, "done.\n", !IO),
|
|
maybe_dump_hlds(!.HLDS, 10, "dead_pred_elim", !DumpInfo, !IO)
|
|
;
|
|
true
|
|
),
|
|
|
|
%
|
|
% Next typecheck the clauses.
|
|
%
|
|
maybe_write_string(Verbose, "% Type-checking...\n", !IO),
|
|
typecheck(!HLDS, FoundTypeError, ExceededTypeCheckIterationLimit, !IO),
|
|
(
|
|
FoundTypeError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains type error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundTypeError = no,
|
|
maybe_write_string(Verbose, "% Program is type-correct.\n", !IO)
|
|
),
|
|
maybe_dump_hlds(!.HLDS, 15, "typecheck", !DumpInfo, !IO),
|
|
|
|
% We can't continue after an undefined inst/mode error, since
|
|
% propagate_types_into_proc_modes (in post_typecheck.m -- called by
|
|
% purity.m) and mode analysis would get internal errors. Also mode analysis
|
|
% can loop if there are cyclic insts/modes.
|
|
%
|
|
% We can't continue if the type inference iteration limit was exceeeded
|
|
% because the code to resolve overloading in post_typecheck.m (called by
|
|
% purity.m) could abort.
|
|
( FoundUndefModeError = yes ->
|
|
!:FoundError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains undefined inst " ++
|
|
"or undefined mode error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
; ExceededTypeCheckIterationLimit = yes ->
|
|
% FoundTypeError will always be true here, so we've already
|
|
% printed a message about the program containing type errors.
|
|
!:FoundError = yes,
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
puritycheck(Verbose, Stats, !HLDS, FoundTypeError,
|
|
FoundPostTypecheckError, !IO),
|
|
maybe_dump_hlds(!.HLDS, 20, "puritycheck", !DumpInfo, !IO),
|
|
|
|
!:FoundError = !.FoundError `or` FoundTypeError,
|
|
|
|
%
|
|
% Stop here if `--typecheck-only' was specified.
|
|
%
|
|
globals.lookup_bool_option(Globals, typecheck_only, TypecheckOnly),
|
|
(
|
|
TypecheckOnly = yes
|
|
->
|
|
true
|
|
;
|
|
( FoundTypeError = yes
|
|
; FoundPostTypecheckError = yes
|
|
)
|
|
->
|
|
% XXX It would be nice if we could go on and mode-check the
|
|
% predicates which didn't have type errors, but we need to run
|
|
% polymorphism before running mode analysis, and currently
|
|
% polymorphism may get internal errors if any of the predicates
|
|
% are not type-correct.
|
|
|
|
!:FoundError = yes
|
|
;
|
|
% Only write out the `.opt' file if there are no errors.
|
|
(
|
|
!.FoundError = no,
|
|
FoundUndefModeError = no
|
|
->
|
|
maybe_write_optfile(MakeOptInt, !HLDS, !DumpInfo, !IO)
|
|
;
|
|
true
|
|
),
|
|
% If our job was to write out the `.opt' file, then we're done.
|
|
(
|
|
MakeOptInt = yes
|
|
;
|
|
MakeOptInt = no,
|
|
% Now go ahead and do the rest of mode checking
|
|
% and determinism analysis.
|
|
frontend_pass_by_phases(!HLDS, FoundModeOrDetError, !DumpInfo,
|
|
!IO),
|
|
!:FoundError = !.FoundError `or` FoundModeOrDetError
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred maybe_write_optfile(bool::in, module_info::in, module_info::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_write_optfile(MakeOptInt, !HLDS, !DumpInfo, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, intermodule_optimization, Intermod),
|
|
globals.lookup_bool_option(Globals, intermod_unused_args, IntermodArgs),
|
|
globals.lookup_accumulating_option(Globals, intermod_directories,
|
|
IntermodDirs),
|
|
globals.lookup_bool_option(Globals, use_opt_files, UseOptFiles),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
globals.lookup_bool_option(Globals, termination, Termination),
|
|
globals.lookup_bool_option(Globals, termination2, Termination2),
|
|
globals.lookup_bool_option(Globals, structure_sharing_analysis,
|
|
SharingAnalysis),
|
|
globals.lookup_bool_option(Globals, structure_reuse_analysis,
|
|
ReuseAnalysis),
|
|
globals.lookup_bool_option(Globals, analyse_exceptions,
|
|
ExceptionAnalysis),
|
|
globals.lookup_bool_option(Globals, analyse_closures,
|
|
ClosureAnalysis),
|
|
globals.lookup_bool_option(Globals, analyse_trail_usage,
|
|
TrailingAnalysis),
|
|
(
|
|
MakeOptInt = yes,
|
|
intermod.write_optfile(!HLDS, !IO),
|
|
|
|
% If intermod_unused_args is being performed, run polymorphism,
|
|
% mode analysis and determinism analysis, then run unused_args
|
|
% to append the unused argument information to the `.opt.tmp'
|
|
% file written above.
|
|
(
|
|
( IntermodArgs = yes
|
|
; Termination = yes
|
|
; Termination2 = yes
|
|
; ExceptionAnalysis = yes
|
|
; TrailingAnalysis = yes
|
|
; SharingAnalysis = yes
|
|
; ReuseAnalysis = yes
|
|
)
|
|
->
|
|
frontend_pass_by_phases(!HLDS, FoundModeError, !DumpInfo, !IO),
|
|
( FoundModeError = no ->
|
|
(
|
|
% Closure analysis assumes that lambda expressions have
|
|
% been converted into separate predicates.
|
|
ClosureAnalysis = yes,
|
|
mercury_compile.process_lambdas(Verbose, Stats,
|
|
!HLDS, !IO),
|
|
mercury_compile.maybe_closure_analysis(Verbose, Stats,
|
|
!HLDS, !IO)
|
|
;
|
|
ClosureAnalysis = no
|
|
),
|
|
(
|
|
ExceptionAnalysis = yes,
|
|
maybe_exception_analysis(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
ExceptionAnalysis = no
|
|
),
|
|
(
|
|
IntermodArgs = yes,
|
|
maybe_unused_args(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
IntermodArgs = no
|
|
),
|
|
(
|
|
Termination = yes,
|
|
maybe_termination(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
Termination = no
|
|
),
|
|
(
|
|
Termination2 = yes,
|
|
maybe_termination2(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
Termination2 = no
|
|
),
|
|
(
|
|
SharingAnalysis = yes,
|
|
maybe_structure_sharing_analysis(Verbose, Stats,
|
|
!HLDS, !IO)
|
|
;
|
|
SharingAnalysis = no
|
|
),
|
|
(
|
|
ReuseAnalysis = yes,
|
|
maybe_structure_reuse_analysis(Verbose, Stats,
|
|
!HLDS, !IO)
|
|
;
|
|
ReuseAnalysis = no
|
|
),
|
|
(
|
|
TrailingAnalysis = yes,
|
|
maybe_analyse_trail_usage(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
TrailingAnalysis = no
|
|
)
|
|
;
|
|
io.set_exit_status(1, !IO)
|
|
)
|
|
;
|
|
true
|
|
),
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(ModuleName, ".opt", yes, OptName, !IO),
|
|
update_interface(OptName, !IO),
|
|
touch_interface_datestamp(ModuleName, ".optdate", !IO)
|
|
;
|
|
MakeOptInt = no,
|
|
% If there is a `.opt' file for this module the import
|
|
% status of items in the `.opt' file needs to be updated.
|
|
( Intermod = yes ->
|
|
UpdateStatus = yes
|
|
; UseOptFiles = yes ->
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_search_file_name(ModuleName, ".opt", OptName, !IO),
|
|
search_for_file(IntermodDirs, OptName, Found, !IO),
|
|
( Found = ok(_) ->
|
|
UpdateStatus = yes,
|
|
io.seen(!IO)
|
|
;
|
|
UpdateStatus = no
|
|
)
|
|
;
|
|
UpdateStatus = no
|
|
),
|
|
(
|
|
UpdateStatus = yes,
|
|
intermod.adjust_pred_import_status(!HLDS, !IO)
|
|
;
|
|
UpdateStatus = no
|
|
)
|
|
).
|
|
|
|
:- pred output_trans_opt_file(module_info::in, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
output_trans_opt_file(!.HLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
globals.io_lookup_bool_option(analyse_closures, ClosureAnalysis, !IO),
|
|
%
|
|
% Closure analysis assumes that lambda expressions have
|
|
% been converted into separate predicates.
|
|
%
|
|
(
|
|
ClosureAnalysis = yes,
|
|
process_lambdas(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
ClosureAnalysis = no
|
|
),
|
|
maybe_dump_hlds(!.HLDS, 110, "lambda", !DumpInfo, !IO),
|
|
maybe_closure_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 117, "closure_analysis", !DumpInfo, !IO),
|
|
maybe_exception_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 118, "exception_analysis", !DumpInfo, !IO),
|
|
maybe_termination(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 120, "termination", !DumpInfo, !IO),
|
|
maybe_termination2(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 121, "termination_2", !DumpInfo, !IO),
|
|
maybe_analyse_trail_usage(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 167, "trail_usage", !DumpInfo, !IO),
|
|
maybe_structure_sharing_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 193, "structure_sharing", !DumpInfo, !IO),
|
|
maybe_structure_reuse_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 194, "structure_reuse", !DumpInfo, !IO),
|
|
trans_opt.write_optfile(!.HLDS, !IO).
|
|
|
|
:- pred output_analysis_file(module_name::in,
|
|
module_info::in, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
output_analysis_file(ModuleName, !.HLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
globals.io_lookup_bool_option(analyse_closures, ClosureAnalysis, !IO),
|
|
%
|
|
% Closure analysis assumes that lambda expressions have
|
|
% been converted into separate predicates.
|
|
%
|
|
(
|
|
ClosureAnalysis = yes,
|
|
process_lambdas(Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
ClosureAnalysis = no
|
|
),
|
|
maybe_dump_hlds(!.HLDS, 110, "lambda", !DumpInfo, !IO),
|
|
maybe_closure_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 117, "closure_analysis", !DumpInfo, !IO),
|
|
maybe_exception_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 118, "exception_analysis", !DumpInfo, !IO),
|
|
maybe_termination(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 120, "termination", !DumpInfo, !IO),
|
|
maybe_termination2(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 121, "termination_2", !DumpInfo, !IO),
|
|
maybe_unused_args(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 165, "unused_args", !DumpInfo, !IO),
|
|
maybe_analyse_trail_usage(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 167, "trail_usage", !DumpInfo, !IO),
|
|
|
|
module_info_get_analysis_info(!.HLDS, AnalysisInfo),
|
|
module_info_get_all_deps(!.HLDS, ImportedModules),
|
|
ModuleId = module_name_to_module_id(ModuleName),
|
|
ImportedModuleIds = set.map(module_name_to_module_id,
|
|
ImportedModules),
|
|
analysis.write_analysis_files(mmc, ModuleId, ImportedModuleIds,
|
|
AnalysisInfo, _AnalysisInfo, !IO).
|
|
|
|
:- pred frontend_pass_by_phases(module_info::in, module_info::out,
|
|
bool::out, dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
frontend_pass_by_phases(!HLDS, FoundError, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_polymorphism(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 30, "polymorphism", !DumpInfo, !IO),
|
|
|
|
maybe_mode_constraints(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 33, "mode_constraints", !DumpInfo, !IO),
|
|
|
|
modecheck(Verbose, Stats, !HLDS, FoundModeError, UnsafeToContinue, !IO),
|
|
maybe_dump_hlds(!.HLDS, 35, "modecheck", !DumpInfo, !IO),
|
|
|
|
(
|
|
UnsafeToContinue = yes,
|
|
FoundError = yes
|
|
;
|
|
UnsafeToContinue = no,
|
|
detect_switches(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 40, "switch_detect", !DumpInfo, !IO),
|
|
|
|
detect_cse(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 45, "cse", !DumpInfo, !IO),
|
|
|
|
check_determinism(Verbose, Stats, !HLDS, FoundDetError, !IO),
|
|
maybe_dump_hlds(!.HLDS, 50, "determinism", !DumpInfo, !IO),
|
|
|
|
check_unique_modes(Verbose, Stats, !HLDS, FoundUniqError, !IO),
|
|
maybe_dump_hlds(!.HLDS, 55, "unique_modes", !DumpInfo, !IO),
|
|
|
|
check_stratification(Verbose, Stats, !HLDS, FoundStratError, !IO),
|
|
maybe_dump_hlds(!.HLDS, 60, "stratification", !DumpInfo, !IO),
|
|
|
|
simplify(yes, frontend, Verbose, Stats, process_all_nonimported_procs,
|
|
!HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 65, "frontend_simplify", !DumpInfo, !IO),
|
|
|
|
% Work out whether we encountered any errors.
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
(
|
|
FoundModeError = no,
|
|
FoundDetError = no,
|
|
FoundUniqError = no,
|
|
FoundStratError = no,
|
|
% Strictly speaking, we shouldn't need to check the exit status.
|
|
% But the values returned for FoundModeError etc. aren't always
|
|
% correct.
|
|
ExitStatus = 0
|
|
->
|
|
FoundError = no
|
|
;
|
|
FoundError = yes
|
|
)
|
|
),
|
|
maybe_dump_hlds(!.HLDS, 99, "front_end", !DumpInfo, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred middle_pass(module_name::in, module_info::in, module_info::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
middle_pass(ModuleName, !HLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_read_experimental_complexity_file(!HLDS, !IO),
|
|
|
|
tabling(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 105, "tabling", !DumpInfo, !IO),
|
|
|
|
process_lambdas(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 110, "lambda", !DumpInfo, !IO),
|
|
|
|
expand_equiv_types_hlds(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 115, "equiv_types", !DumpInfo, !IO),
|
|
|
|
maybe_closure_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 117, "closure_analysis", !DumpInfo, !IO),
|
|
|
|
% Uncomment the following code to check that unique mode analysis works
|
|
% after simplification has been run. Currently it does not because common.m
|
|
% does not preserve unique mode correctness (this test fails on about
|
|
% five modules in the compiler and library). It is important that unique
|
|
% mode analysis work most of the time after optimizations because
|
|
% deforestation reruns it.
|
|
%
|
|
|
|
% check_unique_modes(Verbose, Stats, !HLDS,
|
|
% FoundUniqError, !IO),
|
|
% ( FoundUniqError = yes ->
|
|
% error("unique modes failed")
|
|
% ;
|
|
% true
|
|
% ),
|
|
|
|
% Exception analysis and termination analysis need to come before any
|
|
% optimization passes that could benefit from the information that
|
|
% they provide.
|
|
%
|
|
maybe_exception_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 118, "exception_analysis", !DumpInfo, !IO),
|
|
|
|
maybe_termination(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 120, "termination", !DumpInfo, !IO),
|
|
|
|
maybe_termination2(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 121, "termination2", !DumpInfo, !IO),
|
|
|
|
maybe_type_ctor_infos(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 125, "type_ctor_infos", !DumpInfo, !IO),
|
|
|
|
% warn_dead_procs must come after type_ctor_infos, so that it handles
|
|
% unification & comparison procedures correctly, but it must also come
|
|
% before optimizations such as higher-order specialization and inlining,
|
|
% which can make the original code for a procedure dead by
|
|
% inlining/specializing all uses of it.
|
|
maybe_warn_dead_procs(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 130, "warn_dead_procs", !DumpInfo, !IO),
|
|
|
|
maybe_bytecodes(!.HLDS, ModuleName, Verbose, Stats, !DumpInfo, !IO),
|
|
% stage number 31 is used by maybe_bytecodes
|
|
|
|
maybe_untuple_arguments(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 133, "untupling", !DumpInfo, !IO),
|
|
|
|
maybe_tuple_arguments(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 134, "tupling", !DumpInfo, !IO),
|
|
|
|
maybe_higher_order(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 135, "higher_order", !DumpInfo, !IO),
|
|
|
|
maybe_introduce_accumulators(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 140, "accum", !DumpInfo, !IO),
|
|
|
|
maybe_do_inlining(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 145, "inlining", !DumpInfo, !IO),
|
|
|
|
% Hoisting loop invariants first invokes pass 148, "mark_static".
|
|
% "mark_static" is also run at stage 420.
|
|
%
|
|
maybe_loop_inv(Verbose, Stats, !HLDS, !DumpInfo, !IO),
|
|
maybe_dump_hlds(!.HLDS, 150, "loop_inv", !DumpInfo, !IO),
|
|
|
|
maybe_deforestation(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 155, "deforestation", !DumpInfo, !IO),
|
|
|
|
maybe_delay_construct(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 160, "delay_construct", !DumpInfo, !IO),
|
|
|
|
maybe_unused_args(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 165, "unused_args", !DumpInfo, !IO),
|
|
|
|
maybe_analyse_trail_usage(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 167, "trail_usage", !DumpInfo, !IO),
|
|
|
|
maybe_unneeded_code(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 170, "unneeded_code", !DumpInfo, !IO),
|
|
|
|
maybe_lco(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 175, "lco", !DumpInfo, !IO),
|
|
|
|
maybe_eliminate_dead_procs(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 192, "dead_procs", !DumpInfo, !IO),
|
|
|
|
maybe_structure_sharing_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 193, "structure_sharing", !DumpInfo, !IO),
|
|
|
|
maybe_structure_reuse_analysis(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 194, "structure_reuse", !DumpInfo, !IO),
|
|
|
|
% If we are compiling in a deep profiling grade then now rerun simplify.
|
|
% The reason for doing this now is that we want to take advantage of any
|
|
% opportunities the other optimizations have provided for constant
|
|
% propagation and we cannot do that once the term-size profiling or deep
|
|
% profiling transformations have been applied.
|
|
%
|
|
simplify(no, pre_prof_transforms, Verbose, Stats,
|
|
process_all_nonimported_procs, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 197, "pre_prof_transform_simplify", !DumpInfo,
|
|
!IO),
|
|
|
|
% The term size profiling transformation should be after all
|
|
% transformations that construct terms of non-zero size. (Deep profiling
|
|
% does not construct non-zero size terms.)
|
|
maybe_term_size_prof(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 200, "term_size_prof", !DumpInfo, !IO),
|
|
|
|
% Deep profiling transformation should be done late in the piece
|
|
% since it munges the code a fair amount and introduces strange
|
|
% disjunctions that might confuse other hlds->hlds transformations.
|
|
maybe_deep_profiling(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 205, "deep_profiling", !DumpInfo, !IO),
|
|
|
|
% Experimental complexity transformation should be done late in the
|
|
% piece for the same reason as deep profiling. At the moment, they are
|
|
% exclusive.
|
|
maybe_experimental_complexity(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 210, "complexity", !DumpInfo, !IO),
|
|
|
|
maybe_dump_hlds(!.HLDS, 299, "middle_pass", !DumpInfo, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred backend_pass(module_info::in, module_info::out,
|
|
global_data::out, list(c_procedure)::out, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
backend_pass(!HLDS, GlobalData, LLDS, !DumpInfo, !IO) :-
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
globals.io_lookup_bool_option(unboxed_float, UnboxFloat, !IO),
|
|
globals.io_lookup_bool_option(common_data, DoCommonData, !IO),
|
|
StaticCellInfo0 = init_static_cell_info(ModuleName, UnboxFloat,
|
|
DoCommonData),
|
|
global_data_init(StaticCellInfo0, GlobalData0),
|
|
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
% map_args_to_regs affects the interface to a predicate,
|
|
% so it must be done in one phase immediately before code generation
|
|
|
|
map_args_to_regs(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 305, "args_to_regs", !DumpInfo, !IO),
|
|
|
|
globals.io_lookup_bool_option(trad_passes, TradPasses, !IO),
|
|
(
|
|
TradPasses = no,
|
|
backend_pass_by_phases(!HLDS, GlobalData0, GlobalData, LLDS, !DumpInfo,
|
|
!IO)
|
|
;
|
|
TradPasses = yes,
|
|
backend_pass_by_preds(!HLDS, GlobalData0, GlobalData, LLDS, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred backend_pass_by_phases(module_info::in, module_info::out,
|
|
global_data::in, global_data::out, list(c_procedure)::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
backend_pass_by_phases(!HLDS, !GlobalData, LLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_saved_vars(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 310, "saved_vars_const", !DumpInfo, !IO),
|
|
|
|
maybe_stack_opt(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 315, "saved_vars_cell", !DumpInfo, !IO),
|
|
|
|
maybe_followcode(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 320, "followcode", !DumpInfo, !IO),
|
|
|
|
simplify(no, ll_backend, Verbose, Stats, process_all_nonimported_procs,
|
|
!HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 325, "ll_backend_simplify", !DumpInfo, !IO),
|
|
|
|
compute_liveness(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 330, "liveness", !DumpInfo, !IO),
|
|
|
|
compute_stack_vars(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 335, "stackvars", !DumpInfo, !IO),
|
|
|
|
allocate_store_map(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 340, "store_map", !DumpInfo, !IO),
|
|
|
|
maybe_goal_paths(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 345, "precodegen", !DumpInfo, !IO),
|
|
|
|
generate_code(!.HLDS, Verbose, Stats, !GlobalData, LLDS1, !IO),
|
|
|
|
maybe_generate_stack_layouts(!.HLDS, LLDS1, Verbose, Stats, !GlobalData,
|
|
!IO),
|
|
% maybe_dump_global_data(!.GlobalData, !IO),
|
|
|
|
maybe_do_optimize(!.GlobalData, Verbose, Stats, LLDS1, LLDS, !IO).
|
|
|
|
:- pred backend_pass_by_preds(module_info::in, module_info::out,
|
|
global_data::in, global_data::out, list(c_procedure)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
backend_pass_by_preds(!HLDS, !GlobalData, LLDS, !IO) :-
|
|
module_info_predids(!.HLDS, PredIds),
|
|
globals.io_lookup_bool_option(optimize_proc_dups, ProcDups, !IO),
|
|
(
|
|
ProcDups = no,
|
|
OrderedPredIds = PredIds,
|
|
MaybeDupProcMap = no
|
|
;
|
|
ProcDups = yes,
|
|
dependency_graph.build_pred_dependency_graph(!.HLDS,
|
|
do_not_include_imported, DepInfo),
|
|
hlds_dependency_info_get_dependency_ordering(DepInfo, PredSCCs),
|
|
list.condense(PredSCCs, OrderedPredIds),
|
|
MaybeDupProcMap = yes(map.init)
|
|
),
|
|
backend_pass_by_preds_2(OrderedPredIds, !HLDS, !GlobalData,
|
|
MaybeDupProcMap, LLDS, !IO).
|
|
|
|
:- pred backend_pass_by_preds_2(list(pred_id)::in,
|
|
module_info::in, module_info::out, global_data::in, global_data::out,
|
|
maybe(map(mdbcomp.prim_data.proc_label,
|
|
mdbcomp.prim_data.proc_label))::in,
|
|
list(c_procedure)::out, io::di, io::uo) is det.
|
|
|
|
backend_pass_by_preds_2([], !HLDS, !GlobalData, _, [], !IO).
|
|
backend_pass_by_preds_2([PredId | PredIds], !HLDS,
|
|
!GlobalData, !.MaybeDupProcMap, Code, !IO) :-
|
|
module_info_preds(!.HLDS, PredTable),
|
|
map.lookup(PredTable, PredId, PredInfo),
|
|
ProcIds = pred_info_non_imported_procids(PredInfo),
|
|
(
|
|
ProcIds = [],
|
|
ProcList = []
|
|
;
|
|
ProcIds = [_ | _],
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
(
|
|
Verbose = yes,
|
|
io.write_string("% Generating code for ", !IO),
|
|
hlds_out.write_pred_id(!.HLDS, PredId, !IO),
|
|
io.write_string("\n", !IO)
|
|
;
|
|
Verbose = no
|
|
),
|
|
(
|
|
PredModule = pred_info_module(PredInfo),
|
|
PredName = pred_info_name(PredInfo),
|
|
PredArity = pred_info_orig_arity(PredInfo),
|
|
no_type_info_builtin(PredModule, PredName, PredArity)
|
|
->
|
|
% These predicates should never be traced, since they do not obey
|
|
% typeinfo_liveness. Since they may be opt_imported into other
|
|
% modules, we must switch off the tracing of such preds on a
|
|
% pred-by-pred basis; module-by-module wouldn't work.
|
|
module_info_get_globals(!.HLDS, Globals0),
|
|
globals.get_trace_level(Globals0, TraceLevel),
|
|
globals.set_trace_level_none(Globals0, Globals1),
|
|
module_info_set_globals(Globals1, !HLDS),
|
|
copy(Globals1, Globals1Unique),
|
|
globals.io_set_globals(Globals1Unique, !IO),
|
|
backend_pass_by_preds_3(ProcIds, PredId, PredInfo, !HLDS,
|
|
!GlobalData, IdProcList, !IO),
|
|
module_info_get_globals(!.HLDS, Globals2),
|
|
globals.set_trace_level(TraceLevel, Globals2, Globals),
|
|
module_info_set_globals(Globals, !HLDS),
|
|
copy(Globals, GlobalsUnique),
|
|
globals.io_set_globals(GlobalsUnique, !IO)
|
|
;
|
|
backend_pass_by_preds_3(ProcIds, PredId, PredInfo, !HLDS,
|
|
!GlobalData, IdProcList, !IO)
|
|
),
|
|
(
|
|
!.MaybeDupProcMap = no,
|
|
assoc_list.values(IdProcList, ProcList)
|
|
;
|
|
!.MaybeDupProcMap = yes(DupProcMap0),
|
|
eliminate_duplicate_procs(IdProcList, ProcList,
|
|
DupProcMap0, DupProcMap),
|
|
!:MaybeDupProcMap = yes(DupProcMap)
|
|
)
|
|
),
|
|
backend_pass_by_preds_2(PredIds, !HLDS, !GlobalData, !.MaybeDupProcMap,
|
|
TailPredsCode, !IO),
|
|
list.append(ProcList, TailPredsCode, Code).
|
|
|
|
:- pred backend_pass_by_preds_3(list(proc_id)::in, pred_id::in, pred_info::in,
|
|
module_info::in, module_info::out, global_data::in, global_data::out,
|
|
assoc_list(mdbcomp.prim_data.proc_label, c_procedure)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
backend_pass_by_preds_3([], _, _, !HLDS, !GlobalData, [], !IO).
|
|
backend_pass_by_preds_3([ProcId | ProcIds], PredId, PredInfo, !HLDS,
|
|
!GlobalData, [ProcLabel - Proc | Procs], !IO) :-
|
|
ProcLabel = make_proc_label(!.HLDS, PredId, ProcId),
|
|
pred_info_get_procedures(PredInfo, ProcTable),
|
|
map.lookup(ProcTable, ProcId, ProcInfo),
|
|
backend_pass_by_preds_4(PredInfo, ProcInfo, _, ProcId, PredId, !HLDS,
|
|
!GlobalData, Proc, !IO),
|
|
backend_pass_by_preds_3(ProcIds, PredId, PredInfo, !HLDS, !GlobalData,
|
|
Procs, !IO).
|
|
|
|
:- pred backend_pass_by_preds_4(pred_info::in, proc_info::in, proc_info::out,
|
|
proc_id::in, pred_id::in, module_info::in, module_info::out,
|
|
global_data::in, global_data::out, c_procedure::out, io::di, io::uo)
|
|
is det.
|
|
|
|
backend_pass_by_preds_4(PredInfo, !ProcInfo, ProcId, PredId, !HLDS,
|
|
!GlobalData, ProcCode, !IO) :-
|
|
module_info_get_globals(!.HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, optimize_saved_vars_const,
|
|
SavedVarsConst),
|
|
(
|
|
SavedVarsConst = yes,
|
|
saved_vars_proc(PredId, ProcId, !ProcInfo, !HLDS, !IO)
|
|
;
|
|
SavedVarsConst = no
|
|
),
|
|
globals.lookup_bool_option(Globals, optimize_saved_vars_cell,
|
|
SavedVarsCell),
|
|
(
|
|
SavedVarsCell = yes,
|
|
stack_opt_cell(PredId, ProcId, !ProcInfo, !HLDS, !IO)
|
|
;
|
|
SavedVarsCell = no
|
|
),
|
|
globals.lookup_bool_option(Globals, follow_code, FollowCode),
|
|
globals.lookup_bool_option(Globals, prev_code, PrevCode),
|
|
(
|
|
( FollowCode = yes
|
|
; PrevCode = yes
|
|
)
|
|
->
|
|
move_follow_code_in_proc(PredId, ProcId, PredInfo, !ProcInfo, !HLDS)
|
|
;
|
|
true
|
|
),
|
|
find_simplifications(no, Globals, Simplifications0),
|
|
SimpList0 = simplifications_to_list(Simplifications0),
|
|
|
|
globals.lookup_bool_option(Globals, profile_deep, DeepProf),
|
|
globals.lookup_bool_option(Globals, record_term_sizes_as_words, TSWProf),
|
|
globals.lookup_bool_option(Globals, record_term_sizes_as_cells, TSCProf),
|
|
|
|
ProfTrans = bool.or_list([DeepProf, TSWProf, TSCProf]),
|
|
(
|
|
% Don't run constant propagation if any of the profiling
|
|
% transformations has been applied.
|
|
%
|
|
% NOTE: Any changes here may also need to be made to
|
|
% mercury_compile.simplify.
|
|
%
|
|
ProfTrans = yes,
|
|
SimpList1 = list.delete_all(SimpList0, constant_prop)
|
|
;
|
|
ProfTrans = no,
|
|
SimpList1 = SimpList0
|
|
),
|
|
|
|
SimpList = [do_once, elim_removable_scopes | SimpList1],
|
|
Simplifications = list_to_simplifications(SimpList),
|
|
simplify_proc(Simplifications, PredId, ProcId, !HLDS, !ProcInfo, !IO),
|
|
write_proc_progress_message("% Computing liveness in ", PredId, ProcId,
|
|
!.HLDS, !IO),
|
|
detect_liveness_proc(PredId, ProcId, !.HLDS, !ProcInfo, !IO),
|
|
write_proc_progress_message("% Allocating stack slots in ", PredId,
|
|
ProcId, !.HLDS, !IO),
|
|
allocate_stack_slots_in_proc(PredId, ProcId, !.HLDS, !ProcInfo, !IO),
|
|
write_proc_progress_message(
|
|
"% Allocating storage locations for live vars in ",
|
|
PredId, ProcId, !.HLDS, !IO),
|
|
allocate_store_maps(final_allocation, PredId, !.HLDS, !ProcInfo),
|
|
globals.io_get_trace_level(TraceLevel, !IO),
|
|
( given_trace_level_is_none(TraceLevel) = no ->
|
|
write_proc_progress_message("% Calculating goal paths in ",
|
|
PredId, ProcId, !.HLDS, !IO),
|
|
fill_goal_path_slots(!.HLDS, !ProcInfo)
|
|
;
|
|
true
|
|
),
|
|
write_proc_progress_message("% Generating low-level (LLDS) code for ",
|
|
PredId, ProcId, !.HLDS, !IO),
|
|
generate_proc_code(PredInfo, !.ProcInfo, ProcId, PredId, !.HLDS,
|
|
!GlobalData, ProcCode0),
|
|
globals.lookup_bool_option(Globals, optimize, Optimize),
|
|
(
|
|
Optimize = yes,
|
|
optimize_proc(!.GlobalData, ProcCode0, ProcCode, !IO)
|
|
;
|
|
Optimize = no,
|
|
ProcCode = ProcCode0
|
|
),
|
|
PredProcId = ProcCode ^ cproc_id,
|
|
Instructions = ProcCode ^ cproc_code,
|
|
write_proc_progress_message(
|
|
"% Generating call continuation information for ",
|
|
PredId, ProcId, !.HLDS, !IO),
|
|
continuation_info.maybe_process_proc_llds(Instructions, PredProcId,
|
|
!.HLDS, !GlobalData).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred puritycheck(bool::in, bool::in, module_info::in, module_info::out,
|
|
bool::in, bool::out, io::di, io::uo) is det.
|
|
|
|
puritycheck(Verbose, Stats, !HLDS, FoundTypeError, FoundPostTypecheckError,
|
|
!IO) :-
|
|
module_info_get_num_errors(!.HLDS, NumErrors0),
|
|
puritycheck(FoundTypeError, FoundPostTypecheckError, !HLDS, !IO),
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( NumErrors \= NumErrors0 ->
|
|
maybe_write_string(Verbose,
|
|
"% Program contains purity error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
maybe_write_string(Verbose,
|
|
"% Program is purity-correct.\n", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred modecheck(bool::in, bool::in, module_info::in, module_info::out,
|
|
bool::out, bool::out, io::di, io::uo) is det.
|
|
|
|
modecheck(Verbose, Stats, !HLDS, FoundModeError, UnsafeToContinue, !IO) :-
|
|
module_info_get_num_errors(!.HLDS, NumErrors0),
|
|
maybe_benchmark_modes(
|
|
(pred(H0::in, {H,U}::out, di, uo) is det -->
|
|
modecheck(H0, H, U)
|
|
),
|
|
"modecheck", !.HLDS, {!:HLDS, UnsafeToContinue}, !IO),
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( NumErrors \= NumErrors0 ->
|
|
FoundModeError = yes,
|
|
maybe_write_string(Verbose, "% Program contains mode error(s).\n",
|
|
!IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundModeError = no,
|
|
maybe_write_string(Verbose, "% Program is mode-correct.\n", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_mode_constraints(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_mode_constraints(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(mode_constraints, ModeConstraints, !IO),
|
|
(
|
|
ModeConstraints = yes,
|
|
maybe_write_string(Verbose, "% Dumping mode constraints...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
maybe_benchmark_modes(mode_constraints.process_module,
|
|
"mode-constraints", !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ModeConstraints = no
|
|
).
|
|
|
|
:- pred maybe_benchmark_modes(pred(T1, T2, io, io)::in(pred(in, out, di, uo)
|
|
is det), string::in, T1::in, T2::out, io::di, io::uo) is det.
|
|
|
|
maybe_benchmark_modes(Pred, Stage, A0, A, !IO) :-
|
|
globals.io_lookup_bool_option(benchmark_modes, BenchmarkModes, !IO),
|
|
(
|
|
BenchmarkModes = yes,
|
|
globals.io_lookup_int_option(benchmark_modes_repeat, Repeats, !IO),
|
|
io.format("%s %d ", [s(Stage), i(Repeats)], !IO),
|
|
promise_equivalent_solutions [A, Time, !:IO] (
|
|
do_io_benchmark(Pred, Repeats, A0, A - Time, !IO)
|
|
),
|
|
io.format("%d ms\n", [i(Time)], !IO)
|
|
;
|
|
BenchmarkModes = no,
|
|
Pred(A0, A, !IO)
|
|
).
|
|
|
|
:- pred do_io_benchmark(pred(T1, T2, io, io)::in(pred(in, out, di, uo) is det),
|
|
int::in, T1::in, pair(T2, int)::out, io::di, io::uo) is cc_multi.
|
|
|
|
do_io_benchmark(Pred, Repeats, A0, A - Time, !IO) :-
|
|
benchmark_det_io(Pred, A0, A, !IO, Repeats, Time).
|
|
|
|
:- pred detect_switches(bool::in, bool::in, module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
detect_switches(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Detecting switches...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
detect_switches(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred detect_cse(bool::in, bool::in, module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
detect_cse(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(common_goal, CommonGoal, !IO),
|
|
(
|
|
CommonGoal = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Detecting common deconstructions...\n", !IO),
|
|
detect_cse(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
CommonGoal = no
|
|
).
|
|
|
|
:- pred check_determinism(bool::in, bool::in,
|
|
module_info::in, module_info::out, bool::out, io::di, io::uo) is det.
|
|
|
|
check_determinism(Verbose, Stats, !HLDS, FoundError, !IO) :-
|
|
module_info_get_num_errors(!.HLDS, NumErrors0),
|
|
determinism_pass(!HLDS, !IO),
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( NumErrors \= NumErrors0 ->
|
|
FoundError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains determinism error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundError = no,
|
|
maybe_write_string(Verbose, "% Program is determinism-correct.\n", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred mercury_compile.maybe_closure_analysis(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
mercury_compile.maybe_closure_analysis(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(analyse_closures, ClosureAnalysis,
|
|
!IO),
|
|
(
|
|
ClosureAnalysis = yes,
|
|
maybe_write_string(Verbose, "% Analysing closures...\n", !IO),
|
|
closure_analysis.process_module(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ClosureAnalysis = no
|
|
).
|
|
|
|
:- pred mercury_compile.maybe_exception_analysis(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
mercury_compile.maybe_exception_analysis(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(analyse_exceptions, ExceptionAnalysis, !IO),
|
|
(
|
|
ExceptionAnalysis = yes,
|
|
maybe_write_string(Verbose, "% Analysing exceptions...\n", !IO),
|
|
analyse_exceptions_in_module(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ExceptionAnalysis = no
|
|
).
|
|
|
|
:- pred maybe_termination(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_termination(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, polymorphism, Polymorphism),
|
|
globals.lookup_bool_option(Globals, termination, Termination),
|
|
% Termination analysis requires polymorphism to be run,
|
|
% since it does not handle complex unification
|
|
(
|
|
Polymorphism = yes,
|
|
Termination = yes
|
|
->
|
|
maybe_write_string(Verbose, "% Detecting termination...\n", !IO),
|
|
analyse_termination_in_module(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% Termination checking done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_termination2(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_termination2(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(polymorphism, Polymorphism, !IO),
|
|
globals.io_lookup_bool_option(termination2, Termination2, !IO),
|
|
% Termination analysis requires polymorphism to be run,
|
|
% as termination analysis does not handle complex unification.
|
|
(
|
|
Polymorphism = yes,
|
|
Termination2 = yes
|
|
->
|
|
maybe_write_string(Verbose, "% Detecting termination 2...\n", !IO),
|
|
term_constr_main.pass(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% Termination 2 checking done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
:- pred maybe_analyse_trail_usage(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_analyse_trail_usage(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(analyse_trail_usage, AnalyseTrail, !IO),
|
|
(
|
|
AnalyseTrail = yes,
|
|
maybe_write_string(Verbose, "% Analysing trail usage...\n", !IO),
|
|
analyse_trail_usage(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% Trail usage analysis done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
AnalyseTrail = no
|
|
).
|
|
|
|
:- pred check_unique_modes(bool::in, bool::in,
|
|
module_info::in, module_info::out, bool::out, io::di, io::uo) is det.
|
|
|
|
check_unique_modes(Verbose, Stats, !HLDS, FoundError, !IO) :-
|
|
maybe_write_string(Verbose,
|
|
"% Checking for backtracking over unique modes...\n", !IO),
|
|
module_info_get_num_errors(!.HLDS, NumErrors0),
|
|
unique_modes.check_module(!HLDS, !IO),
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( NumErrors \= NumErrors0 ->
|
|
FoundError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains unique mode error(s).\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
FoundError = no,
|
|
maybe_write_string(Verbose, "% Program is unique-mode-correct.\n", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred check_stratification(bool::in, bool::in,
|
|
module_info::in, module_info::out, bool::out, io::di, io::uo) is det.
|
|
|
|
check_stratification(Verbose, Stats, !HLDS, FoundError,
|
|
!IO) :-
|
|
module_info_get_stratified_preds(!.HLDS, StratifiedPreds),
|
|
globals.io_lookup_bool_option(warn_non_stratification, Warn, !IO),
|
|
(
|
|
( \+ set.empty(StratifiedPreds)
|
|
; Warn = yes
|
|
)
|
|
->
|
|
maybe_write_string(Verbose,
|
|
"% Checking stratification...\n", !IO),
|
|
io.get_exit_status(OldStatus, !IO),
|
|
io.set_exit_status(0, !IO),
|
|
stratify.check_stratification(!HLDS, !IO),
|
|
io.get_exit_status(NewStatus, !IO),
|
|
( NewStatus \= 0 ->
|
|
FoundError = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Program contains stratification error(s).\n", !IO)
|
|
;
|
|
FoundError = no,
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
io.set_exit_status(OldStatus, !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
FoundError = no
|
|
).
|
|
|
|
:- pred maybe_warn_dead_procs(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_warn_dead_procs(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(warn_dead_procs, WarnDead, !IO),
|
|
(
|
|
WarnDead = yes,
|
|
maybe_write_string(Verbose, "% Warning about dead procedures...\n",
|
|
!IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
dead_proc_elim(warning_pass, !.HLDS, _HLDS1, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
|
|
% XXX The warning pass also does all the work of optimizing
|
|
% away the dead procedures. If we're optimizing, then
|
|
% it would be nice if we could keep the HLDS that results.
|
|
% However, because this pass gets run before type
|
|
% specialization, dead code elimination at this point
|
|
% incorrectly optimizes away procedures created for
|
|
% `pragma type_spec' declarations. So we can't use the
|
|
% code below. Instead we need to keep original HLDS.
|
|
%
|
|
% %%% globals.io_lookup_bool_option(optimize_dead_procs,
|
|
% %%% OptimizeDead, !IO),
|
|
% %%% ( OptimizeDead = yes ->
|
|
% %%% !:HLDS = HLDS1
|
|
% %%% ;
|
|
% %%% true
|
|
% %%% )
|
|
;
|
|
WarnDead = no
|
|
).
|
|
|
|
% This type indicates what stage of compilation we are running
|
|
% the simplification pass at. The exact simplifications we run
|
|
% will depend upon this.
|
|
%
|
|
:- type simplify_pass
|
|
---> frontend
|
|
% Immediately after the frontend passes.
|
|
|
|
; post_untuple
|
|
% After the untupling transformation has been applied.
|
|
|
|
; pre_prof_transforms
|
|
% If deep/term-size profiling is enabled then immediately
|
|
% before the source-to-source transformations that
|
|
% implement them.
|
|
|
|
; ml_backend
|
|
% The first stage of MLDS code generation.
|
|
|
|
; ll_backend.
|
|
% The first stage of LLDS code generation.
|
|
|
|
:- pred simplify(bool::in, simplify_pass::in, bool::in, bool::in,
|
|
pred(task, module_info, module_info, io, io)::in(pred(task, in, out,
|
|
di, uo) is det),
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
simplify(Warn, SimplifyPass, Verbose, Stats, Process, !HLDS, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, profile_deep, DeepProf),
|
|
globals.lookup_bool_option(Globals, record_term_sizes_as_words, TSWProf),
|
|
globals.lookup_bool_option(Globals, record_term_sizes_as_cells, TSCProf),
|
|
%
|
|
% We run the simplify pass before the profiling transformations,
|
|
% only if those transformations are being applied - otherwise we
|
|
% just leave things to the backend simplification passes.
|
|
%
|
|
IsProfPass = bool.or_list([DeepProf, TSWProf, TSCProf]),
|
|
(
|
|
SimplifyPass = pre_prof_transforms,
|
|
IsProfPass = no
|
|
->
|
|
true
|
|
;
|
|
maybe_write_string(Verbose, "% Simplifying goals...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
|
|
some [!SimpList] (
|
|
simplify.find_simplifications(Warn, Globals, Simplifications0),
|
|
!:SimpList = simplifications_to_list(Simplifications0),
|
|
(
|
|
SimplifyPass = frontend
|
|
;
|
|
SimplifyPass = post_untuple,
|
|
list.cons(do_once, !SimpList)
|
|
;
|
|
SimplifyPass = pre_prof_transforms,
|
|
list.cons(do_once, !SimpList)
|
|
;
|
|
SimplifyPass = ml_backend,
|
|
list.cons(do_once, !SimpList)
|
|
;
|
|
% Don't perform constant propagation if one of the
|
|
% profiling transformations has been applied.
|
|
%
|
|
% NOTE: Any changes made here may also need to be made
|
|
% to the relevant parts of backend_pass_by_preds_4/12.
|
|
%
|
|
SimplifyPass = ll_backend,
|
|
(
|
|
IsProfPass = yes,
|
|
list.delete_all(!.SimpList, constant_prop, !:SimpList)
|
|
;
|
|
IsProfPass = no
|
|
),
|
|
list.cons(do_once, !SimpList),
|
|
list.cons(elim_removable_scopes, !SimpList)
|
|
),
|
|
Simplifications = list_to_simplifications(!.SimpList)
|
|
),
|
|
|
|
Process(update_pred_error(simplify_pred(Simplifications)), !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_mark_static_terms(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_mark_static_terms(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(static_ground_terms, StaticGroundTerms,
|
|
!IO),
|
|
(
|
|
StaticGroundTerms = yes,
|
|
maybe_write_string(Verbose, "% Marking static ground terms...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_proc(mark_static_terms),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
StaticGroundTerms = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_add_trail_ops(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_add_trail_ops(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(use_trail, UseTrail, !IO),
|
|
(
|
|
UseTrail = no,
|
|
EmitTrailOps = no
|
|
;
|
|
UseTrail = yes,
|
|
globals.io_lookup_bool_option(disable_trail_ops, DisableTrailOps, !IO),
|
|
(
|
|
DisableTrailOps = yes,
|
|
EmitTrailOps = no
|
|
;
|
|
DisableTrailOps = no,
|
|
EmitTrailOps = yes
|
|
)
|
|
),
|
|
(
|
|
EmitTrailOps = yes,
|
|
globals.io_lookup_bool_option(optimize_trail_usage, OptTrailUse, !IO),
|
|
globals.io_get_target(Target, !IO),
|
|
(
|
|
Target = c,
|
|
globals.io_lookup_bool_option(generate_trail_ops_inline,
|
|
GenerateInline, !IO)
|
|
;
|
|
% XXX Currently, we can only generate trail ops inline for
|
|
% the C backends.
|
|
%
|
|
( Target = il
|
|
; Target = java
|
|
; Target = asm
|
|
),
|
|
GenerateInline = no
|
|
),
|
|
maybe_write_string(Verbose, "% Adding trailing operations...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_proc(add_trail_ops(OptTrailUse, GenerateInline)),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
EmitTrailOps = no
|
|
).
|
|
|
|
:- pred maybe_add_heap_ops(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_add_heap_ops(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_get_gc_method(GC, !IO),
|
|
globals.io_lookup_bool_option(reclaim_heap_on_semidet_failure,
|
|
SemidetReclaim, !IO),
|
|
globals.io_lookup_bool_option(reclaim_heap_on_nondet_failure,
|
|
NondetReclaim, !IO),
|
|
(
|
|
gc_is_conservative(GC) = yes
|
|
->
|
|
% we can't do heap reclamation with conservative GC
|
|
true
|
|
;
|
|
SemidetReclaim = no,
|
|
NondetReclaim = no
|
|
->
|
|
true
|
|
;
|
|
SemidetReclaim = yes,
|
|
NondetReclaim = yes
|
|
->
|
|
maybe_write_string(Verbose,
|
|
"% Adding heap reclamation operations...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_proc(add_heap_ops), !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Msg = "Sorry, not implemented: `--high-level-code' and just one of " ++
|
|
"`--reclaim-heap-on-semidet-failure' and " ++
|
|
"`--reclaim-heap-on-nondet-failure'. " ++
|
|
"Use `--(no-)reclaim-heap-on-failure' instead.",
|
|
write_error_pieces_plain([words(Msg)], !IO),
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_write_dependency_graph(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_write_dependency_graph(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(show_dependency_graph, ShowDepGraph, !IO),
|
|
(
|
|
ShowDepGraph = yes,
|
|
maybe_write_string(Verbose, "% Writing dependency graph...", !IO),
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(ModuleName, ".dependency_graph", yes,
|
|
FileName, !IO),
|
|
io.open_output(FileName, Res, !IO),
|
|
( Res = ok(FileStream) ->
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
dependency_graph.write_dependency_graph(!HLDS, !IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
report_error("unable to write dependency graph.", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ShowDepGraph = no
|
|
).
|
|
|
|
% Outputs the file <module_name>.prof, which contains the static
|
|
% call graph in terms of label names, if the profiling flag is enabled.
|
|
%
|
|
:- pred maybe_output_prof_call_graph(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_output_prof_call_graph(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(profile_calls, ProfileCalls, !IO),
|
|
globals.io_lookup_bool_option(profile_time, ProfileTime, !IO),
|
|
(
|
|
( ProfileCalls = yes
|
|
; ProfileTime = yes
|
|
)
|
|
->
|
|
maybe_write_string(Verbose,
|
|
"% Outputing profiling call graph...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(ModuleName, ".prof", yes, ProfFileName, !IO),
|
|
io.open_output(ProfFileName, Res, !IO),
|
|
( Res = ok(FileStream) ->
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
dependency_graph.write_prof_dependency_graph(!HLDS, !IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
report_error("unable to write profiling static call graph.", !IO)
|
|
),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred tabling(bool::in, bool::in, module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
tabling(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Transforming tabled predicates...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
table_gen.process_module(!HLDS, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred process_lambdas(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
process_lambdas(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Transforming lambda expressions...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
lambda.process_module(!HLDS),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred expand_equiv_types_hlds(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
expand_equiv_types_hlds(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Fully expanding equivalence types...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
equiv_type_hlds.replace_in_hlds(!HLDS),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_polymorphism(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_polymorphism(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(polymorphism, Polymorphism, !IO),
|
|
(
|
|
Polymorphism = yes,
|
|
globals.io_lookup_bool_option(very_verbose, VeryVerbose, !IO),
|
|
(
|
|
VeryVerbose = no,
|
|
maybe_write_string(Verbose,
|
|
"% Transforming polymorphic unifications...", !IO)
|
|
;
|
|
VeryVerbose = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Transforming polymorphic unifications...\n", !IO)
|
|
),
|
|
maybe_flush_output(Verbose, !IO),
|
|
polymorphism.process_module(!HLDS, !IO),
|
|
(
|
|
VeryVerbose = no,
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
VeryVerbose = yes,
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Polymorphism = no,
|
|
% The --no-polymorphism option really doesn't make much
|
|
% sense anymore, because the polymorphism pass is necessary
|
|
% for the proper mode analysis of code using existential
|
|
% types.
|
|
unexpected(this_file,
|
|
"sorry, `--no-polymorphism' is no longer supported")
|
|
).
|
|
|
|
:- pred maybe_type_ctor_infos(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_type_ctor_infos(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(type_ctor_info, TypeCtorInfo, !IO),
|
|
(
|
|
TypeCtorInfo = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Generating type_ctor_info structures...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
type_ctor_info.generate_hlds(!HLDS),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
TypeCtorInfo = no
|
|
).
|
|
|
|
:- pred maybe_bytecodes(module_info::in, module_name::in,
|
|
bool::in, bool::in, dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_bytecodes(HLDS0, ModuleName, Verbose, Stats, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(generate_bytecode, GenBytecode, !IO),
|
|
(
|
|
GenBytecode = yes,
|
|
map_args_to_regs(Verbose, Stats, HLDS0, HLDS1, !IO),
|
|
maybe_dump_hlds(HLDS1, 505, "bytecode_args_to_regs", !DumpInfo, !IO),
|
|
maybe_write_string(Verbose, "% Generating bytecodes...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
bytecode_gen.gen_module(HLDS1, Bytecode, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
module_name_to_file_name(ModuleName, ".bytedebug", yes, BytedebugFile,
|
|
!IO),
|
|
maybe_write_string(Verbose, "% Writing bytecodes to `", !IO),
|
|
maybe_write_string(Verbose, BytedebugFile, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
debug_bytecode_file(BytedebugFile, Bytecode, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
module_name_to_file_name(ModuleName, ".mbc", yes, BytecodeFile, !IO),
|
|
maybe_write_string(Verbose, "% Writing bytecodes to `", !IO),
|
|
maybe_write_string(Verbose, BytecodeFile, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
output_bytecode_file(BytecodeFile, Bytecode, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
GenBytecode = no
|
|
).
|
|
|
|
:- pred maybe_untuple_arguments(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_untuple_arguments(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(untuple, Untuple, !IO),
|
|
(
|
|
Untuple = yes,
|
|
maybe_write_string(Verbose, "% Untupling...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
untuple_arguments(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
simplify(no, post_untuple, Verbose, Stats,
|
|
process_all_nonimported_procs, !HLDS, !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Untuple = no
|
|
).
|
|
|
|
:- pred maybe_tuple_arguments(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_tuple_arguments(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(tuple, Tuple, !IO),
|
|
(
|
|
Tuple = yes,
|
|
maybe_write_string(Verbose, "% Tupling...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
tuple_arguments(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Tuple = no
|
|
).
|
|
|
|
:- pred maybe_higher_order(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_higher_order(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize_higher_order, HigherOrder, !IO),
|
|
% --type-specialization implies --user-guided-type-specialization.
|
|
globals.io_lookup_bool_option(user_guided_type_specialization, Types,
|
|
!IO),
|
|
|
|
% Always produce the specialized versions for which
|
|
% `:- pragma type_spec' declarations exist, because
|
|
% importing modules might call them.
|
|
module_info_get_type_spec_info(!.HLDS, TypeSpecInfo),
|
|
TypeSpecInfo = type_spec_info(TypeSpecPreds, _, _, _),
|
|
(
|
|
( HigherOrder = yes
|
|
; Types = yes
|
|
; \+ set.empty(TypeSpecPreds)
|
|
)
|
|
->
|
|
maybe_write_string(Verbose,
|
|
"% Specializing higher-order and polymorphic predicates...\n",
|
|
!IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
|
|
specialize_higher_order(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_do_inlining(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_do_inlining(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(allow_inlining, Allow, !IO),
|
|
globals.io_lookup_bool_option(inline_simple, Simple, !IO),
|
|
globals.io_lookup_bool_option(inline_single_use, SingleUse, !IO),
|
|
globals.io_lookup_int_option(inline_compound_threshold, Threshold, !IO),
|
|
(
|
|
Allow = yes,
|
|
( Simple = yes
|
|
; SingleUse = yes
|
|
; Threshold > 0
|
|
)
|
|
->
|
|
maybe_write_string(Verbose, "% Inlining...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
inlining(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_deforestation(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_deforestation(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(deforestation, Deforest, !IO),
|
|
|
|
% --constraint-propagation implies --local-constraint-propagation.
|
|
globals.io_lookup_bool_option(local_constraint_propagation, Constraints,
|
|
!IO),
|
|
(
|
|
( Deforest = yes
|
|
; Constraints = yes
|
|
)
|
|
->
|
|
(
|
|
Deforest = no,
|
|
Constraints = no,
|
|
unexpected(this_file, "maybe_deforestation")
|
|
;
|
|
Deforest = yes,
|
|
Constraints = yes,
|
|
Msg = "% Deforestation and constraint propagation...\n"
|
|
;
|
|
Deforest = yes,
|
|
Constraints = no,
|
|
Msg = "% Deforestation...\n"
|
|
;
|
|
Deforest = no,
|
|
Constraints = yes,
|
|
Msg = "% Constraint propagation...\n"
|
|
),
|
|
maybe_write_string(Verbose, Msg, !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
deforestation(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_loop_inv(bool::in, bool::in,
|
|
module_info::in, module_info::out, dump_info::in, dump_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_loop_inv(Verbose, Stats, !HLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(loop_invariants, LoopInv, !IO),
|
|
(
|
|
LoopInv = yes,
|
|
% We run the mark_static pass because we need the construct_how flag
|
|
% to be valid.
|
|
%
|
|
maybe_mark_static_terms(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 148, "mark_static", !DumpInfo, !IO),
|
|
|
|
maybe_write_string(Verbose, "% Hoisting loop invariants...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_module(hoist_loop_invariants),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
LoopInv = no
|
|
).
|
|
|
|
:- pred maybe_delay_construct(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_delay_construct(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(delay_construct, DelayConstruct, !IO),
|
|
(
|
|
DelayConstruct = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Delaying construction unifications ...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_proc_io(delay_construct_proc),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
DelayConstruct = no
|
|
).
|
|
|
|
:- pred maybe_unused_args(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_unused_args(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_get_globals(Globals, !IO),
|
|
globals.lookup_bool_option(Globals, intermod_unused_args, Intermod),
|
|
globals.lookup_bool_option(Globals, optimize_unused_args, Optimize),
|
|
globals.lookup_bool_option(Globals, warn_unused_args, Warn),
|
|
(
|
|
( Optimize = yes
|
|
; Warn = yes
|
|
; Intermod = yes
|
|
)
|
|
->
|
|
maybe_write_string(Verbose, "% Finding unused arguments ...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
unused_args.process_module(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_unneeded_code(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_unneeded_code(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(unneeded_code, UnneededCode, !IO),
|
|
(
|
|
UnneededCode = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Removing unneeded code from procedure bodies...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_module_io(unneeded_code.process_proc_msg), !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
UnneededCode = no
|
|
).
|
|
|
|
:- pred maybe_eliminate_dead_procs(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_eliminate_dead_procs(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize_dead_procs, Dead, !IO),
|
|
(
|
|
Dead = yes,
|
|
maybe_write_string(Verbose, "% Eliminating dead procedures...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
dead_proc_elim(final_optimization_pass, !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Dead = no
|
|
).
|
|
|
|
:- pred maybe_structure_sharing_analysis(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_structure_sharing_analysis(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(structure_sharing_analysis,
|
|
Sharing, !IO),
|
|
(
|
|
Sharing = yes,
|
|
maybe_write_string(Verbose, "% Structure sharing analysis...\n",
|
|
!IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
structure_sharing_analysis(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Sharing = no
|
|
).
|
|
|
|
:- pred maybe_structure_reuse_analysis(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_structure_reuse_analysis(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(structure_reuse_analysis,
|
|
ReuseAnalysis, !IO),
|
|
(
|
|
ReuseAnalysis = yes,
|
|
maybe_write_string(Verbose, "% Structure reuse analysis...\n",
|
|
!IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
structure_reuse_analysis(!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ReuseAnalysis = no
|
|
).
|
|
|
|
:- pred maybe_term_size_prof(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_term_size_prof(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(record_term_sizes_as_words, AsWords, !IO),
|
|
globals.io_lookup_bool_option(record_term_sizes_as_cells, AsCells, !IO),
|
|
(
|
|
AsWords = yes,
|
|
AsCells = yes,
|
|
unexpected(this_file, "maybe_term_size_prof: as_words and as_cells")
|
|
;
|
|
AsWords = yes,
|
|
AsCells = no,
|
|
MaybeTransform = yes(term_words)
|
|
;
|
|
AsWords = no,
|
|
AsCells = yes,
|
|
MaybeTransform = yes(term_cells)
|
|
;
|
|
AsWords = no,
|
|
AsCells = no,
|
|
MaybeTransform = no
|
|
),
|
|
(
|
|
MaybeTransform = yes(Transform),
|
|
maybe_write_string(Verbose,
|
|
"% Applying term size profiling transformation...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_module_io(size_prof.process_proc_msg(Transform)),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
MaybeTransform = no
|
|
).
|
|
|
|
:- pred maybe_read_experimental_complexity_file(
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_read_experimental_complexity_file(!HLDS, !IO) :-
|
|
globals.io_lookup_string_option(experimental_complexity, FileName, !IO),
|
|
globals.io_lookup_bool_option(record_term_sizes_as_words,
|
|
RecordTermSizesAsWords, !IO),
|
|
globals.io_lookup_bool_option(record_term_sizes_as_cells,
|
|
RecordTermSizesAsCells, !IO),
|
|
bool.or(RecordTermSizesAsWords, RecordTermSizesAsCells,
|
|
RecordTermSizes),
|
|
( FileName = "" ->
|
|
% While we could include the following sanity check, it is overly
|
|
% strong. For example, a bootcheck in a term size profiling grade
|
|
% would have to supply an --experimental-complexity option for
|
|
% both the stage3 compiler and the test cases. Since the runtime
|
|
% checks that all procedures mentioned in the files actually
|
|
% exist and are transformed by the compiler, this would require
|
|
% a different complexity experiment file for each test case,
|
|
% which is impractical.
|
|
% (
|
|
% RecordTermSizes = yes,
|
|
% report_error("term size profiling grades require " ++
|
|
% "the --experimental-complexity option", !IO)
|
|
% ;
|
|
% RecordTermSizes = no
|
|
% )
|
|
true
|
|
;
|
|
(
|
|
RecordTermSizes = yes
|
|
;
|
|
RecordTermSizes = no,
|
|
report_error("the --experimental-complexity option " ++
|
|
"requires a term size profiling grade", !IO)
|
|
),
|
|
complexity.read_spec_file(FileName, MaybeNumProcMap, !IO),
|
|
(
|
|
MaybeNumProcMap = ok(NumProcs - ProcMap),
|
|
module_info_set_maybe_complexity_proc_map(yes(NumProcs - ProcMap),
|
|
!HLDS)
|
|
;
|
|
MaybeNumProcMap = error(Msg),
|
|
report_error(Msg, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred maybe_experimental_complexity(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_experimental_complexity(Verbose, Stats, !HLDS, !IO) :-
|
|
module_info_get_maybe_complexity_proc_map(!.HLDS, MaybeNumProcMap),
|
|
(
|
|
MaybeNumProcMap = no
|
|
;
|
|
MaybeNumProcMap = yes(NumProcs - ProcMap),
|
|
maybe_write_string(Verbose,
|
|
"% Applying complexity experiment transformation...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_module_io(complexity.process_proc_msg(NumProcs, ProcMap)),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
).
|
|
|
|
:- pred maybe_deep_profiling(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_deep_profiling(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(profile_deep, ProfileDeep, !IO),
|
|
(
|
|
ProfileDeep = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Applying deep profiling transformation...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
apply_deep_profiling_transformation(!HLDS),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ProfileDeep = no
|
|
).
|
|
|
|
:- pred maybe_introduce_accumulators(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_introduce_accumulators(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(introduce_accumulators, Optimize, !IO),
|
|
(
|
|
Optimize = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Attempting to introduce accumulators...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_module_io(accumulator.process_proc), !HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Optimize = no
|
|
).
|
|
|
|
:- pred maybe_lco(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_lco(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize_constructor_last_call, LCO, !IO),
|
|
(
|
|
LCO = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Looking for LCO modulo constructor application ...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
lco_modulo_constructors(!HLDS),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
LCO = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The backend passes
|
|
|
|
:- pred map_args_to_regs(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
map_args_to_regs(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Mapping args to regs...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
generate_arg_info(!HLDS),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_saved_vars(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_saved_vars(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize_saved_vars_const, SavedVars, !IO),
|
|
(
|
|
SavedVars = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Minimizing variable saves using constants...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_module_io(saved_vars_proc),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
SavedVars = no
|
|
).
|
|
|
|
:- pred maybe_stack_opt(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_stack_opt(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize_saved_vars_cell, SavedVars, !IO),
|
|
(
|
|
SavedVars = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Minimizing variable saves using cells...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_module_io(stack_opt_cell),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
SavedVars = no
|
|
).
|
|
|
|
:- pred maybe_followcode(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_followcode(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(follow_code, FollowCode, !IO),
|
|
globals.io_lookup_bool_option(prev_code, PrevCode, !IO),
|
|
(
|
|
( FollowCode = yes
|
|
; PrevCode = yes
|
|
)
|
|
->
|
|
maybe_write_string(Verbose, "% Migrating branch code...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_module(move_follow_code_in_proc),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred compute_liveness(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
compute_liveness(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Computing liveness...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_proc_io(detect_liveness_proc),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred compute_stack_vars(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
compute_stack_vars(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Computing stack vars...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_proc_io(allocate_stack_slots_in_proc), !HLDS, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred allocate_store_map(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
allocate_store_map(Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Allocating store map...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(
|
|
update_proc_predid(allocate_store_maps(final_allocation)), !HLDS, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_goal_paths(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_goal_paths(Verbose, Stats, !HLDS, !IO) :-
|
|
globals.io_get_trace_level(TraceLevel, !IO),
|
|
( given_trace_level_is_none(TraceLevel) = no ->
|
|
maybe_write_string(Verbose, "% Calculating goal paths...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
process_all_nonimported_procs(update_proc(fill_goal_path_slots),
|
|
!HLDS, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred generate_code(module_info::in, bool::in, bool::in,
|
|
global_data::in, global_data::out, list(c_procedure)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_code(HLDS, Verbose, Stats, !GlobalData, LLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Generating code...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
generate_code(HLDS, !GlobalData, LLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_do_optimize(global_data::in, bool::in, bool::in,
|
|
list(c_procedure)::in, list(c_procedure)::out, io::di, io::uo) is det.
|
|
|
|
maybe_do_optimize(GlobalData, Verbose, Stats, !LLDS, !IO) :-
|
|
globals.io_lookup_bool_option(optimize, Optimize, !IO),
|
|
(
|
|
Optimize = yes,
|
|
maybe_write_string(Verbose, "% Doing optimizations...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
optimize_procs(GlobalData, !LLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
Optimize = no
|
|
).
|
|
|
|
:- pred maybe_generate_stack_layouts(module_info::in, list(c_procedure)::in,
|
|
bool::in, bool::in, global_data::in, global_data::out, io::di, io::uo)
|
|
is det.
|
|
|
|
maybe_generate_stack_layouts(HLDS, LLDS, Verbose, Stats, !GlobalData, !IO) :-
|
|
maybe_write_string(Verbose,
|
|
"% Generating call continuation information...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
continuation_info.maybe_process_llds(LLDS, HLDS, !GlobalData),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Gather together the information from the HLDS, given the foreign
|
|
% language we are going to use, that is used for the foreign language
|
|
% interface.
|
|
% This stuff mostly just gets passed directly to the LLDS unchanged, but
|
|
% we do do a bit of code generation -- for example, we call
|
|
% export.get_foreign_export_{decls,defns} here, which do the generation
|
|
% of C code for `pragma export' declarations.
|
|
%
|
|
:- pred get_c_interface_info(module_info::in, foreign_language::in,
|
|
foreign_interface_info::out) is det.
|
|
|
|
get_c_interface_info(HLDS, UseForeignLanguage, Foreign_InterfaceInfo) :-
|
|
module_info_get_name(HLDS, ModuleName),
|
|
module_info_get_foreign_decl(HLDS, ForeignDecls),
|
|
module_info_get_foreign_import_module(HLDS, ForeignImports),
|
|
module_info_get_foreign_body_code(HLDS, ForeignBodyCode),
|
|
foreign.filter_decls(UseForeignLanguage, ForeignDecls,
|
|
WantedForeignDecls, _OtherDecls),
|
|
foreign.filter_imports(UseForeignLanguage, ForeignImports,
|
|
WantedForeignImports, _OtherImports),
|
|
foreign.filter_bodys(UseForeignLanguage, ForeignBodyCode,
|
|
WantedForeignBodys, _OtherBodys),
|
|
export.get_foreign_export_decls(HLDS, Foreign_ExportDecls),
|
|
export.get_foreign_export_defns(HLDS, Foreign_ExportDefns),
|
|
|
|
Foreign_InterfaceInfo = foreign_interface_info(ModuleName,
|
|
WantedForeignDecls, WantedForeignImports,
|
|
WantedForeignBodys, Foreign_ExportDecls, Foreign_ExportDefns).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The LLDS output pass
|
|
|
|
:- pred output_pass(module_info::in, global_data::in, list(c_procedure)::in,
|
|
module_name::in, bool::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
output_pass(HLDS, GlobalData0, Procs, ModuleName, CompileErrors,
|
|
FactTableObjFiles, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
% Here we generate the LLDS representations for various data structures
|
|
% used for RTTI, type classes, and stack layouts.
|
|
% XXX This should perhaps be part of backend_pass rather than output_pass.
|
|
type_ctor_info.generate_rtti(HLDS, TypeCtorRttiData),
|
|
generate_base_typeclass_info_rtti(HLDS, OldTypeClassInfoRttiData),
|
|
globals.io_lookup_bool_option(new_type_class_rtti, NewTypeClassRtti, !IO),
|
|
generate_type_class_info_rtti(HLDS, NewTypeClassRtti,
|
|
NewTypeClassInfoRttiData),
|
|
list.append(OldTypeClassInfoRttiData, NewTypeClassInfoRttiData,
|
|
TypeClassInfoRttiData),
|
|
stack_layout.generate_llds(HLDS, GlobalData0, GlobalData, StackLayoutDatas,
|
|
LayoutLabels),
|
|
|
|
% Here we perform some optimizations on the LLDS data.
|
|
% XXX This should perhaps be part of backend_pass rather than output_pass.
|
|
% XXX We assume that the foreign language we use is C.
|
|
get_c_interface_info(HLDS, c, C_InterfaceInfo),
|
|
global_data_get_all_proc_vars(GlobalData, GlobalVars),
|
|
global_data_get_all_closure_layouts(GlobalData, ClosureLayoutDatas),
|
|
global_data_get_static_cell_info(GlobalData, StaticCellInfo),
|
|
get_static_cells(StaticCellInfo,
|
|
ScalarCommonCellDatas, VectorCommonCellDatas),
|
|
|
|
% Next we put it all together and output it to one or more C files.
|
|
RttiDatas = TypeCtorRttiData ++ TypeClassInfoRttiData,
|
|
LayoutDatas = ClosureLayoutDatas ++ StackLayoutDatas,
|
|
construct_c_file(HLDS, C_InterfaceInfo, Procs, GlobalVars,
|
|
ScalarCommonCellDatas, VectorCommonCellDatas, RttiDatas, LayoutDatas,
|
|
CFile, !IO),
|
|
module_info_get_complexity_proc_infos(HLDS, ComplexityProcs),
|
|
output_llds(ModuleName, CFile, ComplexityProcs, LayoutLabels,
|
|
Verbose, Stats, !IO),
|
|
|
|
C_InterfaceInfo = foreign_interface_info(_, _, _, _, C_ExportDecls, _),
|
|
export.produce_header_file(C_ExportDecls, ModuleName, !IO),
|
|
|
|
% Finally we invoke the C compiler to compile it.
|
|
globals.io_lookup_bool_option(target_code_only, TargetCodeOnly, !IO),
|
|
(
|
|
TargetCodeOnly = no,
|
|
io.output_stream(OutputStream, !IO),
|
|
c_to_obj(OutputStream, ModuleName, CompileOK, !IO),
|
|
module_get_fact_table_files(HLDS, FactTableBaseFiles),
|
|
list.map2_foldl(compile_fact_table_file(OutputStream),
|
|
FactTableBaseFiles, FactTableObjFiles, FactTableCompileOKs, !IO),
|
|
bool.and_list([CompileOK | FactTableCompileOKs], AllOk),
|
|
maybe_set_exit_status(AllOk, !IO),
|
|
bool.not(AllOk, CompileErrors)
|
|
;
|
|
TargetCodeOnly = yes,
|
|
CompileErrors = no,
|
|
FactTableObjFiles = []
|
|
).
|
|
|
|
% Split the code up into bite-size chunks for the C compiler.
|
|
%
|
|
:- pred construct_c_file(module_info::in, foreign_interface_info::in,
|
|
list(c_procedure)::in, list(comp_gen_c_var)::in,
|
|
list(scalar_common_data_array)::in, list(vector_common_data_array)::in,
|
|
list(rtti_data)::in, list(layout_data)::in, c_file::out, io::di, io::uo)
|
|
is det.
|
|
|
|
construct_c_file(ModuleInfo, C_InterfaceInfo, Procedures, GlobalVars,
|
|
ScalarCommonCellDatas, VectorCommonCellDatas, RttiDatas, LayoutDatas,
|
|
CFile, !IO) :-
|
|
C_InterfaceInfo = foreign_interface_info(ModuleSymName, C_HeaderCode0,
|
|
C_Includes, C_BodyCode0, _C_ExportDecls, C_ExportDefns),
|
|
MangledModuleName = sym_name_mangle(ModuleSymName),
|
|
string.append(MangledModuleName, "_module", ModuleName),
|
|
globals.io_lookup_int_option(procs_per_c_function, ProcsPerFunc, !IO),
|
|
get_c_body_code(C_BodyCode0, C_BodyCode),
|
|
( ProcsPerFunc = 0 ->
|
|
% ProcsPerFunc = 0 really means infinity -
|
|
% we store all the procs in a single function.
|
|
ChunkedModules = [comp_gen_c_module(ModuleName, Procedures)]
|
|
;
|
|
list.chunk(Procedures, ProcsPerFunc, ChunkedProcs),
|
|
combine_chunks(ChunkedProcs, ModuleName, ChunkedModules)
|
|
),
|
|
list.map_foldl(make_foreign_import_header_code, C_Includes,
|
|
C_IncludeHeaderCode, !IO),
|
|
|
|
% The lists are reversed because insertions into them are at the front.
|
|
% We don't want to put C_LocalHeaderCode between Start and End, because
|
|
% C_IncludeHeaderCode may include our own header file, which defines
|
|
% the module's guard macro, which in turn #ifdefs out the stuff between
|
|
% Start and End.
|
|
list.filter(foreign_decl_code_is_local, list.reverse(C_HeaderCode0),
|
|
C_LocalHeaderCode, C_ExportedHeaderCode),
|
|
make_decl_guards(ModuleSymName, Start, End),
|
|
C_HeaderCode = list.reverse(C_IncludeHeaderCode) ++
|
|
C_LocalHeaderCode ++ [Start | C_ExportedHeaderCode] ++ [End],
|
|
|
|
module_info_user_init_pred_c_names(ModuleInfo, UserInitPredCNames),
|
|
module_info_user_final_pred_c_names(ModuleInfo, UserFinalPredCNames),
|
|
|
|
CFile = c_file(ModuleSymName, C_HeaderCode, C_BodyCode, C_ExportDefns,
|
|
GlobalVars, ScalarCommonCellDatas, VectorCommonCellDatas,
|
|
RttiDatas, LayoutDatas, ChunkedModules, UserInitPredCNames,
|
|
UserFinalPredCNames).
|
|
|
|
:- pred foreign_decl_code_is_local(foreign_decl_code::in) is semidet.
|
|
|
|
foreign_decl_code_is_local(Decl) :-
|
|
Decl = foreign_decl_code(_, foreign_decl_is_local, _, _).
|
|
|
|
:- pred make_decl_guards(sym_name::in, foreign_decl_code::out,
|
|
foreign_decl_code::out) is det.
|
|
|
|
make_decl_guards(ModuleName, StartGuard, EndGuard) :-
|
|
Define = decl_guard(ModuleName),
|
|
Start = "#ifndef " ++ Define ++ "\n#define " ++ Define ++ "\n",
|
|
End = "\n#endif",
|
|
StartGuard = foreign_decl_code(c, foreign_decl_is_exported, Start,
|
|
term.context_init),
|
|
EndGuard = foreign_decl_code(c, foreign_decl_is_exported, End,
|
|
term.context_init).
|
|
|
|
:- pred make_foreign_import_header_code(foreign_import_module::in,
|
|
foreign_decl_code::out, io::di, io::uo) is det.
|
|
|
|
make_foreign_import_header_code(ForeignImportModule, Include, !IO) :-
|
|
ForeignImportModule = foreign_import_module(Lang, ModuleName, Context),
|
|
(
|
|
Lang = c,
|
|
module_name_to_search_file_name(ModuleName, ".mh", HeaderFileName,
|
|
!IO),
|
|
IncludeString = "#include """ ++ HeaderFileName ++ """\n",
|
|
Include = foreign_decl_code(c, foreign_decl_is_exported,
|
|
IncludeString, Context)
|
|
;
|
|
Lang = csharp,
|
|
sorry(this_file, ":- import_module not yet implemented: " ++
|
|
"`:- pragma foreign_import_module' for C#")
|
|
;
|
|
Lang = managed_cplusplus,
|
|
sorry(this_file, ":- import_module not yet implemented: " ++
|
|
"`:- pragma foreign_import_module' for Managed C++")
|
|
;
|
|
Lang = il,
|
|
sorry(this_file, ":- import_module not yet implemented: " ++
|
|
"`:- pragma foreign_import_module' for IL")
|
|
;
|
|
Lang = java,
|
|
sorry(this_file, ":- import_module not yet implemented: " ++
|
|
"`:- pragma foreign_import_module' for Java")
|
|
).
|
|
|
|
:- pred get_c_body_code(foreign_body_info::in, list(user_foreign_code)::out)
|
|
is det.
|
|
|
|
get_c_body_code([], []).
|
|
get_c_body_code([foreign_body_code(Lang, Code, Context) | CodesAndContexts],
|
|
[user_foreign_code(Lang, Code, Context) | C_Modules]) :-
|
|
get_c_body_code(CodesAndContexts, C_Modules).
|
|
|
|
:- pred combine_chunks(list(list(c_procedure))::in, string::in,
|
|
list(comp_gen_c_module)::out) is det.
|
|
|
|
combine_chunks(ChunkList, ModName, Modules) :-
|
|
combine_chunks_2(ChunkList, ModName, 0, Modules).
|
|
|
|
:- pred combine_chunks_2(list(list(c_procedure))::in,
|
|
string::in, int::in, list(comp_gen_c_module)::out) is det.
|
|
|
|
combine_chunks_2([], _ModName, _N, []).
|
|
combine_chunks_2([Chunk | Chunks], ModuleName, Num, [Module | Modules]) :-
|
|
string.int_to_string(Num, NumString),
|
|
string.append(ModuleName, NumString, ThisModuleName),
|
|
Module = comp_gen_c_module(ThisModuleName, Chunk),
|
|
Num1 = Num + 1,
|
|
combine_chunks_2(Chunks, ModuleName, Num1, Modules).
|
|
|
|
:- pred output_llds(module_name::in, c_file::in,
|
|
list(complexity_proc_info)::in, map(llds.label, llds.data_addr)::in,
|
|
bool::in, bool::in, io::di, io::uo) is det.
|
|
|
|
output_llds(ModuleName, LLDS0, ComplexityProcs, StackLayoutLabels,
|
|
Verbose, Stats, !IO) :-
|
|
maybe_write_string(Verbose, "% Writing output to `", !IO),
|
|
module_name_to_file_name(ModuleName, ".c", yes, FileName, !IO),
|
|
maybe_write_string(Verbose, FileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
transform_llds(LLDS0, LLDS, !IO),
|
|
output_llds(LLDS, ComplexityProcs, StackLayoutLabels, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred c_to_obj(io.output_stream::in, module_name::in, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
c_to_obj(ErrorStream, ModuleName, Succeeded, !IO) :-
|
|
get_linked_target_type(LinkedTargetType, !IO),
|
|
get_object_code_type(LinkedTargetType, PIC, !IO),
|
|
maybe_pic_object_file_extension(PIC, Obj, !IO),
|
|
module_name_to_file_name(ModuleName, ".c", no, C_File, !IO),
|
|
module_name_to_file_name(ModuleName, Obj, yes, O_File, !IO),
|
|
compile_target_code.compile_c_file(ErrorStream, PIC, C_File, O_File,
|
|
Succeeded, !IO).
|
|
|
|
:- pred compile_fact_table_file(io.output_stream::in, string::in,
|
|
string::out, bool::out, io::di, io::uo) is det.
|
|
|
|
compile_fact_table_file(ErrorStream, BaseName, O_File, Succeeded, !IO) :-
|
|
get_linked_target_type(LinkedTargetType, !IO),
|
|
get_object_code_type(LinkedTargetType, PIC, !IO),
|
|
maybe_pic_object_file_extension(PIC, Obj, !IO),
|
|
string.append(BaseName, ".c", C_File),
|
|
string.append(BaseName, Obj, O_File),
|
|
compile_target_code.compile_c_file(ErrorStream, PIC, C_File, O_File,
|
|
Succeeded, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The MLDS backend
|
|
|
|
:- pred mlds_backend(module_info::in, module_info::out, mlds::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
mlds_backend(!HLDS, MLDS, !DumpInfo, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
simplify(no, ml_backend, Verbose, Stats,
|
|
process_all_nonimported_procs, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 405, "ml_backend_simplify", !DumpInfo, !IO),
|
|
|
|
% NOTE: it is unsafe for passes after add_trail_ops to reorder
|
|
% disjunctions if trail usage has been optimized. Such reordering
|
|
% may result in the trail being corrupted.
|
|
%
|
|
maybe_add_trail_ops(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 410, "add_trail_ops", !DumpInfo, !IO),
|
|
|
|
maybe_add_heap_ops(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 415, "add_heap_ops", !DumpInfo, !IO),
|
|
|
|
maybe_mark_static_terms(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 420, "mark_static", !DumpInfo, !IO),
|
|
|
|
% We need to do map_args_to_regs, even though that module is meant
|
|
% for the LLDS back-end, because with the MLDS back-end the arg_infos
|
|
% that map_args_to_regs generates are used by continuation_info.m,
|
|
% which is used by ml_unify_gen.m when outputting closure layout structs.
|
|
map_args_to_regs(Verbose, Stats, !HLDS, !IO),
|
|
maybe_dump_hlds(!.HLDS, 425, "args_to_regs", !DumpInfo, !IO),
|
|
|
|
maybe_dump_hlds(!.HLDS, 499, "final", !DumpInfo, !IO),
|
|
|
|
maybe_write_string(Verbose, "% Converting HLDS to MLDS...\n", !IO),
|
|
ml_code_gen(!.HLDS, MLDS0, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS0, 0, "initial", !IO),
|
|
|
|
maybe_write_string(Verbose, "% Generating RTTI data...\n", !IO),
|
|
mlds_gen_rtti_data(!.HLDS, MLDS0, MLDS10),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS10, 10, "rtti", !IO),
|
|
|
|
% Detection of tail calls needs to occur before the
|
|
% chain_gc_stack_frame pass of ml_elim_nested,
|
|
% because we need to unlink the stack frame from the
|
|
% stack chain before tail calls.
|
|
globals.io_lookup_bool_option(optimize_tailcalls, OptimizeTailCalls, !IO),
|
|
(
|
|
OptimizeTailCalls = yes,
|
|
maybe_write_string(Verbose, "% Detecting tail calls...\n", !IO),
|
|
ml_mark_tailcalls(MLDS10, MLDS20, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
OptimizeTailCalls = no,
|
|
MLDS10 = MLDS20
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS20, 20, "tailcalls", !IO),
|
|
|
|
% Warning about non-tail calls needs to come after detection
|
|
% of tail calls
|
|
globals.io_lookup_bool_option(warn_non_tail_recursion, WarnTailCalls,
|
|
!IO),
|
|
(
|
|
OptimizeTailCalls = yes,
|
|
WarnTailCalls = yes
|
|
->
|
|
maybe_write_string(Verbose,
|
|
"% Warning about non-tail recursive calls...\n", !IO),
|
|
ml_warn_tailcalls(MLDS20, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
true
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
|
|
% run the ml_optimize pass before ml_elim_nested,
|
|
% so that we eliminate as many local variables as possible
|
|
% before the ml_elim_nested transformations.
|
|
% We also want to do tail recursion optimization before
|
|
% ml_elim_nested, since this means that the stack-handling
|
|
% code for accurate GC will go outside the loop rather than
|
|
% inside the loop.
|
|
%
|
|
% However, we need to disable optimize_initializations,
|
|
% because ml_elim_nested doesn't correctly handle
|
|
% code containing initializations.
|
|
globals.io_lookup_bool_option(optimize, Optimize, !IO),
|
|
(
|
|
Optimize = yes,
|
|
globals.io_lookup_bool_option(optimize_initializations,
|
|
OptimizeInitializations, !IO),
|
|
globals.io_set_option(optimize_initializations, bool(no), !IO),
|
|
maybe_write_string(Verbose, "% Optimizing MLDS...\n", !IO),
|
|
ml_optimize.optimize(MLDS20, MLDS25, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
|
|
globals.io_set_option(optimize_initializations,
|
|
bool(OptimizeInitializations), !IO)
|
|
;
|
|
Optimize = no,
|
|
MLDS25 = MLDS20
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS25, 25, "optimize1", !IO),
|
|
|
|
%
|
|
% Note that we call ml_elim_nested twice --
|
|
% the first time to chain the stack frames together, for accurate GC,
|
|
% and the second time to flatten nested functions.
|
|
% These two passes are quite similar,
|
|
% but must be done separately.
|
|
% Currently chaining the stack frames together for accurate GC
|
|
% needs to be done first, because the code for doing that
|
|
% can't handle the env_ptr references that the other pass
|
|
% generates.
|
|
%
|
|
|
|
globals.io_get_gc_method(GC, !IO),
|
|
( GC = accurate ->
|
|
maybe_write_string(Verbose,
|
|
"% Threading GC stack frames...\n", !IO),
|
|
ml_elim_nested(chain_gc_stack_frames, MLDS25, MLDS30, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
MLDS30 = MLDS25
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS30, 30, "gc_frames", !IO),
|
|
|
|
globals.io_lookup_bool_option(gcc_nested_functions, NestedFuncs, !IO),
|
|
(
|
|
NestedFuncs = no,
|
|
maybe_write_string(Verbose, "% Flattening nested functions...\n", !IO),
|
|
ml_elim_nested(hoist_nested_funcs, MLDS30, MLDS35, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
NestedFuncs = yes,
|
|
MLDS35 = MLDS30
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS35, 35, "nested_funcs", !IO),
|
|
|
|
% run the ml_optimize pass again after ml_elim_nested,
|
|
% to do optimize_initializations. (It may also help pick
|
|
% up some additional optimization opportunities for the
|
|
% other optimizations in this pass.)
|
|
(
|
|
Optimize = yes,
|
|
maybe_write_string(Verbose, "% Optimizing MLDS again...\n", !IO),
|
|
ml_optimize.optimize(MLDS35, MLDS40, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
Optimize = no,
|
|
MLDS40 = MLDS35
|
|
),
|
|
maybe_report_stats(Stats, !IO),
|
|
maybe_dump_mlds(MLDS40, 40, "optimize2", !IO),
|
|
|
|
MLDS = MLDS40,
|
|
maybe_dump_mlds(MLDS, 99, "final", !IO).
|
|
|
|
:- pred mlds_gen_rtti_data(module_info::in, mlds::in, mlds::out) is det.
|
|
|
|
mlds_gen_rtti_data(HLDS, MLDS0, MLDS) :-
|
|
type_ctor_info.generate_rtti(HLDS, TypeCtorRtti),
|
|
generate_base_typeclass_info_rtti(HLDS, TypeClassInfoRtti),
|
|
|
|
module_info_get_globals(HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, new_type_class_rtti,
|
|
NewTypeClassRtti),
|
|
generate_type_class_info_rtti(HLDS, NewTypeClassRtti,
|
|
NewTypeClassInfoRttiData),
|
|
list.condense([TypeCtorRtti, TypeClassInfoRtti,
|
|
NewTypeClassInfoRttiData], RttiData),
|
|
RttiDefns = rtti_data_list_to_mlds(HLDS, RttiData),
|
|
MLDS0 = mlds(ModuleName, ForeignCode, Imports, Defns0, InitPreds,
|
|
FinalPreds),
|
|
list.append(RttiDefns, Defns0, Defns),
|
|
MLDS = mlds(ModuleName, ForeignCode, Imports, Defns,
|
|
InitPreds, FinalPreds).
|
|
|
|
% The `--high-level-C' MLDS output pass
|
|
|
|
:- pred mlds_to_high_level_c(mlds::in, io::di, io::uo) is det.
|
|
|
|
mlds_to_high_level_c(MLDS, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_write_string(Verbose, "% Converting MLDS to C...\n", !IO),
|
|
mlds_to_c.output_mlds(MLDS, "", !IO),
|
|
maybe_write_string(Verbose, "% Finished converting MLDS to C.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred mlds_to_java(module_info::in, mlds::in, io::di, io::uo) is det.
|
|
|
|
mlds_to_java(ModuleInfo, MLDS, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_write_string(Verbose, "% Converting MLDS to Java...\n", !IO),
|
|
mlds_to_java.output_mlds(ModuleInfo, MLDS, !IO),
|
|
maybe_write_string(Verbose, "% Finished converting MLDS to Java.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred maybe_mlds_to_gcc(mlds::in, bool::out, io::di, io::uo) is det.
|
|
|
|
maybe_mlds_to_gcc(MLDS, ContainsCCode, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_write_string(Verbose,
|
|
"% Passing MLDS to GCC and compiling to assembler...\n", !IO),
|
|
maybe_mlds_to_gcc.compile_to_asm(MLDS, ContainsCCode, !IO),
|
|
maybe_write_string(Verbose, "% Finished compiling to assembler.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
:- pred mlds_to_il_assembler(mlds::in, io::di, io::uo) is det.
|
|
|
|
mlds_to_il_assembler(MLDS, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
|
|
maybe_write_string(Verbose, "% Converting MLDS to IL...\n", !IO),
|
|
mlds_to_ilasm.output_mlds(MLDS, !IO),
|
|
maybe_write_string(Verbose, "% Finished converting MLDS to IL.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type dump_info
|
|
---> no_prev_dump
|
|
; prev_dumped_hlds(string, module_info).
|
|
|
|
:- pred maybe_dump_hlds(module_info::in, int::in, string::in,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_dump_hlds(HLDS, StageNum, StageName, !DumpInfo, !IO) :-
|
|
globals.io_lookup_accumulating_option(dump_hlds, DumpStages, !IO),
|
|
StageNumStr = stage_num_str(StageNum),
|
|
( should_dump_stage(StageNum, StageNumStr, StageName, DumpStages) ->
|
|
module_info_get_name(HLDS, ModuleName),
|
|
module_name_to_file_name(ModuleName, ".hlds_dump", yes, BaseFileName,
|
|
!IO),
|
|
DumpFileName = BaseFileName ++ "." ++ StageNumStr ++ "-" ++ StageName,
|
|
(
|
|
!.DumpInfo = prev_dumped_hlds(PrevDumpFileName, PrevHLDS),
|
|
HLDS = PrevHLDS
|
|
->
|
|
CurDumpFileName = PrevDumpFileName,
|
|
io.open_output(DumpFileName, Res, !IO),
|
|
( Res = ok(FileStream) ->
|
|
io.write_string(FileStream, "This stage is identical " ++
|
|
"to the stage in " ++ PrevDumpFileName ++ ".\n", !IO),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
maybe_write_string(Verbose, "\n", !IO),
|
|
Msg = "can't open file `" ++ DumpFileName ++ "' for output.",
|
|
report_error(Msg, !IO)
|
|
)
|
|
;
|
|
dump_hlds(DumpFileName, HLDS, !IO),
|
|
CurDumpFileName = DumpFileName
|
|
),
|
|
!:DumpInfo = prev_dumped_hlds(CurDumpFileName, HLDS)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- func stage_num_str(int) = string.
|
|
|
|
stage_num_str(StageNum) = StageNumStr :-
|
|
int_to_string(StageNum, StageNumStr0),
|
|
( string.length(StageNumStr0, 1) ->
|
|
StageNumStr = "00" ++ StageNumStr0
|
|
; string.length(StageNumStr0, 2) ->
|
|
StageNumStr = "0" ++ StageNumStr0
|
|
;
|
|
StageNumStr = StageNumStr0
|
|
).
|
|
|
|
:- pred should_dump_stage(int::in, string::in, string::in, list(string)::in)
|
|
is semidet.
|
|
|
|
should_dump_stage(StageNum, StageNumStr, StageName, DumpStages) :-
|
|
list.member(DumpStage, DumpStages),
|
|
(
|
|
StageName = DumpStage
|
|
;
|
|
"all" = DumpStage
|
|
;
|
|
(
|
|
DumpStage = StageNumStr
|
|
;
|
|
string.append("0", DumpStage, StageNumStr)
|
|
;
|
|
string.append("00", DumpStage, StageNumStr)
|
|
)
|
|
;
|
|
string.append(From, "+", DumpStage),
|
|
string.to_int(From, FromInt),
|
|
StageNum >= FromInt
|
|
).
|
|
|
|
:- pred dump_hlds(string::in, module_info::in, io::di, io::uo) is det.
|
|
|
|
dump_hlds(DumpFile, HLDS, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
maybe_write_string(Verbose, "% Dumping out HLDS to `", !IO),
|
|
maybe_write_string(Verbose, DumpFile, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.open_output(DumpFile, Res, !IO),
|
|
( Res = ok(FileStream) ->
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
hlds_out.write_hlds(0, HLDS, !IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
maybe_write_string(Verbose, "\n", !IO),
|
|
Msg = "can't open file `" ++ DumpFile ++ "' for output.",
|
|
report_error(Msg, !IO)
|
|
).
|
|
|
|
:- pred maybe_dump_mlds(mlds::in, int::in, string::in, io::di, io::uo) is det.
|
|
|
|
maybe_dump_mlds(MLDS, StageNum, StageName, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_accumulating_option(dump_mlds, DumpStages, !IO),
|
|
globals.io_lookup_accumulating_option(verbose_dump_mlds,
|
|
VerboseDumpStages, !IO),
|
|
StageNumStr = stage_num_str(StageNum),
|
|
( should_dump_stage(StageNum, StageNumStr, StageName, DumpStages) ->
|
|
maybe_write_string(Verbose, "% Dumping out MLDS as C...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
DumpSuffix = "_dump." ++ StageNumStr ++ "-" ++ StageName,
|
|
mlds_to_c.output_mlds(MLDS, DumpSuffix, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
true
|
|
),
|
|
( should_dump_stage(StageNum, StageNumStr, StageName, VerboseDumpStages) ->
|
|
maybe_write_string(Verbose, "% Dumping out raw MLDS...\n", !IO),
|
|
ModuleName = mlds_get_module_name(MLDS),
|
|
module_name_to_file_name(ModuleName, ".mlds_dump", yes, BaseFileName,
|
|
!IO),
|
|
string.append_list([BaseFileName, ".", StageNumStr, "-", StageName],
|
|
DumpFile),
|
|
dump_mlds(DumpFile, MLDS, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred dump_mlds(string::in, mlds::in, io::di, io::uo) is det.
|
|
|
|
dump_mlds(DumpFile, MLDS, !IO) :-
|
|
globals.io_lookup_bool_option(verbose, Verbose, !IO),
|
|
globals.io_lookup_bool_option(statistics, Stats, !IO),
|
|
maybe_write_string(Verbose, "% Dumping out MLDS to `", !IO),
|
|
maybe_write_string(Verbose, DumpFile, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.open_output(DumpFile, Res, !IO),
|
|
( Res = ok(FileStream) ->
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
pprint.write(80, pprint.to_doc(MLDS), !IO),
|
|
io.nl(!IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
maybe_write_string(Verbose, "\n", !IO),
|
|
string.append_list(["can't open file `", DumpFile, "' for output."],
|
|
ErrorMessage),
|
|
report_error(ErrorMessage, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "mercury_compile.m".
|
|
|
|
%-----------------------------------------------------------------------------%
|