%---------------------------------------------------------------------------%
% vim: ts=4 sw=4 et ft=mercury
%---------------------------------------------------------------------------%
% Copyright (C) 2016, 2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% This module defines the "terrain" on which the grade solver operates,
% by defining the list of solver variables, the values they may have,
% and the constraints that assignments of values to solver variables
% must satisfy to be a valid solution.
%
% To keep this module easy to read and thus to check, it contains only
% specification-level information; it intentionally leaves out the data
% structures needed during constraint solving that are irrelevant to
% the specification of the problem. Those will be added by the code of
% setup_solver_info in grade_setup.m.
%

:- module grade_lib.grade_spec.
:- interface.

:- import_module list.

%---------------------------------------------------------------------------%
%
% The solver_var_id and solver_var_value_id types embody one of the four
% representations of grades described in compiler/notes/grade_library.html.
% This is the representation used by the solver, because it allows
% the solver to use generic code to implement propagation on *all*
% requirements, regardless of what solver variables and values they involve.
%

:- type solver_var_id
    --->    svar_ac_gcc_regs_avail
    ;       svar_ac_gcc_gotos_avail
    ;       svar_ac_gcc_labels_avail
    ;       svar_ac_low_tag_bits_avail
    ;       svar_ac_size_of_double
    ;       svar_ac_merc_file

    ;       svar_backend
    ;       svar_target
    ;       svar_gcc_conf
    ;       svar_low_tag_bits_use
    ;       svar_stack_len
    ;       svar_trail
    ;       svar_minmodel
    ;       svar_thread_safe
    ;       svar_gc
    ;       svar_deep_prof
    ;       svar_mprof_call
    ;       svar_mprof_time
    ;       svar_mprof_memory
    ;       svar_tscope_prof
            % Paul wants to call the threadscope style profiler a different
            % name, since the data it generates isn't compatible with
            % threadscope anymore. But the name we use here in the solver
            % isn't visible outside, and "tscope" gives readers the right
            % intuition.
    ;       svar_term_size_prof
    ;       svar_debug
    ;       svar_ssdebug
    ;       svar_target_debug
    ;       svar_rbmm
    ;       svar_rbmm_debug
    ;       svar_rbmm_prof
    ;       svar_pregen
    ;       svar_request_single_prec_float
    ;       svar_merc_float.

