Files
mercury/compiler/handle_options.m
Zoltan Somogyi 1659e9e2cb Use explicit streams in analysis*.m.
compiler/analysis.m:
compiler/analysis.file.m:
    Switch to using explicit streams.

    Use io.format when relevant.

    Give some fields, predicates and variables more meaningful names.

    Replace some rafe-isms with code in our usual coding style.

    Add XXXs for possible future improvements.

    Replace the flag that says whether debug is enabled with a maybe
    wrapped around the stream where debug output should go.

compiler/handle_options.m:
    Initialize the maybe debug stream.

compiler/Mercury.options:
    Stop disabling the warning for the use of implicit streams
    for analysis.m and analysis.file.m.
2023-04-07 01:08:56 +10:00

3252 lines
128 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 The University of Melbourne.
% Copyright (C) 2013-2023 The Mercury Team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: handle_options.m.
% Main authors: fjh, zs.
%
% This module does post-processing on the command-line options, after
% getopt has done its stuff.
%
%---------------------------------------------------------------------------%
:- module libs.handle_options.
:- interface.
:- import_module libs.globals.
:- import_module parse_tree.
:- import_module parse_tree.error_spec.
:- import_module io.
:- import_module list.
%---------------------------------------------------------------------------%
% Generate a dummy globals value based on the default values of the
% options.
%
:- pred generate_default_globals(io.text_output_stream::in, globals::out,
io::di, io::uo) is det.
% handle_given_options(ProgressStream, Args, OptionArgs, NonOptionArgs,
% Specs, Globals, !IO).
%
:- pred handle_given_options(io.text_output_stream::in, list(string)::in,
list(string)::out, list(string)::out, list(error_spec)::out,
globals::out, io::di, io::uo) is det.
% separate_option_args(Args, OptionArgs, NonOptionArgs):
%
% Separate the list of arguments into option and non-option arguments.
%
:- pred separate_option_args(list(string)::in,
list(string)::out, list(string)::out) is det.
% Display the compiler version.
%
:- pred display_compiler_version(io.text_output_stream::in,
io::di, io::uo) is det.
% usage_errors(Globals, Specs, !IO)
%
% Print the list of error messages, and then the usage message.
%
:- pred usage_errors(io.text_output_stream::in, globals::in,
list(error_spec)::in, io::di, io::uo) is det.
% Display usage message.
%
:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
% Display long usage message for help.
%
:- pred long_usage(io.text_output_stream::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module analysis.
:- import_module libs.compiler_util.
:- import_module libs.compute_grade.
:- import_module libs.op_mode.
:- import_module libs.optimization_options.
:- import_module libs.options.
:- import_module libs.trace_params.
:- import_module mdbcomp.
:- import_module mdbcomp.feedback.
:- import_module parse_tree.write_error_spec.
:- import_module bool.
:- import_module cord.
:- import_module dir.
:- import_module getopt.
:- import_module int.
:- import_module io.environment.
:- import_module io.file.
:- import_module library.
:- import_module map.
:- import_module maybe.
:- import_module require.
:- import_module string.
%---------------------------------------------------------------------------%
generate_default_globals(ProgressStream, DefaultGlobals, !IO) :-
handle_given_options(ProgressStream, [], _, _, _, DefaultGlobals, !IO).
handle_given_options(ProgressStream, Args0, OptionArgs, Args, Specs,
!:Globals, !IO) :-
trace [compile_time(flag("debug_handle_given_options")), io(!TIO)] (
io.write_string(ProgressStream, "\noriginal arguments\n", !TIO),
dump_arguments(ProgressStream, Args0, !TIO)
),
process_given_options(Args0, OptionArgs, Args, MaybeError, OptionTable,
OptOptions, !IO),
trace [compile_time(flag("debug_handle_given_options")), io(!TIO)] (
io.write_string(ProgressStream, "\nfinal option arguments\n", !TIO),
dump_arguments(ProgressStream, OptionArgs, !TIO),
io.write_string(ProgressStream, "\nfinal non-option arguments\n",
!TIO),
dump_arguments(ProgressStream, Args, !TIO)
),
convert_option_table_result_to_globals(ProgressStream, MaybeError,
OptionTable, OptOptions, Specs, !:Globals, !IO),
(
Specs = [_ | _]
% Do NOT set the exit status. This predicate may be called before all
% the options are known, so the errors may not be valid.
;
Specs = [],
% XXX Why is this not tested for at the same time as the other reasons
% for disabling smart recompilation?
globals.get_op_mode(!.Globals, OpMode),
globals.lookup_bool_option(!.Globals, smart_recompilation, Smart),
( if
Smart = yes,
OpMode = opm_top_args(OpModeArgs, _),
OpModeArgs = opma_augment(opmau_generate_code(
opmcg_target_object_and_executable))
then
% XXX Currently smart recompilation doesn't check that all the
% files needed to link are present and up-to-date, so disable it.
disable_smart_recompilation(ProgressStream, "linking",
!Globals, !IO)
else
true
)
).
separate_option_args(RawArgs, OptionArgs, NonOptionArgs) :-
getopt.init_option_table(option_defaults, OptionTable0),
% XXX Ignoring _MaybeError seems a bit careless.
getopt.record_arguments(short_option, long_option, OptionTable0,
RawArgs, NonOptionArgs, OptionArgs, _MaybeError, _OptionValues).
% process_given_options(Args, OptionArgs, NonOptionArgs, MaybeOptionTable,
% !IO):
%
% Process the options, but don't do any post-processing. This is mainly
% useful for separating the list of arguments into option and non-option
% arguments.
%
:- pred process_given_options(list(string)::in, list(string)::out,
list(string)::out, maybe(option_error(option))::out,
option_table(option)::out, cord(optimization_option)::out,
io::di, io::uo) is det.
process_given_options(RawArgs, OptionArgs, NonOptionArgs, MaybeError,
OptionTable, OptOptionsCord, !IO) :-
OptionOps =
option_ops_userdata(short_option, long_option, special_handler),
getopt.init_option_table(option_defaults, OptionTable0),
getopt.process_options_userdata_io(OptionOps, RawArgs,
OptionArgs, NonOptionArgs, MaybeError, _OptionsSet,
OptionTable0, OptionTable, cord.init, OptOptionsCord, !IO).
:- pred dump_arguments(io.text_output_stream::in, list(string)::in,
io::di, io::uo) is det.
dump_arguments(_, [], !IO).
dump_arguments(ProgressStream, [Arg | Args], !IO) :-
io.format(ProgressStream, " <%s>\n", [s(Arg)], !IO),
dump_arguments(ProgressStream, Args, !IO).
%---------------------------------------------------------------------------%
% Convert string-valued options into the appropriate enumeration types,
% and process implications among the options (i.e. situations where setting
% one option implies setting/unsetting another one).
%
:- pred convert_option_table_result_to_globals(io.text_output_stream::in,
maybe(option_error(option))::in, option_table(option)::in,
cord(optimization_option)::in,
list(error_spec)::out, globals::out, io::di, io::uo) is det.
convert_option_table_result_to_globals(ProgressStream, MaybeError,
OptionTable0, OptOptionsCord, !:Specs, Globals, !IO) :-
(
MaybeError = yes(Error),
ErrorMessage = option_error_to_string(Error),
OptionTableSpec = simplest_no_context_spec($pred, severity_error,
phase_options, [words(ErrorMessage)]),
!:Specs = [OptionTableSpec],
generate_default_globals(ProgressStream, Globals, !IO)
;
MaybeError = no,
OptOptions = cord.list(OptOptionsCord),
process_optimization_options(OptionTable, OptOptions, OptTuple),
check_option_values(OptionTable0, OptionTable,
Target, WordSize, GC_Method, TermNorm, Term2Norm,
TraceLevel, TraceSuppress, SSTraceLevel,
MaybeThreadSafe, C_CompilerType, CSharp_CompilerType,
ReuseStrategy, MaybeFeedbackInfo,
HostEnvType, SystemEnvType, TargetEnvType, LimitErrorContextsMap,
!:Specs, !IO),
decide_op_mode(OptionTable, OpMode, OtherOpModes),
(
OtherOpModes = []
;
OtherOpModes = [_ | _],
OpModeStrs = list.map(op_mode_to_option_string(OptionTable),
[OpMode | OtherOpModes]),
OpModePieces = [words("Error: only one of the following options"),
words("may be given:")] ++
list_to_quoted_pieces(OpModeStrs) ++ [suffix("."), nl],
add_error(phase_options, OpModePieces, !Specs)
),
(
!.Specs = [],
convert_options_to_globals(ProgressStream, OptionTable, OptTuple,
OpMode, Target, WordSize, GC_Method, TermNorm, Term2Norm,
TraceLevel, TraceSuppress, SSTraceLevel, MaybeThreadSafe,
C_CompilerType, CSharp_CompilerType,
ReuseStrategy, MaybeFeedbackInfo,
HostEnvType, SystemEnvType, TargetEnvType,
LimitErrorContextsMap, !Specs, Globals, !IO)
;
!.Specs = [_ | _],
generate_default_globals(ProgressStream, Globals, !IO)
)
).
:- pred check_option_values(option_table::in, option_table::out,
compilation_target::out, word_size::out, gc_method::out,
termination_norm::out, termination_norm::out, trace_level::out,
trace_suppress_items::out, ssdb_trace_level::out, may_be_thread_safe::out,
c_compiler_type::out, csharp_compiler_type::out,
reuse_strategy::out,
maybe(feedback_info)::out, env_type::out, env_type::out, env_type::out,
limit_error_contexts_map::out,
list(error_spec)::out, io::di, io::uo) is det.
check_option_values(!OptionTable, Target, WordSize, GC_Method,
TermNorm, Term2Norm, TraceLevel, TraceSuppress, SSTraceLevel,
MaybeThreadSafe,
C_CompilerType, CSharp_CompilerType, ReuseStrategy, MaybeFeedbackInfo,
HostEnvType, SystemEnvType, TargetEnvType, LimitErrorContextsMap,
!:Specs, !IO) :-
!:Specs = [],
raw_lookup_string_option(!.OptionTable, target, TargetStr),
( if convert_target(TargetStr, TargetPrime) then
Target = TargetPrime
else
Target = target_c, % dummy
TargetSpec =
[words("Invalid argument"), quote(TargetStr), words("to the"),
quote("--target"), words("option; must be")] ++
list_to_quoted_pieces_or(["c", "java", "csharp"]) ++
[suffix("."), nl],
add_error(phase_options, TargetSpec, !Specs)
),
raw_lookup_int_option(!.OptionTable, bits_per_word, BitsPerWord),
( if BitsPerWord = 32 then
WordSize = word_size_32
else if BitsPerWord = 64 then
WordSize = word_size_64
else
WordSize = word_size_64, % dummy
BitsPerWordStr = string.int_to_string(BitsPerWord),
WordSizeSpec =
[words("Invalid argument"), quote(BitsPerWordStr),
words("to the"), quote("--bits-per-word"), words("option;"),
words("must be either"), quote("32"), words("or"), quote("64"),
suffix("."), nl],
add_error(phase_options, WordSizeSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, gc, GC_MethodStr),
( if convert_gc_method(GC_MethodStr, GC_MethodPrime) then
GC_Method = GC_MethodPrime
else
GC_Method = gc_none, % dummy
GCMethodSpec =
[words("Invalid argument"), quote(GC_MethodStr),
words("to the"), quote("--gc"), words("option; must be")] ++
list_to_quoted_pieces_or(["none", "conservative", "boehm", "hgc",
"accurate", "automatic"]) ++ [suffix("."), nl],
add_error(phase_options, GCMethodSpec, !Specs)
),
raw_lookup_int_option(!.OptionTable, fact_table_hash_percent_full,
FactTablePercentFull),
( if
FactTablePercentFull >= 1,
FactTablePercentFull =< 100
then
true
else
FactTablePercentFullSpec =
[words("Invalid argument"), int_fixed(FactTablePercentFull),
words("to the"), quote("--fact-table-hash-percent-full"),
words("option; must be an integer between 1 and 100."), nl],
add_error(phase_options, FactTablePercentFullSpec, !Specs)
),
raw_lookup_int_option(!.OptionTable, inform_incomplete_switch_threshold,
IncompleteSwitchThreshold),
( if
IncompleteSwitchThreshold >= 0,
IncompleteSwitchThreshold =< 100
then
true
else
IncompleteSwitchThresholdSpec =
[words("Invalid argument"), int_fixed(IncompleteSwitchThreshold),
words("to the"), quote("--inform-incomplete-switch-threshold"),
words("option; must be an integer between 0 and 100."), nl],
add_error(phase_options, IncompleteSwitchThresholdSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, termination_norm, TermNormStr),
( if convert_termination_norm(TermNormStr, TermNormPrime) then
TermNorm = TermNormPrime
else
TermNorm = norm_simple, % dummy
TermNormSpec =
[words("Invalid argument"), quote(TermNormStr), words("to the"),
quote("--termination-norm"), words("option; must be")] ++
list_to_quoted_pieces_or(["simple", "total", "num-data-elems"]) ++
[suffix("."), nl],
add_error(phase_options, TermNormSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, termination2_norm, Term2NormStr),
( if convert_termination_norm(Term2NormStr, Term2NormPrime) then
Term2Norm = Term2NormPrime
else
Term2Norm = norm_simple, % dummy
Term2NormSpec =
[words("Invalid argument"), quote(TermNormStr), words("to the"),
quote("--termination2-norm"), words("option; must be")] ++
list_to_quoted_pieces_or(["simple", "total", "num-data-elems"]) ++
[suffix("."), nl],
add_error(phase_options, Term2NormSpec, !Specs)
),
raw_lookup_bool_option(!.OptionTable, force_disable_tracing,
ForceDisableTracing),
(
ForceDisableTracing = yes,
TraceLevel = trace_level_none
;
ForceDisableTracing = no,
raw_lookup_string_option(!.OptionTable, trace_level, Trace),
raw_lookup_bool_option(!.OptionTable, exec_trace, ExecTrace),
raw_lookup_bool_option(!.OptionTable, decl_debug, DeclDebug),
( if
convert_trace_level(Trace, ExecTrace, DeclDebug, MaybeTraceLevel)
then
(
MaybeTraceLevel = yes(TraceLevel)
;
MaybeTraceLevel = no,
TraceLevel = trace_level_none, % dummy
InconsistentTraceLevelSpec =
[words("The specified trace level"), quote(Trace),
words("is not compatible with the value of the"),
quote("--decl-debug"), words("option."), nl],
add_error(phase_options, InconsistentTraceLevelSpec, !Specs)
)
else
TraceLevel = trace_level_none, % dummy
BadTraceLevelSpec =
[words("Invalid argument"), quote(Trace), words("to the"),
quote("--trace"), words("option; must be")] ++
list_to_quoted_pieces_or(["minimum", "shallow", "deep",
"decl", "rep", "default"]) ++ [suffix("."), nl],
add_error(phase_options, BadTraceLevelSpec, !Specs)
)
),
raw_lookup_string_option(!.OptionTable, suppress_trace, SuppressStr),
( if convert_trace_suppress(SuppressStr, TraceSuppressPrime) then
TraceSuppress = TraceSuppressPrime
else
TraceSuppress = default_trace_suppress, % dummy
TraceSuppressSpec =
[words("Invalid argument"), quote(SuppressStr),
words("to the"), quote("--suppress-trace"), words("option."), nl],
% The set of valid options is a given language, not a simple set
% of words, so there is no simple, short suggestion we can print.
add_error(phase_options, TraceSuppressSpec, !Specs)
),
raw_lookup_bool_option(!.OptionTable, force_disable_ssdebug,
ForceDisableSSDB),
(
ForceDisableSSDB = yes,
SSTraceLevel = ssdb_none
;
ForceDisableSSDB = no,
raw_lookup_string_option(!.OptionTable, ssdb_trace_level, SSTrace),
raw_lookup_bool_option(!.OptionTable, source_to_source_debug, SSDB),
( if convert_ssdb_trace_level(SSTrace, SSDB, SSTL) then
SSTraceLevel = SSTL
else
SSTraceLevel = ssdb_none,
SSDBSpec =
[words("Invalid argument"), quote(SSTrace), words("to the"),
quote("--ssdb-trace"), words("option; must be")] ++
list_to_quoted_pieces_or(["default", "none",
"shallow", "deep"]) ++ [suffix("."), nl],
add_error(phase_options, SSDBSpec, !Specs)
)
),
raw_lookup_string_option(!.OptionTable, maybe_thread_safe_opt,
MaybeThreadSafeStr),
( if
convert_maybe_thread_safe(MaybeThreadSafeStr, MaybeThreadSafePrime)
then
MaybeThreadSafe = MaybeThreadSafePrime
else
MaybeThreadSafe = no, % dummy
% XXX This should either be a boolean option, or the two values
% should have descriptive names, not `yes' and `no'.
% XXX They should definitely use a bespoke type inside the compiler.
MTSSpec =
[words("Invalid argument"), quote(MaybeThreadSafeStr),
words("to the"), quote("--maybe-thread-safe"), words("option;"),
words("must be")] ++
list_to_quoted_pieces_or(["no", "yes"]) ++ [suffix("."), nl],
add_error(phase_options, MTSSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, dump_hlds_alias, DumpAlias),
( if DumpAlias = "" then
true
else if convert_dump_alias(DumpAlias, AliasDumpOptions) then
map.set(dump_hlds_options, string(AliasDumpOptions), !OptionTable)
else
DumpAliasSpec =
[words("Invalid argument"), quote(DumpAlias),
words("to the"), quote("D"), words("(also known as"),
quote("--dump-hlds-alias"), suffix(")"), words("option."), nl],
add_error(phase_options, DumpAliasSpec, !Specs)
),
some [!DumpOptions] (
lookup_string_option(!.OptionTable, dump_hlds_options, !:DumpOptions),
% If we have not specified what we want, dump bare procedures
% as default.
( if !.DumpOptions = "" then
!:DumpOptions = "x"
else
true
),
% If we want structured insts in arg-modes, then we want arg-modes.
( if
string.contains_char(!.DumpOptions, 'y'),
not string.contains_char(!.DumpOptions, 'a')
then
!:DumpOptions = "a" ++ !.DumpOptions
else
true
),
% If we want arg-modes, then we want the unifications they apply to.
( if
string.contains_char(!.DumpOptions, 'a'),
not string.contains_char(!.DumpOptions, 'u')
then
!:DumpOptions = "u" ++ !.DumpOptions
else
true
),
% If we want any of the things that decorate predicates or the goals
% inside predicates, then we want the predicates themselves.
( if
( string.contains_char(!.DumpOptions, 'A')
; string.contains_char(!.DumpOptions, 'B')
; string.contains_char(!.DumpOptions, 'D')
; string.contains_char(!.DumpOptions, 'G')
; string.contains_char(!.DumpOptions, 'P')
; string.contains_char(!.DumpOptions, 'R')
; string.contains_char(!.DumpOptions, 'S')
; string.contains_char(!.DumpOptions, 'b')
; string.contains_char(!.DumpOptions, 'c')
; string.contains_char(!.DumpOptions, 'd')
; string.contains_char(!.DumpOptions, 'f')
; string.contains_char(!.DumpOptions, 'g')
; string.contains_char(!.DumpOptions, 'i')
; string.contains_char(!.DumpOptions, 'l')
; string.contains_char(!.DumpOptions, 'm')
; string.contains_char(!.DumpOptions, 'n')
; string.contains_char(!.DumpOptions, 'p')
; string.contains_char(!.DumpOptions, 's')
; string.contains_char(!.DumpOptions, 't')
; string.contains_char(!.DumpOptions, 'u')
; string.contains_char(!.DumpOptions, 'z')
),
not string.contains_char(!.DumpOptions, 'x')
then
!:DumpOptions = "x" ++ !.DumpOptions
else
true
),
map.set(dump_hlds_options, string(!.DumpOptions), !OptionTable)
),
raw_lookup_string_option(!.OptionTable, c_compiler_type,
C_CompilerTypeStr),
( if convert_c_compiler_type(C_CompilerTypeStr, C_CompilerTypePrime) then
C_CompilerType = C_CompilerTypePrime
else
C_CompilerType = cc_unknown, % dummy
CCTpec =
[words("Invalid argument"), quote(C_CompilerTypeStr),
words("to the"), quote("--c-compiler-type"), words("option;"),
words("must be")] ++
list_to_quoted_pieces_or(["gcc", "clang", "msvc", "unknown"]) ++
[suffix("."), nl],
add_error(phase_options, CCTpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, csharp_compiler_type,
CSharp_CompilerTypeStr),
( if
convert_csharp_compiler_type(CSharp_CompilerTypeStr,
CSharp_CompilerTypePrime)
then
CSharp_CompilerType = CSharp_CompilerTypePrime
else
CSharp_CompilerType = csharp_unknown, % dummy
CSCSpec =
[words("Invalid argument"), quote(CSharp_CompilerTypeStr),
words("to the"), quote("--csharp-compiler-type"), words("option;"),
words("must be")] ++
list_to_quoted_pieces_or(["microsoft", "mono", "unknown"]) ++
[suffix("."), nl],
add_error(phase_options, CSCSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, structure_reuse_constraint,
ReuseConstraintStr),
raw_lookup_int_option(!.OptionTable, structure_reuse_constraint_arg,
ReuseConstraintArgNum),
( if
convert_reuse_strategy(ReuseConstraintStr, ReuseConstraintArgNum,
ReuseStrategyPrime)
then
ReuseStrategy = ReuseStrategyPrime
else
ReuseStrategy = same_cons_id, % dummy
ReuseConstrSpec =
[words("Invalid argument"), quote(ReuseConstraintStr),
words("to the"), quote("--structure-reuse-constraint"),
words("option; must be")] ++
list_to_quoted_pieces_or(["same_cons_id",
"within_n_cells_difference"]) ++
[suffix("."), nl],
add_error(phase_options, ReuseConstrSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, feedback_file, FeedbackFile),
( if FeedbackFile = "" then
% No feedback info.
MaybeFeedbackInfo = no
else
% When we are compiling a single module, we generally don't know
% the name of the executable that the compiled module will end up in,
% and in fact the compiled module may end up in more than one
% executable. We therefore cannot require that we use feedback files
% derived from any one profiled program.
MaybeExpectedProfiledProgramName = no,
read_feedback_file(FeedbackFile, MaybeExpectedProfiledProgramName,
FeedbackReadResult, !IO),
(
FeedbackReadResult = ok(FeedbackInfo),
MaybeFeedbackInfo = yes(FeedbackInfo)
;
FeedbackReadResult = error(Error),
feedback_read_error_message_string(FeedbackFile, Error,
ErrorMessage),
add_error(phase_options, [words(ErrorMessage)], !Specs),
MaybeFeedbackInfo = no
)
),
raw_lookup_string_option(!.OptionTable, host_env_type, HostEnvTypeStr),
( if convert_env_type(HostEnvTypeStr, HostEnvTypePrime) then
HostEnvType = HostEnvTypePrime
else
HostEnvType = env_type_posix, % dummy
HostEnvSpec =
[words("Invalid argument"), quote(HostEnvTypeStr), words("to the"),
quote("--host-env-type"), words("option; must be")] ++
list_to_quoted_pieces_or(["posix", "cygwin", "msys", "windows"]) ++
[suffix("."), nl],
add_error(phase_options, HostEnvSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, system_env_type, SystemEnvTypeStr),
( if
( if SystemEnvTypeStr = "" then
SystemEnvTypePrime = HostEnvType
else
convert_env_type(SystemEnvTypeStr, SystemEnvTypePrime)
)
then
SystemEnvType = SystemEnvTypePrime
else
SystemEnvType = env_type_posix, % dummy
SystemEnvSpec =
[words("Invalid argument"), quote(SystemEnvTypeStr),
words("to the"), quote("--system-env-type"), words("option;"),
words("must be")] ++
list_to_quoted_pieces_or(["posix", "cygwin", "msys", "windows"]) ++
[suffix("."), nl],
add_error(phase_options, SystemEnvSpec, !Specs)
),
raw_lookup_string_option(!.OptionTable, target_env_type, TargetEnvTypeStr),
( if convert_env_type(TargetEnvTypeStr, TargetEnvTypePrime) then
TargetEnvType = TargetEnvTypePrime
else
TargetEnvType = env_type_posix, % dummy
TargetEnvTypeSpec =
[words("Invalid argument"), quote(TargetEnvTypeStr),
words("to the"), quote("--target-env-type"), words("option;"),
words("must be")] ++
list_to_quoted_pieces_or(["posix", "cygwin", "msys", "windows"]) ++
[suffix("."), nl],
add_error(phase_options, TargetEnvTypeSpec, !Specs)
),
raw_lookup_accumulating_option(!.OptionTable, limit_error_contexts,
LimitErrorContextsOptionStrs),
convert_limit_error_contexts(LimitErrorContextsOptionStrs,
BadLimitErrorContextsOptions, LimitErrorContextsMap),
(
BadLimitErrorContextsOptions = []
;
BadLimitErrorContextsOptions = [BadLimitErrorContextsOption],
LECSpec =
[words("Invalid argument"), quote(BadLimitErrorContextsOption),
words("to the"), quote("--limit-error-contexts"),
words("option."), nl],
add_error(phase_options, LECSpec, !Specs)
;
BadLimitErrorContextsOptions = [_, _ | _],
BadPieces = list_to_quoted_pieces(BadLimitErrorContextsOptions),
LECSpec =
[words("Invalid arguments")] ++ BadPieces ++
[words("to the"), quote("--limit-error-contexts"),
words("option."), nl],
add_error(phase_options, LECSpec, !Specs)
).
% NOTE: We take two termination_norm arguments because each
% termination analyser (the old and the new) has its own norm setting.
%
:- pred convert_options_to_globals(io.text_output_stream::in,
option_table::in, opt_tuple::in, op_mode::in, compilation_target::in,
word_size::in, gc_method::in, termination_norm::in, termination_norm::in,
trace_level::in, trace_suppress_items::in, ssdb_trace_level::in,
may_be_thread_safe::in, c_compiler_type::in, csharp_compiler_type::in,
reuse_strategy::in, maybe(feedback_info)::in,
env_type::in, env_type::in, env_type::in, limit_error_contexts_map::in,
list(error_spec)::in, list(error_spec)::out,
globals::out, io::di, io::uo) is det.
convert_options_to_globals(ProgressStream, OptionTable0, !.OptTuple, OpMode,
Target, WordSize, GC_Method, TermNorm, Term2Norm,
TraceLevel, TraceSuppress, SSTraceLevel,
MaybeThreadSafe, C_CompilerType, CSharp_CompilerType,
ReuseStrategy, MaybeFeedbackInfo,
HostEnvType, SystemEnvType, TargetEnvType, LimitErrorContextsMap,
!Specs, !:Globals, !IO) :-
OptTuple0 = !.OptTuple,
OT_AllowInlining0 = OptTuple0 ^ ot_allow_inlining,
OT_EnableConstStructPoly0 = OptTuple0 ^ ot_enable_const_struct_poly,
OT_EnableConstStructUser0 = OptTuple0 ^ ot_enable_const_struct_user,
OT_OptCommonStructs0 = OptTuple0 ^ ot_opt_common_structs,
OT_PropConstraints0 = OptTuple0 ^ ot_prop_constraints,
OT_PropLocalConstraints0 = OptTuple0 ^ ot_prop_local_constraints,
OT_OptDupCalls0 = OptTuple0 ^ ot_opt_dup_calls,
OT_PropConstants0 = OptTuple0 ^ ot_prop_constants,
OT_ElimExcessAssigns0 = OptTuple0 ^ ot_elim_excess_assigns,
OT_OptTestAfterSwitch0 = OptTuple0 ^ ot_opt_test_after_switch,
OT_OptLoopInvariants0 = OptTuple0 ^ ot_opt_loop_invariants,
OT_OptSVCell0 = OptTuple0 ^ ot_opt_svcell,
OT_OptFollowCode0 = OptTuple0 ^ ot_opt_follow_code,
OT_OptUnusedArgs0 = OptTuple0 ^ ot_opt_unused_args,
OT_OptUnusedArgsIntermod0 = OptTuple0 ^ ot_opt_unused_args_intermod,
OT_OptHigherOrder0 = OptTuple0 ^ ot_opt_higher_order,
OT_HigherOrderSizeLimit0 = OptTuple0 ^ ot_higher_order_size_limit,
OT_SpecTypes0 = OptTuple0 ^ ot_spec_types,
OT_SpecTypesUserGuided0 = OptTuple0 ^ ot_spec_types_user_guided,
OT_IntroduceAccumulators0 = OptTuple0 ^ ot_introduce_accumulators,
OT_OptLCMC0 = OptTuple0 ^ ot_opt_lcmc,
OT_Deforest0 = OptTuple0 ^ ot_deforest,
OT_Tuple0 = OptTuple0 ^ ot_tuple,
OT_Untuple0 = OptTuple0 ^ ot_untuple,
OT_OptMiddleRec0 = OptTuple0 ^ ot_opt_middle_rec,
OT_AllowHijacks0 = OptTuple0 ^ ot_allow_hijacks,
OT_OptMLDSTailCalls0 = OptTuple0 ^ ot_opt_mlds_tailcalls,
OT_Optimize0 = OptTuple0 ^ ot_optimize,
OT_StdLabels0 = OptTuple0 ^ ot_standardize_labels,
OT_OptDups0 = OptTuple0 ^ ot_opt_dups,
OT_OptFrames0 = OptTuple0 ^ ot_opt_frames,
OT_StringBinarySwitchSize0 = OptTuple0 ^ ot_string_binary_switch_size,
lookup_string_option(OptionTable0, install_command, InstallCmd),
( if InstallCmd = "" then
FileInstallCmd = install_cmd_cp
else
lookup_string_option(OptionTable0, install_command_dir_option,
InstallCmdDirOption),
FileInstallCmd = install_cmd_user(InstallCmd, InstallCmdDirOption)
),
globals_init(OptionTable0, !.OptTuple, OpMode, Target, WordSize, GC_Method,
TermNorm, Term2Norm, TraceLevel, TraceSuppress, SSTraceLevel,
MaybeThreadSafe, C_CompilerType, CSharp_CompilerType,
ReuseStrategy, MaybeFeedbackInfo,
HostEnvType, SystemEnvType, TargetEnvType, FileInstallCmd,
LimitErrorContextsMap, !:Globals),
globals.lookup_bool_option(!.Globals, experiment2, Experiment2),
(
Experiment2 = no
;
Experiment2 = yes,
globals.set_option(pack_everything, bool(yes), !Globals),
globals.set_option(allow_double_word_ints, bool(yes), !Globals),
globals.set_option(allow_packing_dummies, bool(yes), !Globals),
globals.set_option(allow_packing_ints, bool(yes), !Globals),
globals.set_option(allow_packing_chars, bool(yes), !Globals),
globals.set_option(allow_packing_local_sectags, bool(yes), !Globals),
globals.set_option(allow_packing_remote_sectags, bool(yes), !Globals)
),
% NOTE: this call must occur *before* any code below that implicitly
% sets options based on the target language or GC method.
check_grade_component_compatibility(!.Globals, Target, GC_Method, !Specs),
check_for_incompatibilities(!.Globals, OpMode, !Specs),
handle_implications_of_pregen_target_spf(!Globals, Target,
OT_StringBinarySwitchSize0, OT_StringBinarySwitchSize, !Specs),
handle_implications_of_parallel(!Globals, !Specs),
handle_gc_options(!Globals, GC_Method, OT_OptFrames0, OT_OptFrames,
!Specs),
handle_minimal_model_options(!Globals, AllowHijacksMMSC, !Specs),
TraceEnabled = is_exec_trace_enabled_at_given_trace_level(TraceLevel),
handle_debugging_options(Target, TraceLevel, TraceEnabled, SSTraceLevel,
AllowSrcChangesDebug, !Globals, !Specs),
maybe_update_event_set_file_name(!Globals, !IO),
globals.lookup_bool_option(!.Globals, profile_deep, ProfileDeep),
handle_profiling_options(!Globals, Target, ProfileDeep,
AllowSrcChangesProf,
OT_HigherOrderSizeLimit0, OT_HigherOrderSizeLimit, !Specs),
handle_record_term_sizes_options(!Globals, AllowOptLCMCTermSize, !Specs),
% Debugging and profiling both affect what stack layouts we need,
% which is why this call is after both handle_debugging_options and
% handle_profiling_options.
handle_stack_layout_options(!Globals, OT_OptDups0, OT_OptDups,
OT_StdLabels0, OT_StdLabels),
% These three calls are in this order because
%
% - handle_opmode_implications may set transitive_optimization,
% - handle_option_to_option_implications may set
% intermodule_optimization if transitive_optimization is set,
% - maybe_disable_smart_recompilation then needs the final value
% of intermodule_optimization.
%
% However, while it is hard to see, there is no conflict here.
% handle_opmode_implications sets transitive_optimization
% only when the opmode calls for making .trans_opt files,
% while maybe_disable_smart_recompilation needs the value of
% intermodule_optimization only when the opmode calls for
% generating target language code.
handle_opmode_implications(OpMode, !Globals),
handle_option_to_option_implications(OpMode, !Globals),
maybe_disable_smart_recompilation(ProgressStream, OpMode, !Globals, !IO),
handle_directory_options(OpMode, !Globals),
handle_target_compile_link_symlink_options(!Globals),
handle_compiler_developer_options(!Globals, !IO),
handle_compare_specialization(!Globals),
(
OT_Optimize0 = do_not_optimize,
% --no-mlds-optimize implies --no-optimize-tailcalls.
OT_OptMLDSTailCalls = do_not_opt_mlds_tailcalls
;
OT_Optimize0 = optimize,
OT_OptMLDSTailCalls = OT_OptMLDSTailCalls0
),
handle_non_tail_rec_warnings(OptTuple0, OT_OptMLDSTailCalls, OpMode,
!Globals, !Specs),
% The rest of the code of this predicate computes the various fields
% of the optimization options tuple.
( if
AllowSrcChangesDebug = allow_src_changes,
AllowSrcChangesProf = allow_src_changes
then
OT_AllowInlining = OT_AllowInlining0
else
OT_AllowInlining = do_not_allow_inlining
),
(
Target = target_c,
OT_EnableConstStructPoly = OT_EnableConstStructPoly0,
NeedProcBodies = trace_needs_proc_body_reps(TraceLevel, TraceSuppress),
( if
(
% We generate representations of procedure bodies for the
% declarative debugger and for the profiler. When
% traverse_primitives in browser/declarative_tree.m looks for
% the Nth argument of variable X and X is built with code
% such as X = ground_term_const(...), it crashes. It should be
% taught not to do that, but in the meantime, we prevent the
% situation from arising in the first place. (We never look
% for the original sources of type infos and typeclass infos,
% so we can use constant structures for them.)
NeedProcBodies = yes
;
% If we allowed the use of references to the const_struct_db,
%
% - those references would be dangling references
% if they were ever written to a .opt file, and
%
% - they would also confuse the termination analyzers,
% since they were written before the const_struct_db
% was implemented, and they have (yet) not been taught
% about it.
OpMode = opm_top_args(opma_augment(Augment), _),
( Augment = opmau_make_plain_opt
; Augment = opmau_make_trans_opt
)
;
% If we are not allowed to use const structs for the
% type_infos and typeclass_infos created by the polymorphism
% pass, then we may not use them for user terms either.
OT_EnableConstStructPoly0 = do_not_enable_const_struct_poly
)
then
OT_EnableConstStructUser = do_not_enable_const_struct_user
else
OT_EnableConstStructUser = OT_EnableConstStructUser0
)
;
Target = target_java,
OT_EnableConstStructPoly = OT_EnableConstStructPoly0,
OT_EnableConstStructUser = do_not_enable_const_struct_user
;
Target = target_csharp,
OT_EnableConstStructPoly = do_not_enable_const_struct_poly,
OT_EnableConstStructUser = do_not_enable_const_struct_user
),
% `--optimize-constant-propagation' effectively inlines builtins.
%
% We want to allow constant propagation in deep profiling grades,
% so `--no-allow-inlining' should not cause it to be disabled.
% (Other forms of inlining must be disabled for deep profiling.)
%
% `--no-allow-inlining' should imply
% `--no-optimize-constant-propagation' otherwise,
% e.g. when tracing is enabled.
OT_InlineBuiltins0 = OptTuple0 ^ ot_inline_builtins,
( if
OT_InlineBuiltins0 = inline_builtins,
( OT_AllowInlining = allow_inlining
; ProfileDeep = yes
)
then
OT_PropConstants = OT_PropConstants0
else
OT_PropConstants = do_not_prop_constants
),
(
AllowSrcChangesDebug = allow_src_changes,
OT_OptDupCalls = OT_OptDupCalls0,
OT_OptHigherOrder = OT_OptHigherOrder0
;
AllowSrcChangesDebug = do_not_allow_src_changes,
OT_OptDupCalls = do_not_opt_dup_calls,
OT_OptHigherOrder = do_not_opt_higher_order
),
% XXX deforestation and constraint propagation do not perform folding
% on polymorphic predicates correctly with --body-typeinfo-liveness.
globals.lookup_bool_option(!.Globals, body_typeinfo_liveness,
BodyTypeInfoLiveness),
globals.lookup_bool_option(!.Globals, reorder_conj, ReorderConj),
( if
AllowSrcChangesDebug = allow_src_changes,
% --no-reorder-conj implies --no-deforestation,
ReorderConj = bool.yes,
% XXX The folding done by deforestation on polymorphic predicates
% does not respect --body-typeinfo-liveness.
BodyTypeInfoLiveness = bool.no
then
OT_Deforest = OT_Deforest0
else
OT_Deforest = do_not_deforest
),
globals.lookup_bool_option(!.Globals, stack_segments, StackSegments),
( if
% The middle_rec special-case code generator cannot implement tracing.
TraceEnabled = exec_trace_is_not_enabled,
% The RTTI needed for accurate gc cannot handle the stack shapes
% that it generates.
GC_Method \= gc_accurate,
% XXX If trailing is enabled, middle recursion optimization
% can generate code which does not allocate a stack frame
% even though stack slots are used to save and restore the trail,
% if the code being optimized contains a construct which
% might save/restore the trail state, i.e. an if-then-else,
% negation, disjunction, or commit.
globals.lookup_bool_option(!.Globals, use_trail, bool.no),
% The cut-down stack frames used by middle recursion optimization
% don't include return addresses. Since stack extension arranges for
% the return to the old stack segments by overriding the return
% address, stack extension via stack segments and middle recursion
% optimization are incompatible.
StackSegments = bool.no
then
OT_OptMiddleRec = OT_OptMiddleRec0
else
OT_OptMiddleRec = do_not_opt_middle_rec
),
% Disable hijacks if debugging is enabled. The code we now use
% to restore the stacks for direct retries works only if the retry
% does not "backtrack" over a hijacked nondet stack frame whose
% hijack has not been undone. Note that code compiled without
% debugging may still hijack nondet stack frames. Execution may
% reemerge from the nondebugged region in one of two ways. If the
% nondebugged code returns, then it will have undone hijack,
% and the retry code will work. If the nondebugged code calls
% debugged code, there will be a region on the stacks containing
% no debugging information, and the retry command will refuse
% to perform retries that go into or beyond this region.
% Both cases preserve correctness.
%
% An alternative solution would be to store everything on the
% nondet stack that may be hijacked in ordinary stack slots
% on entry to every procedure, but that would be not only
% more complex than simply disabling hijacks, it would be slower
% as well, except in procedures that would have many nested
% hijacks, and such code is extremely rare.
( if
TraceEnabled = exec_trace_is_not_enabled,
GC_Method \= gc_accurate,
AllowHijacksMMSC = bool.yes
then
OT_AllowHijacks = OT_AllowHijacks0
else
OT_AllowHijacks = do_not_allow_hijacks
),
% XXX With accurate gc, we disable type specialization, because
% type specialization may create type class constraints of the form
% `c(t(T))' (e.g. `enum(var(T))'' in library/sparse_bitset.m),
% which the current RTTI system can't handle.
( if
AllowSrcChangesDebug = allow_src_changes,
GC_Method \= gc_accurate
then
OT_SpecTypes = OT_SpecTypes0,
(
OT_SpecTypes = spec_types,
% If we are doing type-specialization, we may as well take
% advantage of the declarations supplied by the programmer.
OT_SpecTypesUserGuided = spec_types_user_guided
;
OT_SpecTypes = do_not_spec_types,
OT_SpecTypesUserGuided = OT_SpecTypesUserGuided0
)
else
OT_SpecTypes = do_not_spec_types,
OT_SpecTypesUserGuided = do_not_spec_types_user_guided
),
% The local constraint propagation transformation (constraint.m)
% is a required part of the constraint propagation transformation
% performed by deforest.m, so if propagating local constraints
% is disallowed, we can't propagate any other constraints either.
( if
AllowSrcChangesDebug = allow_src_changes,
% --no-reorder-conj implies --no-local-constraint-propagation,
% and hence also --no-constraint-propagation.
ReorderConj = bool.yes
then
OT_PropLocalConstraints = OT_PropLocalConstraints0
else
OT_PropLocalConstraints = do_not_prop_local_constraints
),
( if
% XXX The folding done by constraint propagation on polymorphic
% predicates does not respect --body-typeinfo-liveness.
BodyTypeInfoLiveness = bool.no,
OT_PropLocalConstraints = prop_local_constraints
then
OT_PropConstraints = OT_PropConstraints0
else
OT_PropConstraints = do_not_prop_constraints
),
% --introduce-accumulators implies --excess-assign and
% --common-struct.
(
OT_IntroduceAccumulators0 = introduce_accumulators,
OT_OptCommonStructs = opt_common_structs
;
OT_IntroduceAccumulators0 = do_not_introduce_accumulators,
OT_OptCommonStructs = OT_OptCommonStructs0
),
( if
% Doing elim_excess_assigns prevents useless variables from
% cluttering the trace. Its explicit setting removes a source
% of variability in the goal paths reported by tracing.
( TraceEnabled = exec_trace_is_enabled
; OT_IntroduceAccumulators0 = introduce_accumulators
)
then
OT_ElimExcessAssigns = elim_excess_assigns
else
OT_ElimExcessAssigns = OT_ElimExcessAssigns0
),
( if
% Don't do the unused_args optimization when making the
% optimization interface.
( AllowSrcChangesDebug = do_not_allow_src_changes
; OpMode = opm_top_args(opma_augment(opmau_make_plain_opt), _)
)
then
OT_OptUnusedArgs = do_not_opt_unused_args
else
OT_OptUnusedArgs = OT_OptUnusedArgs0
),
% --intermod-unused-args implies --intermodule-optimization and
% --optimize-unused-args. The latter implies that it must be switched
% off if --optimize-unused-args is disallowed,
% XXX In the presence of --intermod-unused-args, we used to *ignore*
% whether some code above disallowed --optimize-unused-args; we used
% to turn it *back on*, and set --intermodule-optimization to yes anyway.
( if
OT_OptUnusedArgsIntermod0 = opt_unused_args_intermod,
OT_OptUnusedArgs = opt_unused_args
then
OT_OptUnusedArgsIntermod = OT_OptUnusedArgsIntermod0,
globals.set_option(intermodule_optimization, bool(yes), !Globals)
else
OT_OptUnusedArgsIntermod = do_not_opt_unused_args_intermod
),
% XXX With accurate gc, we need to disable optimize-constructor-last-call
% as currently the collector (and tracing code generator) knows neither
% about the pre-constructed data structures nor the references into them
% that this optimisation uses.
( if
AllowSrcChangesDebug = allow_src_changes,
ProfileDeep = bool.no,
AllowOptLCMCTermSize = bool.yes,
GC_Method \= gc_accurate
then
OT_OptLCMC = OT_OptLCMC0
else
OT_OptLCMC = do_not_opt_lcmc
),
(
TraceEnabled = exec_trace_is_enabled,
% The explicit setting of the following option removes a source
% of variability in the goal paths reported by tracing.
OT_OptFollowCode = opt_follow_code
;
TraceEnabled = exec_trace_is_not_enabled,
OT_OptFollowCode = OT_OptFollowCode0
),
(
AllowSrcChangesDebug = do_not_allow_src_changes,
OT_OptSVCell = do_not_opt_svcell,
OT_OptLoopInvariants = do_not_opt_loop_invariants,
OT_Untuple = do_not_untuple,
OT_Tuple = do_not_tuple,
OT_OptTestAfterSwitch = do_not_opt_test_after_switch
;
AllowSrcChangesDebug = allow_src_changes,
OT_OptSVCell = OT_OptSVCell0,
OT_OptLoopInvariants = OT_OptLoopInvariants0,
OT_Tuple = OT_Tuple0,
OT_Untuple = OT_Untuple0,
OT_OptTestAfterSwitch = OT_OptTestAfterSwitch0
),
!OptTuple ^ ot_allow_inlining := OT_AllowInlining,
!OptTuple ^ ot_enable_const_struct_poly := OT_EnableConstStructPoly,
!OptTuple ^ ot_enable_const_struct_user := OT_EnableConstStructUser,
!OptTuple ^ ot_opt_common_structs := OT_OptCommonStructs,
!OptTuple ^ ot_prop_constraints := OT_PropConstraints,
!OptTuple ^ ot_prop_local_constraints := OT_PropLocalConstraints,
!OptTuple ^ ot_opt_dup_calls := OT_OptDupCalls,
!OptTuple ^ ot_prop_constants := OT_PropConstants,
!OptTuple ^ ot_opt_svcell := OT_OptSVCell,
!OptTuple ^ ot_opt_loop_invariants := OT_OptLoopInvariants,
!OptTuple ^ ot_elim_excess_assigns := OT_ElimExcessAssigns,
!OptTuple ^ ot_opt_test_after_switch := OT_OptTestAfterSwitch,
!OptTuple ^ ot_opt_follow_code := OT_OptFollowCode,
!OptTuple ^ ot_opt_unused_args := OT_OptUnusedArgs,
!OptTuple ^ ot_opt_unused_args_intermod := OT_OptUnusedArgsIntermod,
!OptTuple ^ ot_opt_higher_order := OT_OptHigherOrder,
!OptTuple ^ ot_higher_order_size_limit := OT_HigherOrderSizeLimit,
!OptTuple ^ ot_spec_types := OT_SpecTypes,
!OptTuple ^ ot_spec_types_user_guided := OT_SpecTypesUserGuided,
!OptTuple ^ ot_opt_lcmc := OT_OptLCMC,
!OptTuple ^ ot_deforest := OT_Deforest,
!OptTuple ^ ot_tuple := OT_Tuple,
!OptTuple ^ ot_untuple := OT_Untuple,
!OptTuple ^ ot_opt_middle_rec := OT_OptMiddleRec,
!OptTuple ^ ot_allow_hijacks := OT_AllowHijacks,
!OptTuple ^ ot_opt_mlds_tailcalls := OT_OptMLDSTailCalls,
!OptTuple ^ ot_standardize_labels := OT_StdLabels,
!OptTuple ^ ot_opt_dups := OT_OptDups,
!OptTuple ^ ot_opt_frames := OT_OptFrames,
!OptTuple ^ ot_string_binary_switch_size := OT_StringBinarySwitchSize,
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
(
HighLevelCode = no,
postprocess_options_lowlevel(!Globals, !OptTuple)
;
HighLevelCode = yes
),
globals.set_opt_tuple(!.OptTuple, !Globals),
postprocess_options_libgrades(!Globals, !Specs),
globals_init_mutables(!.Globals, !IO).
%---------------------------------------------------------------------------%
% Options updated:
% none
%
:- pred check_for_incompatibilities(globals::in, op_mode::in,
list(error_spec)::in, list(error_spec)::out) is det.
check_for_incompatibilities(!.Globals, OpMode, !Specs) :-
% `--transitive-intermodule-optimization' and `--make' are
% not compatible with each other.
globals.lookup_bool_option(!.Globals, transitive_optimization, TransOpt),
(
TransOpt = bool.yes,
( if
(
OpMode = opm_top_args(_, InvokedByMMCMake),
InvokedByMMCMake = op_mode_invoked_by_mmc_make
;
OpMode = opm_top_make
)
then
TransOptMakeSpec =
[words("The"), quote("--transitive-intermodule-optimization"),
words("option is incompatible with the"), quote("--make"),
words("option."), nl],
add_error(phase_options, TransOptMakeSpec, !Specs)
else
true
)
;
TransOpt = bool.no
),
% `--intermodule-optimization' and `--intermodule-analysis' are
% not compatible with each other.
globals.lookup_bool_option(!.Globals, intermodule_optimization,
InterModOpt),
globals.lookup_bool_option(!.Globals, intermodule_analysis,
InterModAnalysis),
( if
InterModOpt = bool.yes,
InterModAnalysis = bool.yes
then
OptAnalysisSpec =
[words("The"), quote("--intermodule-optimization"),
words("option is incompatible with the"),
quote("--intermodule-analysis"), words("option."), nl],
add_error(phase_options, OptAnalysisSpec, !Specs)
else
true
),
globals.lookup_maybe_string_option(!.Globals,
generate_standalone_interface, MaybeStandaloneInt),
globals.lookup_bool_option(!.Globals,
extra_initialization_functions, ExtraInitFunctions),
( if
MaybeStandaloneInt = maybe.yes(_),
ExtraInitFunctions = bool.yes
then
ExtraInitsSpec =
[words("The"), quote("--generate-standalone-interface"),
words("option is incompatible with the"),
quote("--extra-initialization-functions"), words("option."), nl],
add_error(phase_options, ExtraInitsSpec, !Specs)
else
true
).
%---------------------%
% Options updated:
% allow_double_word_fields
% allow_multi_arm_switches
% allow_packing_dummies
% allow_packing_ints
% arg_pack_bits
% backend_foreign_languages
% asm_labels
% can_compare_constants_as_ints
% delay_partial_instantiations
% det_copy_out
% executable_file_extension
% gc
% gcc_global_registers
% gcc_non_local_gotos
% highlevel_code
% nondet_copy_out
% num_ptag_bits
% pretest_equality_cast_pointers
% put_commit_in_own_func
% put_nondet_env_on_heap
% reclaim_heap_on_nondet_failure
% reclaim_heap_on_semidet_failure
% single_prec_float
% structure_reuse_analysis
% structure_sharing_analysis
% unboxed_float
% unboxed_int64s
% unboxed_no_tag_types
% use_float_registers
%
:- pred handle_implications_of_pregen_target_spf(globals::in, globals::out,
compilation_target::in, int::in, int::out,
list(error_spec)::in, list(error_spec)::out) is det.
handle_implications_of_pregen_target_spf(!Globals, Target,
OT_StringBinarySwitchSize0, OT_StringBinarySwitchSize, !Specs) :-
% --pregenerated-dist sets options so that the pre-generated C source
% distribution can be compiled equally on 32-bit and 64-bit platforms.
% Any changes here may require changes in runtime/mercury_conf_param.h.
globals.lookup_bool_option(!.Globals, pregenerated_dist, PregeneratedDist),
(
PregeneratedDist = bool.yes,
globals.set_word_size(word_size_32, !Globals),
globals.set_option(num_ptag_bits, int(2), !Globals),
globals.set_option(arg_pack_bits, int(32), !Globals),
globals.set_option(unboxed_float, bool(no), !Globals),
globals.set_option(unboxed_int64s, bool(no), !Globals),
globals.set_option(single_prec_float, bool(no), !Globals),
globals.set_option(allow_double_word_fields, bool(no), !Globals)
;
PregeneratedDist = bool.no
),
% We assume that single-precision floats do not need to be boxed.
option_implies(single_prec_float, unboxed_float, bool(yes), !Globals),
(
Target = target_c,
BackendForeignLanguages = ["c"],
globals.lookup_int_option(!.Globals, num_ptag_bits, NumPtagBits0),
( if NumPtagBits0 = -1 then
% The default value of NumPtagBits0 is -1. We replace it with the
% autoconf-determined value for the number of low ptag bits,
% which is passed to us from the `mmc' script using the
% deliberately undocumented --conf-low-tag-bits option.
globals.lookup_int_option(!.Globals, conf_low_ptag_bits,
NumPtagBits)
else
% This non-default value may have been set above for
% PregeneratedDist = yes, or it could have been set by the user.
% The only legitimate reason for the latter is cross-compilation.
NumPtagBits = NumPtagBits0
),
globals.set_option(num_ptag_bits, int(NumPtagBits), !Globals),
( if (NumPtagBits = 2 ; NumPtagBits = 3) then
true
else
NumPtagBitsSpec =
[words("Error: the value of the"), quote("--num-ptag-bits"),
words("option is"), int_fixed(NumPtagBits), suffix(","),
words("but the only valid values are 2 and 3."), nl],
add_error(phase_options, NumPtagBitsSpec, !Specs)
),
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
(
HighLevelCode = no
;
HighLevelCode = yes,
% Generating high-level C code requires putting each commit
% in its own function, to avoid problems with setjmp() and
% non-volatile local variables. It also disables the use
% of low-level gcc extensions.
globals.set_option(put_commit_in_own_func, bool(yes), !Globals),
globals.set_option(gcc_non_local_gotos, bool(no), !Globals),
globals.set_option(gcc_global_registers, bool(no), !Globals),
globals.set_option(asm_labels, bool(no), !Globals)
),
% Since we have never targeted 16-bit platforms, the minimum number
% of low ptag bits we ever configure is two.
( if NumPtagBits >= 2 then
globals.set_option(can_compare_constants_as_ints, bool(yes),
!Globals)
else
globals.set_option(can_compare_constants_as_ints, bool(no),
!Globals)
),
% Argument packing only works on back-ends which use low-level data,
% i.e the C backend. For the other target languages, implementing
% argument packing will require not just a lot of work on RTTI,
% but also generalizing field addressing, to allow both single fields
% and a group of adjacent fields packed into a single word to be
% addressed via a mechanism other than an argument's name.
globals.lookup_int_option(!.Globals, arg_pack_bits, ArgPackBits0),
globals.lookup_int_option(!.Globals, bits_per_word, BitsPerWord),
% If --arg-pack-bits is negative then it means use all word bits.
( if ArgPackBits0 < 0 then
ArgPackBits = BitsPerWord
else if ArgPackBits0 > BitsPerWord then
ArgPackBits = BitsPerWord,
ArgPackBitsSpec =
[words("Warning: cannot set the value of"),
quote("--arg-pack-bits"),
words("to value higher than the value of"),
quote("--bits-per-word"), suffix("."),
words("Reducing the effective value of"),
quote("--arg-pack-bits"),
words("to the maximum allowable value, which is"),
int_fixed(BitsPerWord), suffix("."), nl],
add_error(phase_options, ArgPackBitsSpec, !Specs)
else
ArgPackBits = ArgPackBits0
),
globals.set_option(arg_pack_bits, int(ArgPackBits), !Globals),
% Leave the value of allow_double_word_fields as set by the user.
% Leave the value of allow_packing_dummies as set by the user.
% Leave the value of allow_packing_ints as set by the user.
OT_StringBinarySwitchSize = OT_StringBinarySwitchSize0
;
( Target = target_java
; Target = target_csharp
),
globals.set_option(num_ptag_bits, int(0), !Globals),
% Generating Java implies
% - gc_method `automatic' and no heap reclamation on failure
% Because GC is handled automatically by the Java implementation.
% - high-level code
% Because only the MLDS back-end supports compiling to Java.
% - high-level data
% Because it is more efficient, and better for interoperability.
% (In theory --low-level-data should work too, but there is
% no reason to bother supporting it.)
% - unboxed floats
% - unboxed 64-bit integers
% - using copy-out for both det and nondet output arguments
% Because Java doesn't support pass-by-reference.
% - using no tags
% Because Java doesn't provide any mechanism for tagging pointers.
% - box no-tag types
% We require no-tag types to be boxed since in Java,
% java.lang.Object is the only type that all other types
% can be successfully cast to and then cast back from.
% - store nondet environments on the heap
% Because Java has no way of allocating structs on the stack.
% - pretest-equality-cast-pointers
% - no structure reuse
% Because mlds_to_java_stmt.m does not handle assign_if_in_heap.
%
% C# should be the same as Java, except that:
% - C# supports pass-by-reference, but for reasons explained in
% mlds_to_cs_stmt.m, we pretend it doesn't at the MLDS level.
globals.set_gc_method(gc_automatic, !Globals),
globals.set_option(gc, string("automatic"), !Globals),
globals.set_option(reclaim_heap_on_nondet_failure, bool(no),
!Globals),
globals.set_option(reclaim_heap_on_semidet_failure, bool(no),
!Globals),
globals.set_option(highlevel_code, bool(yes), !Globals),
globals.set_option(gcc_non_local_gotos, bool(no), !Globals),
globals.set_option(gcc_global_registers, bool(no), !Globals),
globals.set_option(asm_labels, bool(no), !Globals),
globals.set_option(unboxed_float, bool(yes), !Globals),
globals.set_option(unboxed_int64s, bool(yes), !Globals),
globals.set_option(nondet_copy_out, bool(yes), !Globals),
globals.set_option(det_copy_out, bool(yes), !Globals),
globals.set_option(unboxed_no_tag_types, bool(no), !Globals),
globals.set_option(put_nondet_env_on_heap, bool(yes), !Globals),
globals.set_option(pretest_equality_cast_pointers, bool(yes),
!Globals),
globals.set_option(structure_reuse_analysis, bool(no), !Globals),
globals.set_option(structure_sharing_analysis, bool(no), !Globals),
(
Target = target_csharp,
BackendForeignLanguages = ["csharp"],
globals.set_option(executable_file_extension, string(".exe"),
!Globals)
;
Target = target_java,
BackendForeignLanguages = ["java"]
),
% In the non-C backends, it may not be possible to cast a value
% of a non-enum du type to an integer.
globals.set_option(can_compare_constants_as_ints, bool(no), !Globals),
globals.set_option(arg_pack_bits, int(0), !Globals),
globals.set_option(allow_double_word_fields, bool(no), !Globals),
globals.set_option(allow_packing_dummies, bool(no), !Globals),
globals.set_option(allow_packing_ints, bool(no), !Globals),
% Switch off string hash switches until these backends implement
% the hash operations.
OT_StringBinarySwitchSize = 999999
),
% We only use the float registers if floats would not fit into the
% regular registers.
option_implies(unboxed_float, use_float_registers, bool(no), !Globals),
option_implies(highlevel_code, use_float_registers, bool(no), !Globals),
% Only set the backend foreign languages if they are unset.
globals.lookup_accumulating_option(!.Globals,
backend_foreign_languages, CurrentBackendForeignLanguage),
(
CurrentBackendForeignLanguage = [],
globals.set_option(backend_foreign_languages,
accumulating(BackendForeignLanguages), !Globals)
;
CurrentBackendForeignLanguage = [_ | _]
).
%---------------------%
% Options updated:
% ansi_c
% implicit_parallelism
% par_loop_control
% pre_implicit_parallelism_simplify
%
:- pred handle_implications_of_parallel(globals::in, globals::out,
list(error_spec)::in, list(error_spec)::out) is det.
handle_implications_of_parallel(!Globals, !Specs) :-
current_grade_supports_par_conj(!.Globals, GradeSupportsParConj),
globals.lookup_bool_option(!.Globals, parallel, Parallel),
globals.lookup_bool_option(!.Globals, threadscope, Threadscope),
( if
GradeSupportsParConj = bool.no,
Threadscope = bool.yes
then
ThreadScopeSpec =
[words("The"), quote("threadscope"), words("grade component"),
words("requires a parallel grade."), nl],
add_error(phase_options, ThreadScopeSpec, !Specs)
else
true
),
% Implicit parallelism requires feedback information, however this
% error should only be shown in a parallel grade, otherwise implicit
% parallelism should be disabled.
globals.lookup_bool_option(!.Globals, implicit_parallelism,
ImplicitParallelism),
(
ImplicitParallelism = bool.yes,
(
GradeSupportsParConj = bool.yes,
globals.lookup_string_option(!.Globals, feedback_file,
FeedbackFile),
( if FeedbackFile = "" then
NoFeedbackFileSpec =
[words("The"), quote("--implicit-parallelism"),
words("option requires the use of"),
quote("--feedback-file"), suffix("."), nl],
add_error(phase_options, NoFeedbackFileSpec, !Specs)
else
true
)
;
% Report an error when used in parallel grades without parallel
% conjunction support. In non-parallel grades simply ignore
% --implicit-parallelism.
GradeSupportsParConj = bool.no,
(
Parallel = yes,
NoParConjSupportSpec =
[words("The"), quote("--implicit-parallelism"),
words("option requires a grade that"),
words("supports parallel conjunctions."),
words("Use a low-level C grade without trailing."), nl],
add_error(phase_options, NoParConjSupportSpec, !Specs)
;
Parallel = bool.no
),
globals.set_option(implicit_parallelism, bool(no), !Globals)
)
;
ImplicitParallelism = bool.no
),
% Perform a simplification pass before the implicit parallelism pass to
% ensure that the HLDS more-closely matches the feedback data.
option_implies(implicit_parallelism, pre_implicit_parallelism_simplify,
bool(yes), !Globals),
% Loop control is not applicable in non-parallel grades.
(
GradeSupportsParConj = bool.yes
;
GradeSupportsParConj = bool.no,
globals.set_option(par_loop_control, bool(no), !Globals)
),
% The pthreads headers on some architectures (Solaris, Linux)
% don't work with -ansi.
% XXX We don't pass -ansi to the C compiler anymore.
option_implies(parallel, ansi_c, bool(no), !Globals).
%---------------------%
% Options updated:
% agc_stack_layout
% body_typeinfo_liveness
% opt_no_return_calls
% reclaim_heap_on_nondet_failure
% reclaim_heap_on_semidet_failure
%
:- pred handle_gc_options(globals::in, globals::out, gc_method::in,
maybe_opt_frames::in, maybe_opt_frames::out,
list(error_spec)::in, list(error_spec)::out) is det.
handle_gc_options(!Globals, GC_Method, OT_OptFrames0, OT_OptFrames, !Specs) :-
% --gc accurate for the LLDS back-end requires `agc' stack layouts,
% typeinfo liveness, and needs hijacks, frameopt, and middle recursion
% optimization to be switched off.
%
% For the MLDS back-end, `--gc accurate' requires just typeinfo liveness.
%
% XXX Currently we also need to disable heap reclamation on failure
% if accurate GC is enabled.
% There are two issues with heap reclamation on failure:
%
% 1 For heap reclamation on failure to work at all, we also need
% at least some degree of liveness-accuracy. Otherwise, a local variable
% may get initialized to point to the heap, then the heap is reset,
% then the memory is overwritten with new allocations, and then
% a collection occurs, at which point the local variable now points to
% a value of the wrong type.
% 2 The current method of handling saved heap pointers during GC means that
% we lose heap reclamation on failure after a GC occurs. A better method
% would be to just allocate a word of heap space at each choice point.
%
(
GC_Method = gc_accurate,
globals.set_option(agc_stack_layout, bool(yes), !Globals),
globals.set_option(body_typeinfo_liveness, bool(yes), !Globals),
% We turn off optimization of stack slots for no_return calls,
% because that optimization does not preserve agc typeinfo liveness.
globals.set_option(opt_no_return_calls, bool(no), !Globals),
globals.set_option(reclaim_heap_on_semidet_failure, bool(no),
!Globals),
globals.set_option(reclaim_heap_on_nondet_failure, bool(no),
!Globals),
OT_OptFrames = do_not_opt_frames,
% ml_gen_params_base and ml_declare_env_ptr_arg, in ml_code_util.m,
% both assume (for accurate GC) that continuation environments
% are always allocated on the stack, which means that things won't
% work if --gc accurate and --put-nondet-env-on-heap are both enabled.
globals.lookup_bool_option(!.Globals, highlevel_code,
HighLevelCode),
globals.lookup_bool_option(!.Globals, put_nondet_env_on_heap,
PutNondetEnvOnHeap),
( if
HighLevelCode = bool.yes,
PutNondetEnvOnHeap = bool.yes
then
AGCEnvSpec =
[words_quote("--gc accurate"), words("is incompatible with"),
words_quote("--put-nondet-env-on-heap"), suffix("."), nl],
add_error(phase_options, AGCEnvSpec, !Specs)
else
true
)
;
( GC_Method = gc_automatic
; GC_Method = gc_none
; GC_Method = gc_boehm
; GC_Method = gc_boehm_debug
; GC_Method = gc_hgc
),
OT_OptFrames = OT_OptFrames0,
% Conservative GC implies --no-reclaim-heap-*
GCIsConservative = gc_is_conservative(GC_Method),
(
GCIsConservative = bool.yes,
globals.set_option(reclaim_heap_on_semidet_failure, bool(no),
!Globals),
globals.set_option(reclaim_heap_on_nondet_failure, bool(no),
!Globals)
;
GCIsConservative = bool.no
)
).
%---------------------%
% Options updated:
% use_minimal_model_stack_copy_cut
% use_minimal_model_stack_copy_pneg
%
:- pred handle_minimal_model_options(globals::in, globals::out,
bool::out, list(error_spec)::in, list(error_spec)::out) is det.
handle_minimal_model_options(!Globals, AllowHijacksMMSC, !Specs) :-
globals.lookup_bool_option(!.Globals, use_minimal_model_stack_copy,
UseMinimalModelStackCopy),
globals.lookup_bool_option(!.Globals, use_minimal_model_own_stacks,
UseMinimalModelOwnStacks),
( if
UseMinimalModelStackCopy = bool.yes,
UseMinimalModelOwnStacks = bool.yes
then
DualMMSpec =
[words("You cannot use both forms of minimal model tabling"),
words("at once."), nl],
add_error(phase_options, DualMMSpec, !Specs)
else
true
),
bool.or(UseMinimalModelStackCopy, UseMinimalModelOwnStacks,
UseMinimalModel),
(
UseMinimalModel = bool.yes,
% Minimal model tabling is not compatible with any of high level code,
% trailing, and parallelism; see comments in runtime/mercury_grade.h.
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
(
HighLevelCode = yes,
MMHLSpec =
[words("Minimal model tabling is incompatible with"),
words("high level code."), nl],
add_error(phase_options, MMHLSpec, !Specs)
;
HighLevelCode = no
),
globals.lookup_bool_option(!.Globals, use_trail, UseTrail),
(
UseTrail = yes,
MMTrailSpec =
[words("Minimal model tabling is incompatible with"),
words("trailing."), nl],
add_error(phase_options, MMTrailSpec, !Specs)
;
UseTrail = no
),
globals.lookup_bool_option(!.Globals, parallel, Parallel),
(
Parallel = yes,
MMParSpec =
[words("Minimal model tabling is incompatible with"),
words("parallel execution."), nl],
add_error(phase_options, MMParSpec, !Specs)
;
Parallel = no
)
;
UseMinimalModel = no
),
% Stack copy minimal model tabling needs to be able to rewrite all
% the redoips in a given nondet stack segments. If we allow hijacks,
% some of these redoips may have been saved in ordinary framevars,
% which means that tabling can't find them without label layout info.
% Since we want to allow tabling in grades that do not have label
% layout info, we disable hijacks instead.
% XXX we should allow hijacks in table_builtin.m
(
UseMinimalModelStackCopy = bool.yes,
AllowHijacksMMSC = bool.no
;
UseMinimalModelStackCopy = bool.no,
AllowHijacksMMSC = bool.yes
),
% Stack copy minimal model tabling needs to generate extra code
% at possibly negated contexts to handle the pneg stack and at commits
% to handle the cut stack. The code below allows the generation of
% these extra pieces of code to be disabled. The disabled program will
% work only if the program doesn't actually use minimal model tabling,
% which makes it useful only for performance testing.
globals.lookup_bool_option(!.Globals,
disable_minimal_model_stack_copy_pneg, DisablePneg),
globals.lookup_bool_option(!.Globals,
disable_minimal_model_stack_copy_cut, DisableCut),
( if
UseMinimalModelStackCopy = bool.yes,
DisablePneg = bool.no
then
globals.set_option(use_minimal_model_stack_copy_pneg, bool(yes),
!Globals)
else
true
),
( if
UseMinimalModelStackCopy = bool.yes,
DisableCut = bool.no
then
globals.set_option(use_minimal_model_stack_copy_cut, bool(yes),
!Globals)
else
true
).
%---------------------%
:- type maybe_allow_src_changes
---> do_not_allow_src_changes
; allow_src_changes.
% Options updated:
% body_typeinfo_liveness
% exec_trace
% exec_trace_tail_rec
% link_ssdb_libs
% opt_no_return_calls
% trace_stack_layout
% trace_table_io
% trace_table_io_all
%
:- pred handle_debugging_options(compilation_target::in, trace_level::in,
maybe_exec_trace_enabled::in, ssdb_trace_level::in,
maybe_allow_src_changes::out, globals::in, globals::out,
list(error_spec)::in, list(error_spec)::out) is det.
handle_debugging_options(Target, TraceLevel, TraceEnabled, SSTraceLevel,
!:AllowSrcChanges, !Globals, !Specs) :-
% --decl-debug is an extension of --debug
option_implies(decl_debug, exec_trace, bool(yes), !Globals),
% In debugging grades, we want to generate executables in which
% one can do retries across I/O safely.
option_implies(exec_trace, trace_table_io_all, bool(yes), !Globals),
% --trace-table-io-all is compulsory application of --trace-table-io
option_implies(trace_table_io_all, trace_table_io, bool(yes),
!Globals),
% --trace-table-io-require is compulsory application
% of --trace-table-io
option_implies(trace_table_io_require, trace_table_io, bool(yes),
!Globals),
% Execution tracing requires
% - disabling optimizations that would change
% the trace being generated (except with --trace-optimized)
% - enabling some low level optimizations to ensure consistent
% paths across optimization levels
% - enabling stack layouts
% - enabling typeinfo liveness
(
TraceEnabled = exec_trace_is_enabled,
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
( if HighLevelCode = bool.no, Target = target_c then
true
else
TraceHLSpec =
[words("Debugging is available only in low level C grades."),
nl],
add_error(phase_options, TraceHLSpec, !Specs)
),
globals.lookup_bool_option(!.Globals, parallel, Parallel),
(
Parallel = no
;
Parallel = yes,
ParSpec =
[words("Debugging is not available in parallel grades."), nl],
add_error(phase_options, ParSpec, !Specs)
),
globals.lookup_bool_option(!.Globals, trace_optimized, TraceOptimized),
(
TraceOptimized = bool.no,
% The options controlled by AllowSrcChanges modify
% the structure of the program, which makes it difficult
% to relate the trace to the source code (although
% it can be easily related to the transformed HLDS).
!:AllowSrcChanges = do_not_allow_src_changes
;
TraceOptimized = bool.yes,
!:AllowSrcChanges = allow_src_changes
),
% The following options cause the info required by tracing
% to be generated.
globals.set_option(trace_stack_layout, bool(yes), !Globals),
globals.set_option(body_typeinfo_liveness, bool(yes), !Globals),
% To support up-level printing, we need to save variables across
% a call even if the call cannot succeed.
globals.set_option(opt_no_return_calls, bool(no), !Globals),
% The declarative debugger does not (yet) know about tail calls.
AllowTraceTailRec = trace_level_allows_tail_rec(TraceLevel),
(
AllowTraceTailRec = bool.no,
% XXX Any code that checks the exec_trace_tail_rec option
% should instead check the effective trace level for whatever
% procedure it wants to operate on. However, at the moment,
% the only code that looks at this option is mark_tail_calls.m,
% which we invoke only in MLDS grades, which do not implement
% execution tracing. So this question is moot (for now).
globals.set_option(exec_trace_tail_rec, bool(no), !Globals)
;
AllowTraceTailRec = bool.yes
)
;
TraceEnabled = exec_trace_is_not_enabled,
!:AllowSrcChanges = allow_src_changes,
% Since there will be no call and exit events, there is no point
% in trying to turn them into tailcall events.
globals.set_option(exec_trace_tail_rec, bool(no), !Globals)
),
% Source-to-source debugging requires disabling many HLDS->HLDS
% optimizations. This is so that the trace being generated relates to the
% source code and also because the SSDB transformation cannot (yet) handle
% the specialised predicates introduced by many optimizations.
(
( SSTraceLevel = ssdb_shallow
; SSTraceLevel = ssdb_deep
),
!:AllowSrcChanges = do_not_allow_src_changes
;
SSTraceLevel = ssdb_none
% Leave AllowSrcChanges alone.
),
% --ssdb implies --link-ssdb-libs
option_implies(source_to_source_debug, link_ssdb_libs, bool(yes),
!Globals).
%---------------------%
% Options updated:
% event_set_file_name
%
:- pred maybe_update_event_set_file_name(globals::in, globals::out,
io::di, io::uo) is det.
maybe_update_event_set_file_name(!Globals, !IO) :-
globals.lookup_string_option(!.Globals, event_set_file_name,
EventSetFileName0),
( if EventSetFileName0 = "" then
io.environment.get_environment_var("MERCURY_EVENT_SET_FILE_NAME",
MaybeEventSetFileName, !IO),
(
MaybeEventSetFileName = maybe.yes(EventSetFileName),
globals.set_option(event_set_file_name, string(EventSetFileName),
!Globals)
;
MaybeEventSetFileName = maybe.no
)
else
true
).
%---------------------%
% Options updated:
% coverage_profiling
% deep_profile_tail_recursion
% pre_prof_transforms_simplify
% prof_optimized
%
:- pred handle_profiling_options(globals::in, globals::out,
compilation_target::in, bool::in,maybe_allow_src_changes::out,
int::in, int::out, list(error_spec)::in, list(error_spec)::out) is det.
handle_profiling_options(!Globals, Target, ProfileDeep, !:AllowSrcChangesProf,
OT_HigherOrderSizeLimit0, OT_HigherOrderSizeLimit, !Specs) :-
% Profile for feedback requires coverage profiling.
option_implies(profile_for_feedback, coverage_profiling, bool(yes),
!Globals),
% At the moment, coverage profiling is not compatible with the tail
% recursion preservation optimization used by the deep profiler.
option_implies(coverage_profiling, deep_profile_tail_recursion,
bool(no), !Globals),
% Perform a simplification pass before the profiling passes if one of
% these profiling options is enabled.
option_implies(profile_deep, pre_prof_transforms_simplify, bool(yes),
!Globals),
option_implies(profile_deep, procid_stack_layout, bool(yes), !Globals),
(
ProfileDeep = bool.yes,
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
( if
HighLevelCode = no,
Target = target_c
then
true
else
DeepHLSpec =
[words("Deep profiling is incompatible with"),
words("high level code."), nl],
add_error(phase_options, DeepHLSpec, !Specs)
),
% Deep profiling disables the optimization for pretesting whether
% x == y in runtime/mercury_unify_compare_body.h, so compensate by
% doing the same test in the unify and compare predicates generated by
% the Mercury compiler.
globals.set_option(should_pretest_equality, bool(yes), !Globals),
% Inlining happens before the deep profiling transformation, so if
% we allowed inlining to happen, then we would lose all profiling
% information about the inlined calls - this is not usually what we
% want so we disable inlining with deep profiling by default.
% The user can re-enable it with the `--profile-optimized' option.
% Leave inlining enabled when profiling for implicit parallelism.
option_implies(profile_for_feedback, prof_optimized, bool(yes),
!Globals),
globals.lookup_bool_option(!.Globals, prof_optimized, ProfOptimized),
(
ProfOptimized = bool.no,
!:AllowSrcChangesProf = do_not_allow_src_changes
;
ProfOptimized = bool.yes,
!:AllowSrcChangesProf = allow_src_changes
),
globals.lookup_bool_option(!.Globals,
use_lots_of_ho_specialization, LotsOfHOSpec),
(
LotsOfHOSpec = bool.yes,
% We used to switch on --optimize-higher-order here,
% even if it was disabled e.g. for execution tracing.
% That was almost certainly a bug.
%
% Setting the limit here is ok, since the limit is irrelevant
% if the only optimization that looks at it is not run.
OT_HigherOrderSizeLimit = 999999
;
LotsOfHOSpec = bool.no,
OT_HigherOrderSizeLimit = OT_HigherOrderSizeLimit0
)
;
ProfileDeep = bool.no,
!:AllowSrcChangesProf = allow_src_changes,
OT_HigherOrderSizeLimit = OT_HigherOrderSizeLimit0
),
globals.lookup_string_option(!.Globals, experimental_complexity, ExpComp),
( if ExpComp = "" then
true
else
!:AllowSrcChangesProf = do_not_allow_src_changes,
globals.lookup_bool_option(!.Globals, record_term_sizes_as_words,
RecordTermSizesAsWords),
globals.lookup_bool_option(!.Globals, record_term_sizes_as_cells,
RecordTermSizesAsCells),
( if
( RecordTermSizesAsWords = bool.yes
; RecordTermSizesAsCells = bool.yes
)
then
true
else
ExpCompSpec =
[words("The --experimental-complexity option"),
words("requires a term size profiling grade."), nl],
add_error(phase_options, ExpCompSpec, !Specs)
)
).
%---------------------%
% Options updated:
% allow_double_word_fields
% pre_prof_transforms_simplify
%
:- pred handle_record_term_sizes_options(globals::in, globals::out,
bool::out, list(error_spec)::in, list(error_spec)::out) is det.
handle_record_term_sizes_options(!Globals, AllowOptLCMCTermSize, !Specs) :-
globals.lookup_bool_option(!.Globals, record_term_sizes_as_words,
RecordTermSizesAsWords),
globals.lookup_bool_option(!.Globals, record_term_sizes_as_cells,
RecordTermSizesAsCells),
( if
RecordTermSizesAsWords = bool.yes,
RecordTermSizesAsCells = bool.yes
then
DualTermSizeSpec =
[words("Cannot record term size as both words and cells."), nl],
add_error(phase_options, DualTermSizeSpec, !Specs),
AllowOptLCMCTermSize = bool.yes
else if
( RecordTermSizesAsWords = bool.yes
; RecordTermSizesAsCells = bool.yes
)
then
globals.set_option(pre_prof_transforms_simplify, bool(yes), !Globals),
AllowOptLCMCTermSize = bool.no,
% Term size profiling breaks the assumption that even word offsets from
% the start of the cell are double-word aligned memory addresses.
%
% XXX Actually, we do not (or should not) make that assumption as it
% would also be violated by memory attribution profiling which also
% allocates an extra word at the start of a cell.
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
globals.set_option(allow_double_word_fields, bool(no), !Globals),
(
HighLevelCode = bool.yes,
TermSizeHLSpec =
[words("Term size profiling is incompatible with"),
words("high level code."), nl],
add_error(phase_options, TermSizeHLSpec, !Specs)
;
HighLevelCode = bool.no
)
else
AllowOptLCMCTermSize = bool.yes
).
%---------------------%
% Options updated:
% basic_stack_layout
% procid_stack_layout
%
:- pred handle_stack_layout_options(globals::in, globals::out,
maybe_opt_dups::in, maybe_opt_dups::out,
maybe_standardize_labels::in, maybe_standardize_labels::out) is det.
handle_stack_layout_options(!Globals, OT_OptDups0, OT_OptDups,
OT_StdLabels0, OT_StdLabels) :-
% --stack-trace requires `procid' stack layouts.
option_implies(stack_trace, procid_stack_layout, bool(yes), !Globals),
% `trace' stack layouts need `procid' stack layouts.
option_implies(trace_stack_layout, procid_stack_layout, bool(yes),
!Globals),
% `procid' and `agc' stack layouts need `basic' stack layouts
option_implies(procid_stack_layout, basic_stack_layout, bool(yes),
!Globals),
option_implies(agc_stack_layout, basic_stack_layout, bool(yes), !Globals),
% dupelim.m doesn't preserve label layout structures (e.g. it can
% change the return address in a call to a different label whose code
% is the same but which has a different label layout structure),
% so we need to disable it when tracing. We also need to disable it
% when using accurate GC, for the same reason.
globals.lookup_bool_option(!.Globals, procid_stack_layout,
ProcIdStackLayout),
globals.lookup_bool_option(!.Globals, agc_stack_layout, AgcStackLayout),
( if (ProcIdStackLayout = yes ; AgcStackLayout = yes) then
OT_OptDups = do_not_opt_dups
else
OT_OptDups = OT_OptDups0
),
% stdlabel.m tries to perform operations that yield compiler aborts
% if any stack layout information is present in the generated code.
globals.lookup_bool_option(!.Globals, basic_stack_layout,
BasicStackLayout),
(
BasicStackLayout = yes,
OT_StdLabels = do_not_standardize_labels
;
BasicStackLayout = no,
OT_StdLabels = OT_StdLabels0
).
%---------------------%
% Options updated:
% generate_item_version_numbers
% line_numbers
% smart_recompilation
% transitive_optimization
% warn_wrong_module_name
% warn_unused_interface_imports
%
:- pred handle_opmode_implications(op_mode::in,
globals::in, globals::out) is det.
handle_opmode_implications(OpMode, !Globals) :-
% Disable `--smart-recompilation' unless we are generating target code.
globals.lookup_bool_option(!.Globals, smart_recompilation, Smart0),
(
OpMode = opm_top_args(OpModeArgs, _),
% Disable --line-numbers when building the `.int', `.opt', etc. files,
% since including line numbers in those would cause unnecessary
% recompilation.
(
OpModeArgs = opma_make_interface(OpModeArgsMI),
globals.set_option(line_numbers, bool(no), !Globals),
globals.lookup_bool_option(!.Globals, halt_at_warn_make_int,
HaltAtWarn),
globals.set_option(halt_at_warn, bool(HaltAtWarn), !Globals),
globals.set_option(warn_unused_interface_imports, bool(no),
!Globals),
(
( OpModeArgsMI = omif_int0
; OpModeArgsMI = omif_int1_int2
),
% NOTE When generating interface files, --smart-recompilation
% calls not for *doing* smart recompilation, but *preparing*
% for smart recompilation, in the form of generating item
% version numbers.
globals.set_option(generate_item_version_numbers,
bool(Smart0), !Globals)
;
OpModeArgsMI = omif_int3,
% We never use version number information in `.int3',
% `.opt' or `.trans_opt' files.
globals.set_option(generate_item_version_numbers,
bool(no), !Globals)
),
Smart = bool.no
;
OpModeArgs = opma_augment(OpModeAugment),
(
OpModeAugment = opmau_make_plain_opt,
globals.set_option(line_numbers, bool(no), !Globals),
globals.lookup_bool_option(!.Globals, halt_at_warn_make_opt,
HaltAtWarn),
globals.set_option(halt_at_warn, bool(HaltAtWarn), !Globals),
Smart = bool.no
;
OpModeAugment = opmau_make_trans_opt,
globals.set_option(transitive_optimization, bool(yes),
!Globals),
globals.set_option(line_numbers, bool(no), !Globals),
globals.lookup_bool_option(!.Globals, halt_at_warn_make_opt,
HaltAtWarn),
globals.set_option(halt_at_warn, bool(HaltAtWarn), !Globals),
Smart = bool.no
;
( OpModeAugment = opmau_make_analysis_registry
; OpModeAugment = opmau_make_xml_documentation
; OpModeAugment = opmau_typecheck_only
; OpModeAugment = opmau_errorcheck_only
),
Smart = bool.no
;
OpModeAugment = opmau_generate_code(_),
Smart = Smart0
)
;
( OpModeArgs = opma_generate_dependencies
; OpModeArgs = opma_generate_dependency_file
; OpModeArgs = opma_convert_to_mercury
),
Smart = bool.no
)
;
OpMode = opm_top_generate_source_file_mapping,
% Without an existing source file mapping, there is no "right"
% module name.
globals.set_option(warn_wrong_module_name, bool(no), !Globals),
Smart = bool.no
;
( OpMode = opm_top_generate_standalone_interface(_)
; OpMode = opm_top_query(_)
; OpMode = opm_top_make
),
Smart = bool.no
),
globals.set_option(smart_recompilation, bool(Smart), !Globals).
%---------------------%
% Options updated:
% mode_constraints
% optimize_trail_usage
% prop_mode_constraints
% stack_trace
% structure_sharing_analysis
% termination
% termination2
% termination2_check
% termination_check
% use_opt_files
% use_trail
% use_trans_opt_files
% verbose_recompilation
% warn_missing_trans_opt_files
% warn_unused_interface_imports
%
:- pred handle_option_to_option_implications(op_mode::in,
globals::in, globals::out) is det.
handle_option_to_option_implications(OpMode, !Globals) :-
% --make handles creation of the module dependencies itself,
% and they don't need to be recreated when compiling to C.
( if
OpMode = opm_top_args(_, InvokedByMMCMake),
InvokedByMMCMake = op_mode_invoked_by_mmc_make
then
globals.set_option(generate_mmc_make_module_dependencies, bool(no),
!Globals)
else
true
),
option_implies(find_all_recompilation_reasons, verbose_recompilation,
bool(yes), !Globals),
option_implies(debug_mode_constraints, prop_mode_constraints, bool(yes),
!Globals),
option_implies(prop_mode_constraints, mode_constraints, bool(yes),
!Globals),
option_implies(simple_mode_constraints, mode_constraints, bool(yes),
!Globals),
% We need to be able to simulate exits for calls between where an
% exception is thrown to where it is caught both in the debugger and
% for deep profiling.
option_implies(exec_trace, stack_trace, bool(yes), !Globals),
option_implies(profile_deep, stack_trace, bool(yes), !Globals),
% The results of trail usage analysis assume that trail usage
% optimization is being done, i.e. that redundant trailing
% operations are really being eliminated.
option_implies(analyse_trail_usage, optimize_trail_usage, bool(yes),
!Globals),
option_implies(structure_reuse_analysis, structure_sharing_analysis,
bool(yes), !Globals),
option_implies(termination_check_verbose, termination_check, bool(yes),
!Globals),
option_implies(termination2_check_verbose, termination2_check,
bool(yes), !Globals),
option_implies(termination_check, termination, bool(yes), !Globals),
option_implies(termination2_check, termination2, bool(yes), !Globals),
% Note that setting warn_missing_trans_opt_files to yes can be
% overridden below.
option_implies(termination_check, warn_missing_trans_opt_files,
bool(yes), !Globals),
option_implies(termination2_check, warn_missing_trans_opt_files,
bool(yes), !Globals),
% If we are doing full inter-module or transitive optimization,
% we need to build all `.opt' or `.trans_opt' files.
option_implies(transitive_optimization, intermodule_optimization,
bool(yes), !Globals),
option_implies(transitive_optimization, use_trans_opt_files, bool(no),
!Globals),
option_implies(intermodule_optimization, use_opt_files, bool(no),
!Globals),
option_implies(use_trans_opt_files, use_opt_files, bool(yes), !Globals),
% XXX `--use-opt-files' is broken.
% When inter-module optimization is enabled, error checking
% without the extra information from the `.opt' files
% is done when making the `.opt' file. With `--use-opt-files',
% that doesn't happen.
% XXX Should that be "with `--no-use-opt-files'"?
globals.set_option(use_opt_files, bool(no), !Globals),
globals.lookup_bool_option(!.Globals, warn_unused_imports, UnusedImports),
(
UnusedImports = no
;
UnusedImports = yes,
% warn_unused_interface_imports does *part* of the job
% of warn_unused_imports.
globals.set_option(warn_unused_interface_imports, bool(no),
!Globals)
).
% --use-opt-files implies --no-warn-missing-opt-files since
% we are expecting some to be missing.
% XXX This rule will never fire while we set use_opt_files to "no" above.
% option_implies(use_opt_files, warn_missing_opt_files, bool(no),
% !Globals).
%---------------------%
% Options updated:
% none
%
:- pred maybe_disable_smart_recompilation(io.text_output_stream::in,
op_mode::in, globals::in, globals::out, io::di, io::uo) is det.
maybe_disable_smart_recompilation(ProgressStream, OpMode, !Globals, !IO) :-
% XXX Smart recompilation does not yet work with intermodule
% optimization, but we still want to generate version numbers
% in interface files for users of a library compiled with
% intermodule optimization but not using intermodule
% optimization themselves.
globals.lookup_bool_option(!.Globals, smart_recompilation, Smart),
(
Smart = bool.no
;
Smart = bool.yes,
( if
globals.lookup_bool_option(!.Globals, intermodule_optimization,
yes)
then
disable_smart_recompilation(ProgressStream,
"`--intermodule-optimization'", !Globals, !IO)
else
true
),
( if globals.lookup_bool_option(!.Globals, use_opt_files, yes) then
disable_smart_recompilation(ProgressStream,
"`--use-opt-files'", !Globals, !IO)
else
true
),
% XXX Smart recompilation does not yet work with
% `--no-target-code-only'. With `--no-target-code-only'
% it becomes difficult to work out what all the target files
% are and check whether they are up-to-date. By default, mmake always
% enables `--target-code-only' and processes the target code file
% itself, so this isn't a problem.
% XXX I (zs) don't believe that mmake sets --target-code-only anymore.
( if
OpMode = opm_top_args(OpModeArgs, _),
OpModeArgs = opma_augment(
opmau_generate_code(opmcg_target_code_only))
then
true
else
disable_smart_recompilation(ProgressStream,
"`--no-target-code-only'", !Globals, !IO)
)
).
%---------------------%
% Options updated:
% c_include_directory
% config_file
% default_runtime_library_directory
% init_file_directories
% intermod_directories
% libgrade_install_check
% link_library_directories
% use_subdirs
%
:- pred handle_directory_options(op_mode::in, globals::in, globals::out)
is det.
handle_directory_options(OpMode, !Globals) :-
globals.lookup_bool_option(!.Globals, use_grade_subdirs,
UseGradeSubdirs),
% This is needed for library installation (the library grades
% are built using `--use-grade-subdirs', and assume that
% the interface files were built using `--use-subdirs').
( if
(
OpMode = opm_top_make
;
OpMode = opm_top_args(_, InvokedByMMCMake),
InvokedByMMCMake = op_mode_invoked_by_mmc_make
;
UseGradeSubdirs = bool.yes
)
then
globals.set_option(use_subdirs, bool(yes), !Globals)
else
true
),
% We only perform the library grade install check if we are
% building a linked target using mmc --make or if we are building
% a single source file linked target. (The library grade install
% check is *not* compatible with the use of mmake.)
( if
(
OpMode = opm_top_make
;
OpMode = opm_top_args(OpModeArgs, _),
OpModeArgs = opma_augment(opmau_generate_code(
opmcg_target_object_and_executable))
)
then
true
else
globals.set_option(libgrade_install_check, bool(no), !Globals)
),
globals.lookup_string_option(!.Globals, mercury_linkage, MercuryLinkage),
( if MercuryLinkage = "static" then
DefaultRuntimeLibraryDirs = bool.no,
globals.set_option(default_runtime_library_directory, bool(no),
!Globals)
else
globals.lookup_bool_option(!.Globals,
default_runtime_library_directory, DefaultRuntimeLibraryDirs)
),
% Add the standard library directory.
globals.lookup_maybe_string_option(!.Globals,
mercury_standard_library_directory, MaybeStdLibDir),
(
MaybeStdLibDir = yes(StdLibDir),
globals.get_options(!.Globals, OptionTable2),
option_table_add_mercury_library_directory(StdLibDir,
OptionTable2, OptionTable),
globals.set_options(OptionTable, !Globals),
% Add `-L' and `-R' options for the location of the GC libraries.
globals.lookup_accumulating_option(!.Globals,
link_library_directories, LinkLibDirs0),
globals.set_option(link_library_directories,
accumulating([StdLibDir/"lib" | LinkLibDirs0]), !Globals),
(
DefaultRuntimeLibraryDirs = bool.yes,
globals.lookup_accumulating_option(!.Globals,
runtime_link_library_directories, Rpath0),
globals.set_option(runtime_link_library_directories,
accumulating([StdLibDir/"lib" | Rpath0]), !Globals)
;
DefaultRuntimeLibraryDirs = bool.no
)
;
MaybeStdLibDir = no,
globals.set_option(libgrade_install_check, bool(no), !Globals)
),
% Add the path to mercury_conf.h.
globals.lookup_maybe_string_option(!.Globals,
mercury_configuration_directory, MaybeConfDir),
(
MaybeConfDir = yes(ConfDir),
globals.lookup_accumulating_option(!.Globals, c_include_directory,
CIncludeDirs0),
globals.set_option(c_include_directory,
accumulating([ConfDir/"conf" | CIncludeDirs0]), !Globals)
;
MaybeConfDir = no
),
% Find the configuration file.
globals.lookup_maybe_string_option(!.Globals, config_file,
ConfigFile),
% yes("") means `--config-file' was not passed on the command line.
( if ConfigFile = yes("") then
(
MaybeConfDir = yes(ConfDir1),
globals.set_option(config_file, maybe_string(yes(
ConfDir1/"conf"/"Mercury.config")), !Globals)
;
MaybeConfDir = no,
globals.set_option(config_file, maybe_string(no), !Globals)
)
else
true
),
% Handle the `.opt', C header, init file and library search
% directories for installed libraries. These couldn't be handled by
% options.m because they are grade dependent.
globals.lookup_accumulating_option(!.Globals,
mercury_library_directories, MercuryLibDirs),
grade_directory_component(!.Globals, GradeString),
(
MercuryLibDirs = [_ | _],
ExtraLinkLibDirs = list.map(
( func(MercuryLibDir) =
MercuryLibDir/"lib"/GradeString
), MercuryLibDirs),
globals.lookup_accumulating_option(!.Globals,
link_library_directories, LinkLibDirs1),
globals.set_option(link_library_directories,
accumulating(LinkLibDirs1 ++ ExtraLinkLibDirs), !Globals),
(
DefaultRuntimeLibraryDirs = bool.yes,
globals.lookup_accumulating_option(!.Globals,
runtime_link_library_directories, Rpath),
globals.set_option(runtime_link_library_directories,
accumulating(Rpath ++ ExtraLinkLibDirs), !Globals)
;
DefaultRuntimeLibraryDirs = bool.no
),
ExtraIncludeDirs = list.map(
( func(MercuryLibDir) =
MercuryLibDir/"lib"/GradeString/"inc"
), MercuryLibDirs),
globals.lookup_accumulating_option(!.Globals, c_include_directory,
CIncludeDirs),
globals.set_option(c_include_directory,
accumulating(ExtraIncludeDirs ++ CIncludeDirs), !Globals),
ExtraIntermodDirs = list.map(
( func(MercuryLibDir) =
dir.make_path_name(MercuryLibDir,
dir.make_path_name("ints", GradeString))
), MercuryLibDirs),
globals.lookup_accumulating_option(!.Globals,
intermod_directories, IntermodDirs0),
globals.set_option(intermod_directories,
accumulating(ExtraIntermodDirs ++ IntermodDirs0), !Globals),
ExtraInitDirs = list.map(
( func(MercuryLibDir) =
MercuryLibDir / "modules" / GradeString
), MercuryLibDirs),
globals.lookup_accumulating_option(!.Globals,
init_file_directories, InitDirs1),
globals.set_option(init_file_directories,
accumulating(InitDirs1 ++ ExtraInitDirs), !Globals)
;
MercuryLibDirs = []
),
% If --use-search-directories-for-intermod is true, append the
% search directories to the list of directories to search for
% .opt files.
globals.lookup_bool_option(!.Globals,
use_search_directories_for_intermod, UseSearchDirs),
(
UseSearchDirs = bool.yes,
globals.lookup_accumulating_option(!.Globals,
intermod_directories, IntermodDirs1),
globals.lookup_accumulating_option(!.Globals,
search_directories, SearchDirs),
globals.set_option(intermod_directories,
accumulating(IntermodDirs1 ++ SearchDirs), !Globals)
;
UseSearchDirs = bool.no
),
globals.lookup_accumulating_option(!.Globals,
search_library_files_directories, SearchLibFilesDirs),
globals.lookup_accumulating_option(!.Globals,
intermod_directories, IntermodDirs2),
globals.lookup_string_option(!.Globals, target_arch, TargetArch),
ToGradeSubdir = (func(Dir) = Dir/"Mercury"/GradeString/TargetArch),
(
UseGradeSubdirs = bool.yes,
% With `--use-grade-subdirs', `.opt', `.trans_opt' and
% `.mih' files are placed in a directory named
% `Mercury/<grade>/<target_arch>/Mercury/<ext>s'.
% When searching for a `.opt' file, module_name_to_file_name
% produces `Mercury/<ext>/<module>.ext' so that searches
% for installed files work, so we need to add
% `--intermod-directory Mercury/<grade>/<target_arch>'
% to find the `.opt' files in the current directory.
GradeSubdir = "Mercury"/GradeString/TargetArch,
% Directories listed with --search-library-files-directories need
% to be treated in the same way as the current directory.
SearchLibFilesGradeSubdirs = list.map(ToGradeSubdir,
SearchLibFilesDirs),
IntermodDirs3 = [GradeSubdir] ++ SearchLibFilesGradeSubdirs ++
list.negated_filter(unify(dir.this_directory), IntermodDirs2)
;
UseGradeSubdirs = bool.no,
IntermodDirs3 = SearchLibFilesDirs ++ IntermodDirs2
),
globals.set_option(intermod_directories,
accumulating(IntermodDirs3), !Globals),
globals.lookup_accumulating_option(!.Globals,
link_library_directories, LinkLibDirs2),
globals.lookup_accumulating_option(!.Globals,
init_file_directories, InitDirs2),
(
UseGradeSubdirs = bool.yes,
% With --use-grade-subdirs we need to search in
% `Mercury/<grade>/<target_arch>/Mercury/lib' for libraries and
% `Mercury/<grade>/<target_arch>/Mercury/inits' for init files,
% for each directory listed with --search-library-files-directory.
ToGradeLibDir = (func(Dir) = ToGradeSubdir(Dir)/"Mercury"/"lib"),
SearchGradeLibDirs = list.map(ToGradeLibDir, SearchLibFilesDirs),
LinkLibDirs = SearchGradeLibDirs ++ LinkLibDirs2,
ToGradeInitDir =
(func(Dir) = ToGradeSubdir(Dir)/"Mercury"/"inits"),
SearchGradeInitDirs = list.map(ToGradeInitDir, SearchLibFilesDirs),
InitDirs = SearchGradeInitDirs ++ InitDirs2
;
UseGradeSubdirs = bool.no,
LinkLibDirs = SearchLibFilesDirs ++ LinkLibDirs2,
InitDirs = SearchLibFilesDirs ++ InitDirs2
),
globals.set_option(link_library_directories,
accumulating(LinkLibDirs), !Globals),
globals.set_option(init_file_directories,
accumulating(InitDirs), !Globals),
% When searching for a header (.mh, .mih) file,
% module_name_to_file_name uses the plain header name, so we need to
% add the full path to the header files in the current directory,
% and any directories listed with --search-library-files-directory.
globals.lookup_bool_option(!.Globals, use_subdirs, UseSubdirs),
( if
(
UseGradeSubdirs = bool.yes,
ToMihsSubdir =
(func(Dir) = ToGradeSubdir(Dir)/"Mercury"/"mihs")
;
UseGradeSubdirs = bool.no,
(
UseSubdirs = yes,
ToMihsSubdir = (func(Dir) = Dir/"Mercury"/"mihs")
;
UseSubdirs = no,
fail
)
)
then
globals.lookup_accumulating_option(!.Globals, c_include_directory,
CIncludeDirs1),
MihsSubdir = ToMihsSubdir(dir.this_directory),
SearchLibMihsSubdirs = list.map(ToMihsSubdir, SearchLibFilesDirs),
SubdirCIncludeDirs = [dir.this_directory, MihsSubdir |
SearchLibMihsSubdirs ++ CIncludeDirs1],
globals.set_option(c_include_directory,
accumulating(SubdirCIncludeDirs), !Globals)
else
true
).
%---------------------%
% Options updated:
% linkage
% mercury_linkage
% lib_linkages
% strip
% use_symlinks
%
:- pred handle_target_compile_link_symlink_options(globals::in, globals::out)
is det.
handle_target_compile_link_symlink_options(!Globals) :-
% Set up options for position independent code.
% Shared libraries always use `--linkage shared'.
option_implies(compile_to_shared_lib, linkage,
string("shared"), !Globals),
option_implies(compile_to_shared_lib, mercury_linkage,
string("shared"), !Globals),
% If no --lib-linkage option has been specified, default to the
% set of all possible linkages.
globals.lookup_accumulating_option(!.Globals, lib_linkages, LibLinkages0),
(
LibLinkages0 = [],
globals.set_option(lib_linkages,
accumulating(["static", "shared"]), !Globals)
;
LibLinkages0 = [_ | _]
),
option_implies(target_debug, strip, bool(no), !Globals),
( if io.file.have_symlinks then
true
else
globals.set_option(use_symlinks, bool(no), !Globals)
).
%---------------------%
% Options updated:
% auto_comments
% debug_modes
% debug_opt
% dump_hlds_options
% dump_hlds_options
% statistics
% trad_passes
% unneeded_code_debug
% verbose
% verbose_commands
% very_verbose
%
:- pred handle_compiler_developer_options(globals::in, globals::out,
io::di, io::uo) is det.
handle_compiler_developer_options(!Globals, !IO) :-
option_implies(very_verbose, verbose, bool(yes), !Globals),
option_implies(verbose, verbose_commands, bool(yes), !Globals),
globals.lookup_bool_option(!.Globals, very_verbose, VeryVerbose),
globals.lookup_bool_option(!.Globals, statistics, Statistics),
( if
VeryVerbose = bool.yes,
Statistics = bool.yes
then
globals.set_option(detailed_statistics, bool(yes), !Globals)
else
true
),
option_implies(debug_modes_minimal, debug_modes, bool(yes), !Globals),
option_implies(debug_modes_verbose, debug_modes, bool(yes), !Globals),
option_implies(debug_modes_statistics, debug_modes, bool(yes), !Globals),
globals.lookup_int_option(!.Globals, debug_liveness, DebugLiveness),
( if
DebugLiveness >= 0,
convert_dump_alias("all", AllDumpOptions)
then
% Programmers only enable --debug-liveness if they are interested
% in the goal annotations put on goals by the various phases
% of the liveness pass. The default dump options do not print
% these annotations.
globals.lookup_string_option(!.Globals, dump_hlds_options,
DumpOptions0),
DumpOptions1 = DumpOptions0 ++ AllDumpOptions,
globals.set_option(dump_hlds_options, string(DumpOptions1), !Globals)
else
true
),
option_implies(debug_modes_verbose, debug_modes, bool(yes), !Globals),
globals.lookup_int_option(!.Globals, debug_modes_pred_id,
DebugModesPredId),
( if DebugModesPredId > 0 then
globals.set_option(debug_modes, bool(yes), !Globals)
else
true
),
globals.lookup_accumulating_option(!.Globals,
unneeded_code_debug_pred_name, DebugUnneededCodePredNames),
(
DebugUnneededCodePredNames = []
;
DebugUnneededCodePredNames = [_ | _],
globals.set_option(unneeded_code_debug, bool(yes), !Globals)
),
globals.lookup_accumulating_option(!.Globals, debug_opt_pred_id,
DebugOptPredIdStrs),
globals.lookup_accumulating_option(!.Globals, debug_opt_pred_name,
DebugOptPredNames),
( if
( DebugOptPredIdStrs = [_ | _]
; DebugOptPredNames = [_ | _]
)
then
globals.set_option(debug_opt, bool(yes), !Globals)
else
true
),
globals.lookup_bool_option(!.Globals, debug_intermodule_analysis,
DebugIntermoduleAnalysis),
(
DebugIntermoduleAnalysis = no,
analysis.set_analysis_debug(maybe.no, !IO)
;
DebugIntermoduleAnalysis = yes,
% The next person to work actively on the analysis framework
% can decide whether writing the framework's debug progress messages
% to stderr is the right call, and if not, where those messages
% should go.
io.stderr_stream(AnalysisDebugStream, !IO),
analysis.set_analysis_debug(yes(AnalysisDebugStream), !IO)
),
globals.lookup_accumulating_option(!.Globals, dump_hlds_pred_id,
DumpHLDSPredIds),
(
DumpHLDSPredIds = [_ | _],
globals.lookup_string_option(!.Globals, dump_hlds_options,
DumpOptions2),
% Prevent the dumping of the mode and type tables.
string.replace_all(DumpOptions2, "M", "", DumpOptions3),
string.replace_all(DumpOptions3, "T", "", DumpOptions),
globals.set_option(dump_hlds_options, string(DumpOptions), !Globals)
;
DumpHLDSPredIds = []
),
option_implies(frameopt_comments, auto_comments, bool(yes), !Globals),
% --dump-hlds, --statistics, --parallel-liveness and
% --parallel-code-gen require compilation by phases.
globals.lookup_accumulating_option(!.Globals, dump_hlds, DumpHLDSStages),
globals.lookup_accumulating_option(!.Globals, dump_trace_counts,
DumpTraceStages),
globals.lookup_bool_option(!.Globals, parallel_liveness, ParallelLiveness),
globals.lookup_bool_option(!.Globals, parallel_code_gen, ParallelCodeGen),
( if
( DumpHLDSStages = [_ | _]
; DumpTraceStages = [_ | _]
; Statistics = bool.yes
; ParallelLiveness = bool.yes
; ParallelCodeGen = bool.yes
)
then
globals.set_option(trad_passes, bool(no), !Globals)
else
true
).
%---------------------%
% Options updated:
% compare_specialization
%
:- pred handle_compare_specialization(globals::in, globals::out) is det.
handle_compare_specialization(!Globals) :-
globals.lookup_int_option(!.Globals, compare_specialization, CompareSpec),
( if CompareSpec < 0 then
% This indicates that the option was not set by the user;
% we should set the option to the default value. This value
% may be back end specific, since different back ends have
% different performance tradeoffs.
globals.lookup_bool_option(!.Globals, highlevel_code, HighLevelCode),
(
HighLevelCode = bool.no,
globals.set_option(compare_specialization, int(13), !Globals)
;
HighLevelCode = bool.yes,
globals.set_option(compare_specialization, int(14), !Globals)
)
else
true
).
%---------------------%
% Options updated:
% none
%
:- pred handle_non_tail_rec_warnings(opt_tuple::in,
maybe_opt_mlds_tailcalls::in, op_mode::in,
globals::in, globals::out,
list(error_spec)::in, list(error_spec)::out) is det.
handle_non_tail_rec_warnings(OptTuple0, OT_OptMLDSTailCalls, OpMode,
!Globals, !Specs) :-
% --warn-non-tail-recursion requires tail call optimization to be enabled.
% It also doesn't work if you use --errorcheck-only.
globals.lookup_bool_option(!.Globals, warn_non_tail_recursion_self,
WarnNonTailRecSelf),
globals.lookup_bool_option(!.Globals, warn_non_tail_recursion_mutual,
WarnNonTailRecMutual),
( if
( WarnNonTailRecSelf = bool.yes
; WarnNonTailRecMutual = bool.yes
)
then
OT_PessimizeTailCalls0 = OptTuple0 ^ ot_pessimize_tailcalls,
(
OT_PessimizeTailCalls0 = do_not_pessimize_tailcalls
;
OT_PessimizeTailCalls0 = pessimize_tailcalls,
PessimizeWords = "--warn-non-tail-recursion is incompatible" ++
" with --pessimize-tailcalls",
% XXX While these two options look diametrically opposed,
% they are actually compatible, because pessimize_tailcalls
% is implemented only for the LLDS backend, while the
% optimize_tailcalls option is only for the MLDS backend.
% (The LLDS backend's tail call optimization does NOT depend
% on the value of that option.)
add_error(phase_options, [words(PessimizeWords)], !Specs)
),
(
OT_OptMLDSTailCalls = opt_mlds_tailcalls
;
OT_OptMLDSTailCalls = do_not_opt_mlds_tailcalls,
% XXX This error message could be misleading. It is possible that
% - the user *did* ask for MLDS tailcalls, but
% - accidentally also specified --no-optimize, which would
% lead to code above turning MLDS tailcalls *off*.
OptimizeWords =
"--warn-non-tail-recursion requires --optimize-tailcalls",
add_error(phase_options, [words(OptimizeWords)], !Specs)
),
( if
OpMode = opm_top_args(opma_augment(opmau_errorcheck_only), _)
then
ECOWords = "--warn-non-tail-recursion is incompatible"
++ " with --errorcheck-only",
add_error(phase_options, [words(ECOWords)], !Specs)
else
true
)
else
true
).
%---------------------------------------------------------------------------%
% These option implications only affect the low-level (LLDS) code
% generator. They may in fact be harmful if set for the high-level
% code generator, because sometimes the same option has different
% meanings and implications in the two backends.
% XXX For each such dual-use option, we should add two others,
% one for the LLDS and one for the MLDS backend, and make the original
% option be a special option that sets the other two. This would leave
% the user interface unchanged, but would let us handle the implications
% for the two backends separately.
%
:- pred postprocess_options_lowlevel(globals::in, globals::out,
opt_tuple::in, opt_tuple::out) is det.
postprocess_options_lowlevel(!Globals, !OptTuple) :-
% --optimize-saved-vars-cell requires --use-local-vars for
% acceptable performance.
SavedVarsCell = !.OptTuple ^ ot_opt_svcell,
(
SavedVarsCell = opt_svcell,
!OptTuple ^ ot_use_local_vars := use_local_vars
;
SavedVarsCell = do_not_opt_svcell
),
% --optimize-frames requires --optimize-labels and
% --optimize-jumps
OptFrames = !.OptTuple ^ ot_opt_frames,
(
OptFrames = opt_frames,
!OptTuple ^ ot_opt_labels := opt_labels,
!OptTuple ^ ot_opt_jumps := opt_jumps
;
OptFrames = do_not_opt_frames
),
% --optimize-proc-dups is implemented only with --trad-passes.
OptProcDups = !.OptTuple ^ ot_opt_proc_dups,
(
OptProcDups = opt_proc_dups,
globals.set_option(trad_passes, bool(yes), !Globals)
;
OptProcDups = do_not_opt_proc_dups
),
UseLocalVars = !.OptTuple ^ ot_use_local_vars,
OptRepeat = !.OptTuple ^ ot_opt_repeat,
( if
( OptFrames = opt_frames
; UseLocalVars = use_local_vars
),
OptRepeat < 1
then
% The frame optimization and the local vars optimization depend on
% the jump and label optimization having been done. They are turned
% on above, but they still won't be executed unless optimize_repeat
% is at least one.
!OptTuple ^ ot_opt_repeat := 1
else
true
),
% The setting of static_ground_floats is governed only by the settings
% of unboxed_float and static_ground_cells.
globals.lookup_bool_option(!.Globals, unboxed_float, UnboxedFloat),
(
UnboxedFloat = yes,
% If we are using unboxed (MR_Word-sized) floats, floating point values
% are always constants.
!OptTuple ^ ot_use_static_ground_floats := use_static_ground_floats
;
UnboxedFloat = no,
% If we are using boxed floats, then we can generate a static constant
% variable to hold a float constant, and gcc doesn't mind us converting
% from its address to MR_Word in a static initializer. In theory,
% we should do this with --static-ground-terms. However, the code
% generator does not yet handle the dynamic creation of boxed float
% constants, and assumes that binding a variable to a constant
% generates no code.
!OptTuple ^ ot_use_static_ground_floats := use_static_ground_floats
),
% Ditto for 64-bit integers.
globals.lookup_bool_option(!.Globals, unboxed_int64s, UnboxedInt64s),
(
UnboxedInt64s = yes,
!OptTuple ^ ot_use_static_ground_int64s := use_static_ground_int64s
;
UnboxedInt64s = no,
!OptTuple ^ ot_use_static_ground_int64s := use_static_ground_int64s
),
% The setting of static_code_addresses is governed only by the settings
% of gcc_non_local_gotos and asm_labels.
globals.lookup_bool_option(!.Globals, gcc_non_local_gotos, NonLocalGotos),
globals.lookup_bool_option(!.Globals, asm_labels, AsmLabels),
( if
NonLocalGotos = yes,
AsmLabels = no
then
% With non-local gotos but no asm labels, jumps to code addresses
% in different c_modules must be done via global variables; the value
% of these global variables is not constant (i.e. not computable at
% load time), since they can't be initialized until we call
% init_modules().
!OptTuple ^ ot_use_static_code_addresses
:= do_not_use_static_code_addresses
else
!OptTuple ^ ot_use_static_code_addresses := use_static_code_addresses
).
% option_implies(SourceBoolOption, ImpliedOption, ImpliedOptionValue):
% If the SourceBoolOption is set to yes, then the ImpliedOption is set
% to ImpliedOptionValue.
%
:- pred option_implies(option::in, option::in, option_data::in,
globals::in, globals::out) is det.
option_implies(SourceOption, ImpliedOption, ImpliedOptionValue, !Globals) :-
globals.lookup_bool_option(!.Globals, SourceOption, SourceOptionValue),
(
SourceOptionValue = yes,
globals.set_option(ImpliedOption, ImpliedOptionValue, !Globals)
;
SourceOptionValue = no
).
% option_neg_implies(SourceBoolOption, ImpliedOption, ImpliedOptionValue):
% If the SourceBoolOption is set to no, then the ImpliedOption is set
% to ImpliedOptionValue.
%
:- pred option_neg_implies(option::in, option::in, option_data::in,
globals::in, globals::out) is det.
:- pragma consider_used(pred(option_neg_implies/5)).
option_neg_implies(SourceOption, ImpliedOption, ImpliedOptionValue,
!Globals) :-
globals.lookup_bool_option(!.Globals, SourceOption, SourceOptionValue),
(
SourceOptionValue = yes
;
SourceOptionValue = no,
globals.set_option(ImpliedOption, ImpliedOptionValue, !Globals)
).
:- pred disable_smart_recompilation(io.text_output_stream::in, string::in,
globals::in, globals::out, io::di, io::uo) is det.
disable_smart_recompilation(ProgressStream, OptionDescr, !Globals, !IO) :-
io_set_disable_smart_recompilation(disable_smart_recompilation, !IO),
globals.set_option(smart_recompilation, bool(no), !Globals),
globals.lookup_bool_option(!.Globals, warn_smart_recompilation, WarnSmart),
(
WarnSmart = yes,
% Disabling smart recompilation is not a module-specific thing,
% so we cannot direct the error message to a module-specific file.
io.format(ProgressStream,
"Warning: smart recompilation does not yet work with %s.\n",
[s(OptionDescr)], !IO),
globals.lookup_bool_option(!.Globals, halt_at_warn, Halt),
(
Halt = yes,
io.set_exit_status(1, !IO)
;
Halt = no
)
;
WarnSmart = no
).
%---------------------------------------------------------------------------%
display_compiler_version(ProgressStream, !IO) :-
library.version(Version, Fullarch),
io.write_strings(ProgressStream, [
"Mercury Compiler, version ", Version, ", on ", Fullarch, "\n",
"Copyright (C) 1993-2012 The University of Melbourne\n",
"Copyright (C) 2013-2023 The Mercury team\n"
], !IO).
usage_errors(ErrorStream, Globals, Specs, !IO) :-
io.progname_base("mercury_compile", ProgName, !IO),
io.format(ErrorStream, "%s:\n", [s(ProgName)], !IO),
write_error_specs(ErrorStream, Globals, Specs, !IO).
:- mutable(already_printed_usage, bool, no, ground,
[untrailed, attach_to_io_state]).
usage(ProgressStream, !IO) :-
% usage is called from many places; ensure that we don't print the
% duplicate copies of the message.
get_already_printed_usage(AlreadyPrinted, !IO),
(
AlreadyPrinted = no,
display_compiler_version(ProgressStream, !IO),
io.write_strings(ProgressStream, [
"Usage: mmc [<options>] <arguments>\n",
"Use `mmc --help' for more information.\n"
], !IO),
set_already_printed_usage(yes, !IO)
;
AlreadyPrinted = yes
).
long_usage(ProgressStream, !IO) :-
% long_usage is called from only one place, so can't print duplicate
% copies of the long usage message. We can print both a short and along
% usage message, but there is no simple way to avoid that.
library.version(Version, Fullarch),
Template =
"Name: mmc -- Melbourne Mercury Compiler, version %s on %s\n" ++
"Copyright: Copyright (C) 1993-2012 The University of Melbourne\n" ++
"Copyright (C) 2013-2023 The Mercury team\n" ++
"Usage: mmc [<options>] <arguments>\n" ++
"Arguments:\n" ++
"\tArguments ending in `.m' are assumed to be source file names.\n" ++
"\tArguments that do not end in `.m' " ++
"are assumed to be module names.\n" ++
"\tArguments in the form @file " ++
"are replaced with the contents of the file.\n",
io.format(ProgressStream, Template, [s(Version), s(Fullarch)], !IO),
io.write_string(ProgressStream, "Options:\n", !IO),
options_help(ProgressStream, !IO).
%---------------------------------------------------------------------------%
% This predicate converts a symbolic name for a set of verbosity options
% (a "dump alias") into the string consisting of those options' characters.
%
% The meanings of the option characters are documented by doc/user_guide.texi
% and by compiler/hlds_out.m. The latter is more authoritative :-)
%
% You are welcome to add more aliases.
:- pred convert_dump_alias(string::in, string::out) is semidet.
% None of the 'all' aliases actually include all the options.
convert_dump_alias("ALL", "abcdEfgilmnprstuvzBCDIMPRSTUZ").
convert_dump_alias("allD", "abcdEfgilmnprstuvzBCDMPT").
convert_dump_alias("all", "abcdEfgilmnprstuvzBCMPTZ").
convert_dump_alias("most", "bcdfgilmnprstvzP").
convert_dump_alias("cmp", "bdfgilmnprstuvzP").
convert_dump_alias("trans", "bcdglmnstuvz").
convert_dump_alias("mintrans", "bcdglmnstvz").
convert_dump_alias("codegen", "dfnprsu").
convert_dump_alias("vanessa", "ltuCIU").
convert_dump_alias("min", "ilv").
convert_dump_alias("paths", "cP").
convert_dump_alias("petdr", "din").
convert_dump_alias("detism", "divM").
convert_dump_alias("mm", "bdgvP"). % For debugging minimal model.
convert_dump_alias("osv", "bcdglmnpruvP").
% for debugging --optimize-saved-vars-cell
convert_dump_alias("ctgc", "cdinpGDRS").
convert_dump_alias("vars", "npBis"). % Var instantiations, liveness etc.
convert_dump_alias("statevar", "gvCP").
convert_dump_alias("lco", "agiuvzD").
convert_dump_alias("poly", "vxX").
convert_dump_alias("du", "TL").
%---------------------------------------------------------------------------%
:- pred raw_lookup_bool_option(option_table::in, option::in, bool::out) is det.
raw_lookup_bool_option(OptionTable, Option, BoolValue) :-
map.lookup(OptionTable, Option, OptionValue),
( if OptionValue = bool(BoolValuePrime) then
BoolValue = BoolValuePrime
else
OptionStr = string.string(Option),
unexpected($pred, OptionStr ++ " is not a bool")
).
:- pred raw_lookup_int_option(option_table::in, option::in, int::out) is det.
raw_lookup_int_option(OptionTable, Option, IntValue) :-
map.lookup(OptionTable, Option, OptionValue),
( if OptionValue = int(IntValuePrime) then
IntValue = IntValuePrime
else
OptionStr = string.string(Option),
unexpected($pred, OptionStr ++ " is not an int")
).
:- pred raw_lookup_string_option(option_table::in, option::in,
string::out) is det.
raw_lookup_string_option(OptionTable, Option, StringValue) :-
map.lookup(OptionTable, Option, OptionValue),
( if OptionValue = string(StringValuePrime) then
StringValue = StringValuePrime
else
OptionStr = string.string(Option),
unexpected($pred, OptionStr ++ " is not a string")
).
:- pred raw_lookup_accumulating_option(option_table::in, option::in,
list(string)::out) is det.
raw_lookup_accumulating_option(OptionTable, Option, AccumulatingValue) :-
map.lookup(OptionTable, Option, OptionValue),
( if OptionValue = accumulating(AccumulatingValuePrime) then
AccumulatingValue = AccumulatingValuePrime
else
OptionStr = string.string(Option),
unexpected($pred, OptionStr ++ " is not accumulating")
).
%---------------------------------------------------------------------------%
:- end_module libs.handle_options.
%---------------------------------------------------------------------------%