%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%

:- pred get_default_opt_level(option_table::in, int::out) is det.

get_default_opt_level(OptionTable, DefaultOptLevel) :-
    % default_opt_level takes a "-O<n>" string for compatibility.
    lookup_string_option(OptionTable, default_opt_level, Str0),
    Str = string.strip(Str0),
    ( if
        string.remove_prefix("-O", Str, Suffix),
        string.to_int(string.lstrip(Suffix), Int)
    then
        DefaultOptLevel = Int
    else
        DefaultOptLevel = 2
    ).

:- pred set_opts_upto_level(option_table::in, int::in, int::in,
    opt_tuple::in, opt_tuple::out,
    maybe_seen_opt_level::in, maybe_seen_opt_level::out) is det.

set_opts_upto_level(OptionTable, Cur, Max,
        !OptTuple, !MaybeSeenOptLevel) :-
    ( if
        Cur =< Max,
        opts_enabled_at_level(Cur, _Desc, LevelOptExpOptions)
    then
        UndocFunc = (func(doc_oo(O, _, _)) = O),
        LevelOptOptions = list.map(UndocFunc, LevelOptExpOptions),
        list.foldl2(update_opt_tuple(from_opt_level, OptionTable),
            LevelOptOptions, !OptTuple, !MaybeSeenOptLevel),
        set_opts_upto_level(OptionTable, Cur + 1, Max,
            !OptTuple, !MaybeSeenOptLevel)
    else
        true
    ).

:- pred set_opts_for_space(opt_tuple::in, opt_tuple::out) is det.

set_opts_for_space(!OptTuple) :-
    UnneededCopyLimit = !.OptTuple ^ ot_opt_unneeded_code_copy_limit,
    !OptTuple ^ ot_opt_unneeded_code_copy_limit :=
        int.min(UnneededCopyLimit, 1),
    !OptTuple ^ ot_opt_dead_procs := opt_dead_procs,
    !OptTuple ^ ot_opt_labels := opt_labels,
    !OptTuple ^ ot_opt_dup_instrs_llds := opt_dup_instrs_llds,
    !OptTuple ^ ot_opt_dup_procs_llds := opt_dup_procs_llds,
    !OptTuple ^ ot_opt_fulljumps := opt_fulljumps,
    !OptTuple ^ ot_opt_llds_reassign := opt_llds_reassign,
    !OptTuple ^ ot_inline_alloc := inline_alloc,
    !OptTuple ^ ot_use_macro_for_redo_fail := use_macro_for_redo_fail,
    !OptTuple ^ ot_opt_loop_invariants := do_not_opt_loop_invariants.