:- type solver_var_value_id
    --->    svalue_ac_gcc_regs_avail_no
    ;       svalue_ac_gcc_regs_avail_yes

    ;       svalue_ac_gcc_gotos_avail_no
    ;       svalue_ac_gcc_gotos_avail_yes

    ;       svalue_ac_gcc_labels_avail_no
    ;       svalue_ac_gcc_labels_avail_yes

    ;       svalue_ac_low_tag_bits_avail_0
    ;       svalue_ac_low_tag_bits_avail_2
    ;       svalue_ac_low_tag_bits_avail_3
            % Autoconf can detect that 4 low tag bits are available.
            % However, since we never use 4 low tag bits, this is functionally
            % indistinguishable from 3 low tag bits being available,
            % so we require the code setting up the grade settings to be solved
            % to map 4 avail tag bits to 3.

    ;       svalue_ac_size_of_double_eq_ptr
    ;       svalue_ac_size_of_double_ne_ptr

    ;       svalue_ac_merc_file_no
    ;       svalue_ac_merc_file_yes

    ;       svalue_backend_mlds
    ;       svalue_backend_llds

    ;       svalue_target_c
    ;       svalue_target_csharp
    ;       svalue_target_java

    ;       svalue_gcc_conf_none       % used for non-LLDS backends
    ;       svalue_gcc_conf_reg
    ;       svalue_gcc_conf_jump
    ;       svalue_gcc_conf_fast
    ;       svalue_gcc_conf_asm_jump
    ;       svalue_gcc_conf_asm_fast

    ;       svalue_low_tag_bits_use_0
            % If we are using 0 low primary tag bits, then the data
            % representation may use no primary tag at all.
            % Once upon a time, we *could* use (an almost arbitrary number of)
            % high primary tag bits, but we eventually removed that capability,
            % never having used it for anything except benchmarking the extent
            % of the superiority of low tags :-).
            %
            % If we ever wanted to bring back high tags, which is extremely
            % unlikely, we would need a new solver variable named (say)
            % svar_high_tag_bits_use, whose value being any nonzero value
            % would imply svalue_low_tag_bits_use_0.
    ;       svalue_low_tag_bits_use_2
    ;       svalue_low_tag_bits_use_3

    ;       svalue_stack_len_std
    ;       svalue_stack_len_segments
    ;       svalue_stack_len_extend

    ;       svalue_trail_no
    ;       svalue_trail_yes

    ;       svalue_minmodel_no
    ;       svalue_minmodel_stack_copy
    ;       svalue_minmodel_stack_copy_debug
    ;       svalue_minmodel_own_stack
    ;       svalue_minmodel_own_stack_debug

    ;       svalue_thread_safe_c_no
    ;       svalue_thread_safe_c_yes
    ;       svalue_thread_safe_target_native

    ;       svalue_gc_none
    ;       svalue_gc_bdw
    ;       svalue_gc_bdw_debug
    ;       svalue_gc_target_native
    ;       svalue_gc_accurate
    ;       svalue_gc_history

    ;       svalue_deep_prof_no
    ;       svalue_deep_prof_yes

    ;       svalue_mprof_call_no
    ;       svalue_mprof_call_yes

    ;       svalue_mprof_time_no
    ;       svalue_mprof_time_yes

    ;       svalue_mprof_memory_no
    ;       svalue_mprof_memory_yes

    ;       svalue_tscope_prof_no
    ;       svalue_tscope_prof_yes

    ;       svalue_term_size_prof_no
    ;       svalue_term_size_prof_cells
    ;       svalue_term_size_prof_words

    ;       svalue_debug_none
    ;       svalue_debug_debug
    ;       svalue_debug_decldebug

    ;       svalue_ssdebug_no
    ;       svalue_ssdebug_yes

    ;       svalue_target_debug_no
    ;       svalue_target_debug_yes

    ;       svalue_rbmm_no
    ;       svalue_rbmm_yes

    ;       svalue_rbmm_debug_no
    ;       svalue_rbmm_debug_yes

    ;       svalue_rbmm_prof_no
    ;       svalue_rbmm_prof_yes

    ;       svalue_pregen_no
    ;       svalue_pregen_yes

    ;       svalue_request_single_prec_float_no
    ;       svalue_request_single_prec_float_yes

    ;       svalue_merc_float_is_boxed_c_double
    ;       svalue_merc_float_is_unboxed_c_double
    ;       svalue_merc_float_is_unboxed_c_float.

%---------------------------------------------------------------------------%

    % See the documentation of init_solver_var_specs below.
:- type specs_version
    --->    specs_version_0
    ;       specs_version_1.

    % solvar_var_spec(VarId, [ValueId1, ValueId2, ... ValueIdN]) means that
    % the value of VarId must be one of {ValueId1, ValueId2, ValueIdN}.
    %
:- type solver_var_spec
    --->    solver_var_spec(
                svs_var                     :: solver_var_id,
                svs_values                  :: list(solver_var_value_id)
            ).

    % Return the list of solver variables in a grade specification problem,
    % and the list of possible values of each of those solver variables.
    %
    % The order of solver variables in the returned list is the order
    % in which labeling will try to choose a variable to bind.
    %
    % The order of values within each solver variable is the order of
    % preference: labeling will always choose to set the chosen solver variable
    % to the first of its values that has not previously been ruled out.
    %
    % For a few solver variables, the order of "preference" we currently use
    % is not the one we *want* to use. If a grade string does not explicitly
    % specify the use of a specific gc algorithm, we currently assume that this
    % implicitly specifies NOT using any garbage collector, and we likewise
    % assume that the absence of a grade component (.stseg or .exts) that
    % asks for a dynamically sized stack means that the user wants a fixed
    % size stack. This is "version 0" of the preferences, and hence of
    % the solver var specs. However, we want to change the defaults
    % to the use of the Boehm collector and stack segments respectively,
    % which is what version 1 of the preferences calls for.
    %
:- func init_solver_var_specs(specs_version) = list(solver_var_spec).