opts_enabled_at_level(0, [
    "Aim to minimize overall compilation time."
], [
    doc_oo(oo_use_llds_common_data(yes),
                        optopt_use_llds_common_data,            bool(yes)),
    doc_oo(oo_optimize_llds(yes),       optopt_optimize_llds,   bool(yes)),
    doc_oo(oo_optimize_mlds(yes),       optopt_optimize_mlds,   bool(yes)),
    doc_oo(oo_opt_repeat(1),            optopt_repeat_opts,     int(1)),
    doc_oo(oo_peep_llds(yes),           optopt_peep_llds,       bool(yes)),
    doc_oo(oo_peep_llds_mkword(yes),    optopt_peep_llds_mkword, bool(yes)),
    doc_oo(oo_peep_mlds(yes),           optopt_peep_mlds,       bool(yes)),
    doc_oo(oo_use_static_ground_cells(yes),
                        optopt_use_static_ground_cells,         bool(yes)),
    doc_oo(oo_use_smart_indexing(yes),  optopt_use_smart_indexing,  bool(yes)),
    doc_oo(oo_use_smart_indexing_atomic(yes),
                        optopt_use_smart_indexing_atomic,       bool(yes)),
    doc_oo(oo_use_smart_indexing_string(yes),
                        optopt_use_smart_indexing_string,       bool(yes)),
    doc_oo(oo_use_smart_indexing_tag(yes),
                        optopt_use_smart_indexing_tag,          bool(yes)),
    doc_oo(oo_use_smart_indexing_float(yes),
                        optopt_use_smart_indexing_float,        bool(yes)),
    doc_oo(oo_opt_jumps(yes),           optopt_opt_jumps,       bool(yes)),
    doc_oo(oo_opt_labels(yes),          optopt_opt_labels,      bool(yes)),
    doc_oo(oo_opt_dead_procs(yes),      optopt_opt_dead_procs,      bool(yes)),
    doc_oo(oo_elim_excess_assigns(yes), optopt_elim_excess_assigns, bool(yes))
]).
opts_enabled_at_level(1, [
    "Apply optimizations which are cheap and have a good payoff",
    "while still keeping compilation time small."
], [
    doc_oo(oo_use_local_vars_llds(yes), optopt_use_local_vars_llds, bool(yes)),
    % XXX We want `gcc -O1'
    doc_oo(oo_opt_c(yes),               optopt_c_optimize,      bool(yes)),
    doc_oo(oo_opt_frames(yes),          optopt_opt_frames,      bool(yes)),
    % We ignore oo_opt_delay_slot if have_delay_slot = no.
    doc_oo(oo_opt_delay_slot(yes),      optopt_opt_delay_slot,  bool(yes)),
    doc_oo(oo_opt_middle_rec_llds(yes), optopt_opt_middle_rec_llds, bool(yes)),
    doc_oo(oo_emit_c_loops(yes),        optopt_emit_c_loops,    bool(yes)),
    doc_oo(oo_opt_mlds_tailcalls(yes),  optopt_opt_mlds_tailcalls, bool(yes))
]).
opts_enabled_at_level(2, [
    "Apply optimizations which have a good payoff relative to their cost;",
    "but include optimizations which are more costly than with -O1."
], [
    doc_oo(oo_opt_fulljumps(yes),       optopt_opt_fulljumps,   bool(yes)),
    doc_oo(oo_opt_repeat(3),            optopt_repeat_opts,     int(3)),
    doc_oo(oo_opt_dup_instrs_llds(yes), optopt_opt_dup_instrs_llds, bool(yes)),
    doc_oo(oo_opt_follow_code(yes),     optopt_opt_follow_code, bool(yes)),
    doc_oo(oo_prop_constants(yes),      optopt_prop_constants,  bool(yes)),
    doc_oo(oo_inline_simple(yes),       optopt_inline_simple,   bool(yes)),
    doc_oo(oo_inline_single_use(yes),   optopt_inline_single_use, bool(yes)),
    doc_oo(oo_inline_compound_threshold(10),
                        optopt_inline_compound_threshold,       int(10)),
    doc_oo(oo_opt_common_structs(yes),  optopt_opt_common_structs,  bool(yes)),
    doc_oo(oo_spec_types_user_guided(yes),
                        optopt_spec_types_user_guided,          bool(yes)),
    doc_oo(oo_opt_simple_neg_llds(yes), optopt_opt_simple_neg_llds, bool(yes)),
    doc_oo(oo_merge_code_after_switch(yes),
                        optopt_merge_code_after_switch,         bool(yes)),
    doc_oo(oo_opt_mlds_inits(yes),      optopt_opt_mlds_inits,  bool(yes)),
    doc_oo(oo_split_switch_arms(yes),   optopt_split_switch_arms,   bool(yes))
]).
opts_enabled_at_level(3, [
    "Apply optimizations which usually have a good payoff even if they",
    "increase compilation time quite a bit."
], [
    doc_oo(oo_opt_saved_vars_const(yes),
                                optopt_opt_saved_vars_const,    bool(yes)),
    doc_oo(oo_opt_unused_args(yes),     optopt_opt_unused_args,     bool(yes)),
    doc_oo(oo_opt_higher_order(yes),    optopt_opt_higher_order,    bool(yes)),
    doc_oo(oo_deforest(yes),            optopt_deforest,        bool(yes)),
    doc_oo(oo_prop_constraints(yes),    optopt_prop_constraints,    bool(yes)),
    doc_oo(oo_prop_local_constraints(yes),
                                optopt_prop_local_constraints,  bool(yes)),
    doc_oo(oo_opt_llds_reassign(yes),   optopt_opt_llds_reassign,   bool(yes)),
    doc_oo(oo_opt_repeat(4),            optopt_repeat_opts,     int(4))
]).
opts_enabled_at_level(4, [
    "Apply optimizations which may have some payoff even if they",
    "increase compilation time quite a bit.",
    "",
    "Currently this enables the use of local variables",
    "and increases the inlining thresholds."
], [
    doc_oo(oo_inline_simple_threshold(8),
                            optopt_inline_simple_threshold,     int(8)),
    doc_oo(oo_inline_compound_threshold(20),
                            optopt_inline_compound_threshold,   int(20)),
    doc_oo(oo_higher_order_size_limit(30),
                            optopt_higher_order_size_limit,     int(30))
]).
opts_enabled_at_level(5, [
    "Apply optimizations which may have some payoff even if they",
    "increase compilation time a lot.",
    "",
    "Currently this enables the search for construction unifications that",
    "can be delayed past failing computations, allows more passes of the",
    "low-level optimizations, and increases the inlining thresholds",
    "still further. We also enable eliminate_local_vars only at",
    "this level, because that pass is implemented pretty inefficiently."
], [
    doc_oo(oo_opt_repeat(5),            optopt_repeat_opts,     int(5)),
    doc_oo(oo_delay_constructs(yes),    optopt_delay_constructs, bool(yes)),
    doc_oo(oo_inline_compound_threshold(100),
                            optopt_inline_compound_threshold,   int(100)),
    doc_oo(oo_higher_order_size_limit(40),
                            optopt_higher_order_size_limit,     int(40)),
    doc_oo(oo_elim_local_vars_mlds(yes),
                            optopt_elim_local_vars_mlds,        bool(yes)),
    doc_oo(oo_opt_loop_invariants(yes), optopt_opt_loop_invariants, bool(yes))
]).
opts_enabled_at_level(6, [
    "Apply optimizations which may have any payoff even if they",
    "increase compilation time to completely unreasonable levels.",
    "",
    "Currently this just enables inlining of GC_malloc(), redo(), and fail()."
], [
    doc_oo(oo_inline_alloc(yes),        optopt_inline_alloc,    bool(yes)),
    doc_oo(oo_use_macro_for_redo_fail(yes),
                            optopt_use_macro_for_redo_fail,     bool(yes))
]).

% Many optimization options are not enabled at any level.
% We stopped keeping track of them a long time ago.
% While we did keep track, we listed the reasons for not automatically
% enabling the following optimizations.
%
%   checked_nondet_tailcalls:
%       This is deliberate, because the transformation might make
%       code run slower.
%
%   unneeded_code:
%       Because it can cause slowdowns at high optimization levels;
%       cause unknown.
%
%   introduce_accumulators:
%       Disabled until a bug in extras/trailed_update/var.m is resolved.
%
%   optimize_constructor_last_call:
%       Not a speedup in general.

%---------------------------------------------------------------------------%
:- end_module libs.optimization_options.
%---------------------------------------------------------------------------%