%---------------------------------------------------------------------------%
%
% A requirement is an implication of the form
%
%   (IfVar `being` IfValue)
%       `implies_that`
%   (ThenVar `is_one_of` [ThenValue1, ..., ThenValueN])
%
% The solver can use the implication to do propagation in both directions.
%
% Forward direction: if the solver knows that IfVar = IfValue, it will
% mark all values of ThenVar that are not among ThenValue1 .. ThenValueN
% as not possible.
%
% Reverse direction: if the solver knows that ThenVar cannot be
% any of the values in ThenValue1 .. ThenValueN, then it will record that
% IfVar = IfValue is not possible either.
%

:- type if_spec
    --->    being(solver_var_id, solver_var_value_id).

:- type then_spec
    --->    is_one_of(solver_var_id, list(solver_var_value_id)).

:- type implication_spec
    --->    implies_that(if_spec, then_spec).

% Besides the implication, each requirement also has an explanation,
% which is a short sentence describing the rationale for the implication.

:- type requirement_spec
    --->    requirement_spec(
                rs_explanation              :: string,
                rs_implication              :: implication_spec
            ).

    % Return the list of requirements that represent the dependencies
    % and incompatibilities between grade components, as represented
    % by their solver vars.
    %
:- func init_requirement_specs = list(requirement_spec).

%---------------------------------------------------------------------------%

:- implementation.

%---------------------------------------------------------------------------%

init_solver_var_specs(SpecsVersion) = Specs :-
    (
        SpecsVersion = specs_version_0,
        StackLenPrefOrder =
            [svalue_stack_len_std, svalue_stack_len_segments,
            svalue_stack_len_extend],
        GcPrefOrder =
            [svalue_gc_none, svalue_gc_bdw, svalue_gc_target_native,
            svalue_gc_bdw_debug, svalue_gc_accurate, svalue_gc_history]
    ;
        SpecsVersion = specs_version_1,
        StackLenPrefOrder =
            [svalue_stack_len_segments, svalue_stack_len_std,
            svalue_stack_len_extend],
        GcPrefOrder =
            [svalue_gc_bdw, svalue_gc_target_native, svalue_gc_bdw_debug,
            svalue_gc_none, svalue_gc_accurate, svalue_gc_history]
    ),

    Specs = [
        % This first group of variables are set (by setup_solver_info)
        % to autoconfigured values before constraint solving starts.
        % By putting them at the start, the very first labeling step will
        % skip over them; later labelling steps won't have to look at them.
        solver_var_spec(svar_ac_gcc_regs_avail,
            [svalue_ac_gcc_regs_avail_no,
            svalue_ac_gcc_regs_avail_yes]),
        solver_var_spec(svar_ac_gcc_gotos_avail,
            [svalue_ac_gcc_gotos_avail_no,
            svalue_ac_gcc_gotos_avail_yes]),
        solver_var_spec(svar_ac_gcc_labels_avail,
            [svalue_ac_gcc_labels_avail_no,
            svalue_ac_gcc_labels_avail_yes]),
        solver_var_spec(svar_ac_low_tag_bits_avail,
            [svalue_ac_low_tag_bits_avail_0,
            svalue_ac_low_tag_bits_avail_2,
            svalue_ac_low_tag_bits_avail_3]),
        solver_var_spec(svar_ac_size_of_double,
            [svalue_ac_size_of_double_eq_ptr,
            svalue_ac_size_of_double_ne_ptr]),
        solver_var_spec(svar_ac_merc_file,
            [svalue_ac_merc_file_no, svalue_ac_merc_file_yes]),

        solver_var_spec(svar_backend,
            [svalue_backend_mlds, svalue_backend_llds]),
        solver_var_spec(svar_target,
            [svalue_target_c, svalue_target_csharp, svalue_target_java]),

        solver_var_spec(svar_gcc_conf,
            % XXX The order of preference here is partially speed,
            % partially how well the grade is tested.
            [svalue_gcc_conf_asm_fast,
            svalue_gcc_conf_reg,
            svalue_gcc_conf_none,
            svalue_gcc_conf_fast,
            svalue_gcc_conf_jump,
            svalue_gcc_conf_asm_jump]),

        solver_var_spec(svar_pregen,
            [svalue_pregen_no, svalue_pregen_yes]),
        solver_var_spec(svar_low_tag_bits_use,
            [svalue_low_tag_bits_use_3, svalue_low_tag_bits_use_2,
            svalue_low_tag_bits_use_0]),

        solver_var_spec(svar_stack_len,
            StackLenPrefOrder),
        solver_var_spec(svar_trail,
            [svalue_trail_no, svalue_trail_yes]),

        solver_var_spec(svar_minmodel,
            [svalue_minmodel_no,
            svalue_minmodel_stack_copy, svalue_minmodel_stack_copy_debug,
            svalue_minmodel_own_stack, svalue_minmodel_own_stack_debug]),

        solver_var_spec(svar_thread_safe,
            [svalue_thread_safe_c_no, svalue_thread_safe_c_yes,
            svalue_thread_safe_target_native]),

        solver_var_spec(svar_gc,
            GcPrefOrder),

        solver_var_spec(svar_deep_prof,
            [svalue_deep_prof_no, svalue_deep_prof_yes]),
        solver_var_spec(svar_mprof_call,
            [svalue_mprof_call_no, svalue_mprof_call_yes]),
        solver_var_spec(svar_mprof_time,
            [svalue_mprof_time_no, svalue_mprof_time_yes]),
        solver_var_spec(svar_mprof_memory,
            [svalue_mprof_memory_no, svalue_mprof_memory_yes]),
        solver_var_spec(svar_tscope_prof,
            [svalue_tscope_prof_no, svalue_tscope_prof_yes]),
        solver_var_spec(svar_term_size_prof,
            [svalue_term_size_prof_no,
            svalue_term_size_prof_cells, svalue_term_size_prof_words]),

        solver_var_spec(svar_debug,
            [svalue_debug_none, svalue_debug_debug, svalue_debug_decldebug]),
        solver_var_spec(svar_ssdebug,
            [svalue_ssdebug_no, svalue_ssdebug_yes]),
        solver_var_spec(svar_target_debug,
            [svalue_target_debug_no, svalue_target_debug_yes]),

        solver_var_spec(svar_rbmm,
            [svalue_rbmm_no, svalue_rbmm_yes]),
        solver_var_spec(svar_rbmm_debug,
            [svalue_rbmm_debug_no, svalue_rbmm_debug_yes]),
        solver_var_spec(svar_rbmm_prof,
            [svalue_rbmm_prof_no, svalue_rbmm_prof_yes]),

        solver_var_spec(svar_request_single_prec_float,
            [svalue_request_single_prec_float_no,
            svalue_request_single_prec_float_yes]),

        solver_var_spec(svar_merc_float,
            [svalue_merc_float_is_unboxed_c_double,
            svalue_merc_float_is_boxed_c_double,
            svalue_merc_float_is_unboxed_c_float])
    ].

%---------------------------------------------------------------------------%

init_requirement_specs = [
% Requirements of values of svar_ac_gcc_regs_avail.
    % None. The value is set by configure.

% Requirements of values of svar_ac_gcc_gotos_avail.
    % None. The value is set by configure.

% Requirements of values of svar_ac_gcc_labels_avail.
    % None. The value is set by configure.

% Requirements of values of svar_ac_low_tag_bits_avail.
    % None. The value is set by configure.

% Requirements of values of svar_ac_size_of_double.
    % None. The value is set by configure.

% Requirements of values of svar_ac_merc_file.
    % None. The value is set by configure.

% Requirements of values of svar_backend.
    requirement_spec(
        "Using the MLDS backend requires targeting C, C# or Java.",
        (svar_backend `being` svalue_backend_mlds) `implies_that`
        (svar_target `is_one_of`
            [svalue_target_c, svalue_target_csharp, svalue_target_java])
    ),
    requirement_spec(
        "Using the LLDS backend requires targeting C.",
        (svar_backend `being` svalue_backend_llds) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "The .c_debug grade component is not supported by the LLDS backend.",
        % We could possibly support it for grades that do not use any gcc
        % extensions to C, but invoking gdb on object code that uses
        % e.g. gcc extensions to execute gotos between modules without going
        % through function prologues/epilogues is not going to be
        % remotely useful.
        (svar_backend `being` svalue_backend_llds) `implies_that`
        (svar_target_debug `is_one_of` [svalue_target_debug_no])
    ),

    requirement_spec(
        "The use of gcc extensions makes sense only for the LLDS backend.",
        (svar_backend `being` svalue_backend_mlds) `implies_that`
        (svar_gcc_conf `is_one_of` [svalue_gcc_conf_none])
    ),

% Requirements of values of svar_target.
    requirement_spec(
        "Targeting C# requires the MLDS backend.",
        (svar_target `being` svalue_target_csharp) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_mlds])
    ),
    requirement_spec(
        "Targeting Java requires the MLDS backend.",
        (svar_target `being` svalue_target_java) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_mlds])
    ),

    requirement_spec(
        "C does not have a native garbage collector.",
        (svar_target `being` svalue_target_c) `implies_that`
        (svar_gc `is_one_of` [svalue_gc_bdw, svalue_gc_bdw_debug,
            svalue_gc_accurate, svalue_gc_history, svalue_gc_none])
    ),
    requirement_spec(
        "Targeting C# requires target native gc.",
        (svar_target `being` svalue_target_csharp) `implies_that`
        (svar_gc `is_one_of` [svalue_gc_target_native])
    ),
    requirement_spec(
        "Targeting Java requires target native gc.",
        (svar_target `being` svalue_target_java) `implies_that`
        (svar_gc `is_one_of` [svalue_gc_target_native])
    ),

    requirement_spec(
        "C does not have its own native threading model.",
        (svar_target `being` svalue_target_c) `implies_that`
        (svar_thread_safe `is_one_of`
            [svalue_thread_safe_c_no, svalue_thread_safe_c_yes])
    ),
    requirement_spec(
        "Generating C# implies using the C# threading model.",
        (svar_target `being` svalue_target_csharp) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_target_native])
    ),
    requirement_spec(
        "Generating Java implies using the Java threading model.",
        (svar_target `being` svalue_target_java) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_target_native])
    ),

% These are covered by a single requirement from spf back to target.
%   requirement_spec(
%       "Targeting C# is incompatible with single-precision floats.",
%       (svar_target `being` svalue_target_csharp) `implies_that`
%       (svar_single_prec_float `is_one_of` [svalue_single_prec_float_no])
%   ),
%   requirement_spec(
%       "Targeting Java is incompatible with single-precision floats.",
%       (svar_target `being` svalue_target_java) `implies_that`
%       (svar_single_prec_float `is_one_of` [svalue_single_prec_float_no])
%   ),

% Requirements of values of svar_gcc_conf.
    % These requirements are expressed in reverse.
    requirement_spec(
        "Using gcc register extensions requires them to be available.",
        (svar_ac_gcc_regs_avail `being` svalue_ac_gcc_regs_avail_no)
            `implies_that`
        (svar_gcc_conf `is_one_of`
            [svalue_gcc_conf_none,
            svalue_gcc_conf_jump,
            svalue_gcc_conf_asm_jump])
    ),
    requirement_spec(
        "Using gcc nonlocal gotos requires them to be available.",
        (svar_ac_gcc_gotos_avail `being` svalue_ac_gcc_gotos_avail_no)
            `implies_that`
        (svar_gcc_conf `is_one_of`
            [svalue_gcc_conf_none,
            svalue_gcc_conf_reg])
    ),
    requirement_spec(
        "Using gcc asm labels requires them to be available.",
        (svar_ac_gcc_labels_avail `being` svalue_ac_gcc_labels_avail_no)
            `implies_that`
        (svar_gcc_conf `is_one_of`
            [svalue_gcc_conf_none,
            svalue_gcc_conf_reg,
            svalue_gcc_conf_jump,
            svalue_gcc_conf_fast])
    ),

% Requirements of values of svar_pregen.
    requirement_spec(
        "Pregenerated code always targets C.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Pregenerated code always uses none, reg, asm_fast or hlc.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_gcc_conf `is_one_of`
            [svalue_gcc_conf_none, svalue_gcc_conf_reg,
            svalue_gcc_conf_asm_fast])
    ),
    requirement_spec(
        "Pregenerated code uses 2 low tag bits.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_low_tag_bits_use `is_one_of` [svalue_low_tag_bits_use_2])
    ),
    % XXX We should require stack segments on llds.
    % With version 1 of the requirements, we prefer stack segments when
    % possible, and with the llds backend, they are possible,
    % but this is not as good as a requirement, since it can be overridden.
    % Unfortunately, we don't (yet) support implications with *two*
    % conditions, such as "pregen=yes and backend=llds implies stack segments".
    % XXX Should we relax the following restriction?
    requirement_spec(
        "Pregenerated code never uses a trail.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_trail `is_one_of` [svalue_trail_no])
    ),
    requirement_spec(
        "Pregenerated code never uses minimal model.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_minmodel `is_one_of` [svalue_minmodel_no])
    ),
    requirement_spec(
        "Pregenerated code never supports thread safety.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "Pregenerated code always uses the Boehm garbage collector.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_gc `is_one_of` [svalue_gc_bdw])
    ),
    requirement_spec(
        "Pregenerated code never supports deep profiling.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_deep_prof `is_one_of` [svalue_deep_prof_no])
    ),
    requirement_spec(
        "Pregenerated code never supports mprof-style profiling.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_mprof_call `is_one_of` [svalue_mprof_call_no])
    ),
    requirement_spec(
        "Pregenerated code never supports term size profiling.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_term_size_prof `is_one_of` [svalue_term_size_prof_no])
    ),
    requirement_spec(
        "Pregenerated code never supports debugging.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_debug `is_one_of` [svalue_debug_none])
    ),
    requirement_spec(
        "Pregenerated code never supports source-to-source debugging.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_ssdebug `is_one_of` [svalue_ssdebug_no])
    ),
    requirement_spec(
        "Pregenerated code never supports target language debugging.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_target_debug `is_one_of` [svalue_target_debug_no])
    ),
    requirement_spec(
        "Pregenerated code never supports region based memory management.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_rbmm `is_one_of` [svalue_rbmm_no])
    ),
    requirement_spec(
        "Pregenerated code always uses boxed double-precision floats.",
        (svar_pregen `being` svalue_pregen_yes) `implies_that`
        (svar_merc_float `is_one_of` [svalue_merc_float_is_boxed_c_double])
    ),

% Requirements of values of svar_low_tag_bits_use.
    requirement_spec(
        "Using 2 low tag bits needs at least 2 low tag bits to be available.",
        (svar_low_tag_bits_use `being` svalue_low_tag_bits_use_2)
            `implies_that`
        (svar_ac_low_tag_bits_avail `is_one_of`
            [svalue_ac_low_tag_bits_avail_2, svalue_ac_low_tag_bits_avail_3])
    ),
    requirement_spec(
        "Using 3 low tag bits needs at least 3 low tag bits to be available.",
        (svar_low_tag_bits_use `being` svalue_low_tag_bits_use_3)
            `implies_that`
        (svar_ac_low_tag_bits_avail `is_one_of`
            [svalue_ac_low_tag_bits_avail_3])
    ),

% Requirements of values of svar_stack_segments.
    requirement_spec(
        "Stack segments require the LLDS backend.",
        (svar_stack_len `being` svalue_stack_len_segments) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Stack extension requires the LLDS backend.",
        (svar_stack_len `being` svalue_stack_len_extend) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),

% Requirements of values of svar_trail.
    requirement_spec(
        "Trailing requires targeting C.",
        (svar_trail `being` svalue_trail_yes) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Trailing interferes with minimal model tabling.",
        (svar_trail `being` svalue_trail_yes) `implies_that`
        (svar_minmodel `is_one_of` [svalue_minmodel_no])
    ),

% Requirements of values of svar_minmodel.
    requirement_spec(
        "Minimal model tabling requires the LLDS backend.",
        (svar_minmodel `being` svalue_minmodel_stack_copy)
            `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Minimal model tabling requires the LLDS backend.",
        (svar_minmodel `being` svalue_minmodel_stack_copy_debug)
            `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Minimal model tabling requires the LLDS backend.",
        (svar_minmodel `being` svalue_minmodel_own_stack)
            `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Minimal model tabling requires the LLDS backend.",
        (svar_minmodel `being` svalue_minmodel_own_stack_debug)
            `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Minimal model tabling requires Boehm-Demers-Weiser gc.",
        (svar_minmodel `being` svalue_minmodel_stack_copy)
            `implies_that`
        (svar_gc `is_one_of`
            [svalue_gc_none, svalue_gc_bdw, svalue_gc_bdw_debug])
    ),
    requirement_spec(
        "Minimal model tabling requires Boehm-Demers-Weiser gc.",
        (svar_minmodel `being` svalue_minmodel_stack_copy_debug)
            `implies_that`
        (svar_gc `is_one_of`
            [svalue_gc_none, svalue_gc_bdw, svalue_gc_bdw_debug])
    ),
    requirement_spec(
        "Minimal model tabling requires Boehm-Demers-Weiser gc.",
        (svar_minmodel `being` svalue_minmodel_own_stack)
            `implies_that`
        (svar_gc `is_one_of`
            [svalue_gc_none, svalue_gc_bdw, svalue_gc_bdw_debug])
    ),
    requirement_spec(
        "Minimal model tabling requires Boehm-Demers-Weiser gc.",
        (svar_minmodel `being` svalue_minmodel_own_stack_debug)
            `implies_that`
        (svar_gc `is_one_of`
            [svalue_gc_none, svalue_gc_bdw, svalue_gc_bdw_debug])
    ),
    requirement_spec(
        "Minimal model tabling does not respect thread safety.",
        (svar_minmodel `being` svalue_minmodel_stack_copy)
            `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "Minimal model tabling does not respect thread safety.",
        (svar_minmodel `being` svalue_minmodel_stack_copy_debug)
            `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    % XXX Do svalue_minmodel_own_stack{,_debug} imply svalue_thread_safe_c_no?
    % For now, since the implementation of own stack minimal model
    % is not yet complete, we need not include its requirements in the list,
    % and not including them should make constraint solving a tiny bit faster.

% Requirements of values of svar_thread_safe.
    % None. There are some settings of other solver variables
    % that are incompatible with thread safety, but those incompatibilities
    % are expressed by requirements listed under the *other* solver variable.

% Requirements of values of svar_gc.
    requirement_spec(
        "Boehm-Demers-Weiser gc requires targeting C.",
        (svar_gc `being` svalue_gc_bdw) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Boehm-Demers-Weiser debug gc requires targeting C.",
        (svar_gc `being` svalue_gc_bdw_debug) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Accurate gc requires targeting C.",
        (svar_gc `being` svalue_gc_accurate) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Accurate gc conflicts with threading.",
        (svar_gc `being` svalue_gc_accurate) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "History gc requires targeting C.",
        (svar_gc `being` svalue_gc_history) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "History gc conflicts with threading.",
        (svar_gc `being` svalue_gc_history) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),

% Requirements of values of svar_deep_prof.
    requirement_spec(
        "Deep profiling requires the LLDS backend.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Deep profiling interferes with minimal model tabling.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_minmodel `is_one_of` [svalue_minmodel_no])
    ),
    requirement_spec(
        "Deep profiling does not work for multithreaded programs.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "Deep profiling is incompatible with mprof call profiling.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_mprof_call `is_one_of` [svalue_mprof_call_no])
    ),
    requirement_spec(
        "Deep profiling is incompatible with mprof time profiling.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_mprof_time `is_one_of` [svalue_mprof_time_no])
    ),
    requirement_spec(
        "Deep profiling is incompatible with mprof memory profiling.",
        (svar_deep_prof `being` svalue_deep_prof_yes) `implies_that`
        (svar_mprof_memory `is_one_of` [svalue_mprof_memory_no])
    ),

% Requirements of values of svar_mprof_call.
    requirement_spec(
        "Mprof call profiling requires targeting C.",
        (svar_mprof_call `being` svalue_mprof_call_yes) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Mprof call profiling interferes with minimal model tabling.",
        (svar_mprof_call `being` svalue_mprof_call_yes) `implies_that`
        (svar_minmodel `is_one_of` [svalue_minmodel_no])
    ),
    requirement_spec(
        "Mprof call profiling does not work for multithreaded programs.",
        (svar_mprof_call `being` svalue_mprof_call_yes) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),

% Requirements of values of svar_mprof_time.
    requirement_spec(
        "Mprof time profiling requires targeting C.",
        (svar_mprof_time `being` svalue_mprof_time_yes) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Mprof time profiling requires mprof call profiling.",
        % XXX runtime/mercury_grade.h allows MR_MPROF_PROFILE_TIME without
        % MR_MPROF_PROFILE_CALLS, but calls the combination "useless".
        (svar_mprof_time `being` svalue_mprof_time_yes) `implies_that`
        (svar_mprof_call `is_one_of` [svalue_mprof_call_yes])
    ),

% Requirements of values of svar_mprof_memory.
    requirement_spec(
        "Mprof memory profiling requires targeting C.",
        (svar_mprof_memory `being` svalue_mprof_memory_yes) `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Mprof memory profiling requires mprof call profiling.",
        (svar_mprof_memory `being` svalue_mprof_memory_yes) `implies_that`
        (svar_mprof_call `is_one_of` [svalue_mprof_call_yes])
    ),

% Requirements of values of svar_tscope_prof.
    requirement_spec(
        "Threadscope style profiling requires the LLDS backend.",
        (svar_tscope_prof `being` svalue_tscope_prof_yes) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Threadscope style profiling requires thread safe code.",
        (svar_tscope_prof `being` svalue_tscope_prof_yes) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_yes])
    ),

% Requirements of values of svar_term_size_prof.
    requirement_spec(
        "Term size profiling requires the LLDS backend.",
        (svar_term_size_prof `being` svalue_term_size_prof_cells) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Term size profiling requires the LLDS backend.",
        (svar_term_size_prof `being` svalue_term_size_prof_words) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Term size profiling is incompatible with thread safety.",
        (svar_term_size_prof `being` svalue_term_size_prof_cells) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "Term size profiling is incompatible with thread safety.",
        (svar_term_size_prof `being` svalue_term_size_prof_words) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),

% Requirements of values of svar_debug.
    requirement_spec(
        "Debugging requires the LLDS backend.",
        (svar_debug `being` svalue_debug_debug) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Declarative debugging requires the LLDS backend.",
        (svar_debug `being` svalue_debug_decldebug) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),
    requirement_spec(
        "Debugging does not work for multithreaded programs.",
        (svar_debug `being` svalue_debug_debug) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),
    requirement_spec(
        "Declarative debugging does not work for multithreaded programs.",
        (svar_debug `being` svalue_debug_decldebug) `implies_that`
        (svar_thread_safe `is_one_of` [svalue_thread_safe_c_no])
    ),

% Requirements of values of svar_ssdebug.
    requirement_spec(
        "Source-to-source debugging does not make sense for the LLDS backend.",
        (svar_ssdebug `being` svalue_ssdebug_yes) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_mlds])
    ),
    requirement_spec(
        "Source-to-source debugging does not work for multithreaded programs.",
        (svar_ssdebug `being` svalue_ssdebug_yes) `implies_that`
        (svar_thread_safe `is_one_of`
            [svalue_thread_safe_c_no, svalue_thread_safe_target_native])
    ),

% Requirements of values of svar_target_debug.
    % None. It should be applicable in all cases.

% Requirements of values of svar_rbmm.
    requirement_spec(
        "Region based memory management requires the LLDS backend.",
        (svar_rbmm `being` svalue_rbmm_yes) `implies_that`
        (svar_backend `is_one_of` [svalue_backend_llds])
    ),

% Requirements of values of svar_request_single_prec_float.
    requirement_spec(
        "Single precision floats are available only when targeting C.",
        (svar_request_single_prec_float `being`
            svalue_request_single_prec_float_yes)
            `implies_that`
        (svar_target `is_one_of` [svalue_target_c])
    ),
    requirement_spec(
        "Single precision floats are available when requested.",
        % Since nothing forbids svalue_merc_float_is_unboxed_c_float,
        % this implication should always succeed.
        (svar_request_single_prec_float `being`
            svalue_request_single_prec_float_yes)
            `implies_that`
        (svar_merc_float `is_one_of` [svalue_merc_float_is_unboxed_c_float])
    ),

% Requirements of values of svar_merc_float.
    requirement_spec(
        "Unboxed double precision floats require pointer-sized doubles.",
        (svar_merc_float `being` svalue_merc_float_is_unboxed_c_double)
            `implies_that`
        (svar_ac_size_of_double `is_one_of` [svalue_ac_size_of_double_eq_ptr])
    )
].

%---------------------------------------------------------------------------%
:- end_module grade_lib.grade_spec.
%---------------------------------------------------------------------------%
