// vim: ts=4 sw=4 expandtab ft=c

// Copyright (C) 1997-2012 The University of Melbourne.
// Copyright (C) 2013-2014, 2016-2018, 2020 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.

// The main purpose of this file is to define the MR_GRADE_VAR macro,
// which we use to get the linker to generate an error if the object files
// linked into an executable were compiled with inconsistent grades.
//
// We do this by (a) having mercury_grade.c define a variable whose name
// is MR_GRADE_VAR; and (b) having all .c files generated by the Mercury
// compiler include a reference to the variable whose name is MR_GRADE_VAR.
// The latter will succeed only if the value of the MR_GRADE_VAR macro
// in that object file is the same as its value during the compilation
// of mercury_grade.c.
//
// The value of MR_GRADE_VAR is the link-check version of the Mercury grade.
// This file also computes the user-visible version of the grade into
// the macro MR_GRADE_OPT. This user-visible version of the grade
//
// - uses dots instead of underscores to separate grade components;
// - omits grade components that are the result of autoconfiguration and are
//   of no interest to users, such as _tags2; and
// - omits version numbers from the grade components that have them.
//
// Any conditional compilation macros that affect link compatibility
// should be included here. For documentation on the meaning of these macros,
// see runtime/mercury_conf_param.h.
//
// There are many constraints that apply to grade components. The ones
// that apply between two or more grade components are enforced here,
// with one exception noted below, which is enforced in mercury.h.
// The constraints that also involve macros that do *not* represent
// grade components (such as MR_CLANG) are enforced in mercury_conf_param.h.
//
// IMPORTANT: any changes here may also require changes to
//      runtime/mercury_conf_param.h
//      scripts/init_grade_options.sh-subr
//      scripts/canonical_grade.sh-subr
//      scripts/parse_grade_options.sh-subr
//      scripts/final_grade_options.sh-subr
//      scripts/mgnuc.in
//      scripts/ml.in
//      compiler/handle_options.m
//      compiler/compile_target_code.m
//      configure.ac

#ifndef MERCURY_GRADES_H
#define MERCURY_GRADES_H

#include "mercury_std.h"        // for MR_STRINGIFY and MR_PASTE2
#include "mercury_tags.h"       // for MR_TAGBITS

// The following series of tests define two macros piece by piece.
//
// MR_GRADE encodes the value of all the grade options; we use it to ensure
// that all object files linked together have the same values of these options.
//
// MR_GRADE_OPT encodes the values of only the grade options that it makes
// sense to change on the compiler command line.

// Part 0 of the grade is a binary compatibility version number.
// You should increment it any time you make a change that breaks
// binary backwards compatibility.
// Note that the binary compatibility version number has no direct
// relationship with the source release number (which is in ../VERSION).
//
// It is a good idea to inspect all code for RTTI version number checks
// and remove them when increasing the binary compatibility version number.
// Searching for MR_RTTI_VERSION__ should find all code related to the
// RTTI version number.
//
// The MR_GRADE_EXEC_TRACE_VERSION_NO, MR_GRADE_DEEP_PROF_VERSION_NO and
// MR_GRADE_LLC_PAR_VERSION_NO macros should be incremented when a change
// breaks binary backwards compatibility only in debugging, deep profiling
// and low-level C parallel grades respectively.

#define MR_GRADE_PART_0 v19_
#define MR_GRADE_EXEC_TRACE_VERSION_NO  12
#define MR_GRADE_DEEP_PROF_VERSION_NO   4
#define MR_GRADE_LLC_PAR_VERSION_NO 1
#define MR_GRADE_TRAIL_VERSION_NO 1

#ifdef MR_HIGHLEVEL_CODE

  #define MR_GRADE_PART_1               MR_PASTE2(MR_GRADE_PART_0, hlc)
  #define MR_GRADE_OPT_PART_1           "hlc"

  // Grade component 2 used to specify the use/nonuse of gcc nested functions.

  // This grade component is repeated below version information.
  #ifdef MR_THREAD_SAFE
    #define MR_GRADE_PART_3             MR_PASTE2(MR_GRADE_PART_1, _par)
    #define MR_GRADE_OPT_PART_3         MR_GRADE_OPT_PART_1 ".par"
  #else
    #define MR_GRADE_PART_3             MR_GRADE_PART_1
    #define MR_GRADE_OPT_PART_3         MR_GRADE_OPT_PART_1
  #endif

#else // ! MR_HIGHLEVEL_CODE

  #ifdef MR_USE_ASM_LABELS
    #define MR_GRADE_PART_1             MR_PASTE2(MR_GRADE_PART_0, asm_)
    #define MR_GRADE_OPT_PART_1         "asm_"
  #else
    #define MR_GRADE_PART_1             MR_GRADE_PART_0
    #define MR_GRADE_OPT_PART_1         ""
  #endif

  #ifdef MR_USE_GCC_NONLOCAL_GOTOS
    #ifdef MR_USE_GCC_GLOBAL_REGISTERS
      #define MR_GRADE_PART_2           MR_PASTE2(MR_GRADE_PART_1, fast)
      #define MR_GRADE_OPT_PART_2       MR_GRADE_OPT_PART_1 "fast"
    #else
      #define MR_GRADE_PART_2           MR_PASTE2(MR_GRADE_PART_1, jump)
      #define MR_GRADE_OPT_PART_2       MR_GRADE_OPT_PART_1 "jump"
    #endif
  #else
    #ifdef MR_USE_GCC_GLOBAL_REGISTERS
      #define MR_GRADE_PART_2           MR_PASTE2(MR_GRADE_PART_1, reg)
      #define MR_GRADE_OPT_PART_2       MR_GRADE_OPT_PART_1 "reg"
    #else
      #define MR_GRADE_PART_2           MR_PASTE2(MR_GRADE_PART_1, none)
      #define MR_GRADE_OPT_PART_2       MR_GRADE_OPT_PART_1 "none"
    #endif
  #endif

  // This grade component is repeated above without the version information.

  #ifdef MR_THREAD_SAFE
    #define MR_GRADE_PART_3       MR_PASTE3(MR_GRADE_PART_2, _par, MR_GRADE_LLC_PAR_VERSION_NO)
    #define MR_GRADE_OPT_PART_3   MR_GRADE_OPT_PART_2 ".par"
  #else
    #define MR_GRADE_PART_3       MR_GRADE_PART_2
    #define MR_GRADE_OPT_PART_3   MR_GRADE_OPT_PART_2
  #endif

#endif // ! MR_HIGHLEVEL_CODE

#if defined(MR_HGC)
  #define MR_GRADE_PART_4       MR_PASTE2(MR_GRADE_PART_3, _hgc)
  #define MR_GRADE_OPT_PART_4   MR_GRADE_OPT_PART_3 ".hgc"
#elif defined(MR_BOEHM_GC_DEBUG)
  #define MR_GRADE_PART_4       MR_PASTE2(MR_GRADE_PART_3, _gcd)
  #define MR_GRADE_OPT_PART_4   MR_GRADE_OPT_PART_3 ".gcd"
#elif defined(MR_BOEHM_GC) || defined(MR_CONSERVATIVE_GC)
  #define MR_GRADE_PART_4       MR_PASTE2(MR_GRADE_PART_3, _gc)
  #define MR_GRADE_OPT_PART_4   MR_GRADE_OPT_PART_3 ".gc"
#elif defined(MR_NATIVE_GC)
  #define MR_GRADE_PART_4       MR_PASTE2(MR_GRADE_PART_3, _agc)
  #define MR_GRADE_OPT_PART_4   MR_GRADE_OPT_PART_3 ".agc"
#else
  #define MR_GRADE_PART_4       MR_GRADE_PART_3
  #define MR_GRADE_OPT_PART_4   MR_GRADE_OPT_PART_3
#endif

// There is an active copy of this check in runtime/mercury.h;
// see the comment there for why it is needed.
//
// #ifdef MR_NATIVE_GC
//   #ifdef MR_THREAD_SAFE
//     #error "Sorry, not supported: --gc accurate --thread-safe"
//   #endif
// #endif

#ifdef MR_DEEP_PROFILING
  #define MR_GRADE_PART_5       MR_PASTE3(MR_GRADE_PART_4, _profdeep, MR_GRADE_DEEP_PROF_VERSION_NO)
  #define MR_GRADE_OPT_PART_5   MR_GRADE_OPT_PART_4 ".profdeep"
  #if defined(MR_MPROF_PROFILE_TIME) || defined(MR_MPROF_PROFILE_CALLS) \
        || defined(MR_MPROF_PROFILE_MEMORY)
    // Deep profiling is completely separate from the other profiling
    // alternatives, and there is no point in allowing their combination.

    #error "deep profiling is incompatible with mprof-style profiling"
  #endif
  #ifdef MR_HIGHLEVEL_CODE
    #error "deep profiling is incompatible with --high-level-code"
  #endif
#else // ! MR_DEEP_PROFILING
  #ifdef MR_MPROF_PROFILE_TIME
    #ifdef MR_MPROF_PROFILE_CALLS
      #ifdef MR_MPROF_PROFILE_MEMORY
        #define MR_GRADE_PART_5         MR_PASTE2(MR_GRADE_PART_4, _profall)
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4 ".profall"
      #else // ! MR_MPROF_PROFILE_MEMORY
        #define MR_GRADE_PART_5         MR_PASTE2(MR_GRADE_PART_4, _prof)
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4 ".prof"
      #endif // ! MR_MPROF_PROFILE_MEMORY
    #else // ! MR_MPROF_PROFILE_CALLS
      #ifdef MR_MPROF_PROFILE_MEMORY
        #error "memory profiling requires call profiling"
      #else // ! MR_MPROF_PROFILE_MEMORY
        // Currently useless, but...
        #define MR_GRADE_PART_5         MR_PASTE2(MR_GRADE_PART_4, _proftime)
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4 ".proftime"
      #endif // MR_MPROF_PROFILE_MEMORY
    #endif // MR_MPROF_PROFILE_CALLS
  #else // ! MR_MPROF_PROFILE_TIME
    #ifdef MR_MPROF_PROFILE_CALLS
      #ifdef MR_MPROF_PROFILE_MEMORY
        #define MR_GRADE_PART_5         MR_PASTE2(MR_GRADE_PART_4, _memprof)
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4 ".memprof"
      #else // ! MR_MPROF_PROFILE_MEMORY
        #define MR_GRADE_PART_5         MR_PASTE2(MR_GRADE_PART_4, _profcalls)
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4 ".profcalls"
      #endif // MR_MPROF_PROFILE_MEMORY
    #else // ! MR_MPROF_PROFILE_CALLS
      #ifdef MR_MPROF_PROFILE_MEMORY
        #error "memory profiling requires call profiling"
      #else // ! MR_MPROF_PROFILE_MEMORY
        #define MR_GRADE_PART_5         MR_GRADE_PART_4
        #define MR_GRADE_OPT_PART_5     MR_GRADE_OPT_PART_4
      #endif // MR_MPROF_PROFILE_MEMORY
    #endif // MR_MPROF_PROFILE_CALLS
  #endif // ! MR_MPROF_PROFILE_TIME
#endif // MR_DEEP_PROFILING

#ifdef MR_RECORD_TERM_SIZES
  #ifdef MR_RECORD_TERM_SIZES_AS_CELLS
    #define MR_GRADE_PART_6             MR_PASTE2(MR_GRADE_PART_5, _tsc)
    #define MR_GRADE_OPT_PART_6         MR_GRADE_OPT_PART_5 ".tsc"
  #else
    #define MR_GRADE_PART_6             MR_PASTE2(MR_GRADE_PART_5, _tsw)
    #define MR_GRADE_OPT_PART_6         MR_GRADE_OPT_PART_5 ".tsw"
  #endif
  #ifdef MR_HIGHLEVEL_CODE
    #error "term size profiling is incompatible with --high-level-code"
  #endif
#else
  #define MR_GRADE_PART_6       MR_GRADE_PART_5
  #define MR_GRADE_OPT_PART_6   MR_GRADE_OPT_PART_5
#endif

#ifdef MR_USE_TRAIL
  #ifdef MR_USE_FIXED_SIZE_TRAIL
    #define MR_GRADE_PART_7     MR_PASTE3(MR_GRADE_PART_6, _trfix, MR_GRADE_TRAIL_VERSION_NO)
  #else
    #define MR_GRADE_PART_7     MR_PASTE3(MR_GRADE_PART_6, _trseg, MR_GRADE_TRAIL_VERSION_NO)
  #endif
    #define MR_GRADE_OPT_PART_7 MR_GRADE_OPT_PART_6 ".tr"
#else
  #define MR_GRADE_PART_7       MR_GRADE_PART_6
  #define MR_GRADE_OPT_PART_7   MR_GRADE_OPT_PART_6
#endif

// Grade component 8 used to be used for the .rt (reserve tag) grades.
// It is currently unused.

#define MR_GRADE_PART_8         MR_GRADE_PART_7
#define MR_GRADE_OPT_PART_8     MR_GRADE_OPT_PART_7

#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
  #ifdef MR_USE_MINIMAL_MODEL_OWN_STACKS
    #error "cannot use both forms of minimal model tabling at once"
  #endif
#endif

#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
  #ifdef MR_MINIMAL_MODEL_DEBUG
    #define MR_GRADE_PART_9     MR_PASTE2(MR_GRADE_PART_8, _dmmsc)
    #define MR_GRADE_OPT_PART_9 MR_GRADE_OPT_PART_8 ".dmmsc"
  #else
    #define MR_GRADE_PART_9     MR_PASTE2(MR_GRADE_PART_8, _mmsc)
    #define MR_GRADE_OPT_PART_9 MR_GRADE_OPT_PART_8 ".mmsc"
  #endif
#elif MR_USE_MINIMAL_MODEL_OWN_STACKS
  #ifdef MR_MINIMAL_MODEL_DEBUG
    #define MR_GRADE_PART_9     MR_PASTE2(MR_GRADE_PART_8, _dmmos)
    #define MR_GRADE_OPT_PART_9 MR_GRADE_OPT_PART_8 ".dmmos"
  #else
    #define MR_GRADE_PART_9     MR_PASTE2(MR_GRADE_PART_8, _mmos)
    #define MR_GRADE_OPT_PART_9 MR_GRADE_OPT_PART_8 ".mmos"
  #endif
#else
  #define MR_GRADE_PART_9       MR_GRADE_PART_8
  #define MR_GRADE_OPT_PART_9   MR_GRADE_OPT_PART_8
#endif

#if (defined(MR_USE_MINIMAL_MODEL_STACK_COPY) ||                        \
        defined(MR_USE_MINIMAL_MODEL_OWN_STACKS))
  // One implementation of minimal model tabling works by saving and restoring
  // segments of the nondet stack, the other by creating a separate stack for
  // each generator. Since in high level code grades we don't have a nondet
  // stack that we can save and restore and we can't establish extra stacks,
  // both forms of minimal model tabling are fundamentally incompatible
  // with high level code.

  #if defined(MR_HIGHLEVEL_CODE)
    #error "high level code and minimal model tabling are not compatible"
  #endif

  // Tabling is incompatible with multiple threads of execution for several
  // reasons. The main one is that when we find that the entry for a call's
  // input arguments in the called procedure's call table is already active
  // before the call, all forms of tabling treat it as a sign of infinite
  // recursion, as in e.g. f(42, ...) calling ... calling f(42, ...).
  // However, in a grade that allows more than one thread of execution
  // to be active at the same time, the f(42, ...) calls need not be related
  // as ancestor and descendant; they could be independent calls in different
  // threads. This invalidates the basic assumption on top of which
  // tabling is built.
  // 
  // There are other reasons as well. The data structures used by tabling
  // are not protected by critical sections, so simultaneous access by more
  // than one thread at the same time can cause data corruption, and
  // in the process of suspending one call in one thread, the stack copy
  // implementation of minimal model tabling can actively overwrite parts
  // of the stack that are still being used by other threads.

  #if defined(MR_THREAD_SAFE)
    #error "parallel execution and minimal model tabling are not compatible"
  #endif

  // Neither form of the minimal model tabling works if the system recovers
  // memory allocated after a choice point when backtracking to that choice
  // point. This rules out the use of the native Mercury collector, as well as
  // the absence of a collector. (This may change for the own stack model,
  // with more work.)

  #if !defined(MR_CONSERVATIVE_GC)
    #error "minimal model tabling requires conservative gc"
  #endif

  // Saving and restoring the trail state would not be sufficient
  // to handle the combination of trailing and minimal model tabling.
  // Consider the following sequence of events:
  //
  //  execution enters a goal being committed across
  //  a new entry is pushed on the trail
  //  a tabled goal suspends,
  //      causing the saving of a trail segment and then a failure
  //  the goal being committed across fails,
  //      which invokes a failed commit on the trail entry
  //  ...
  //  the tabled goal is resumed,
  //      causing the restoring of the saved trail segment and then a success
  //  the goal being committed across now succeeds,
  //      which invokes a successful commit on the trail entry
  //
  // The trail handler will be thoroughly confused by such a sequence.
  //
  // Until we can figure out (and implement) a fix for this problem,
  // trailing cannot be used together with either form of minimal model
  // tabling.

  #if defined(MR_USE_TRAIL)
    #error "trailing and minimal model tabling are not compatible"
  #endif
#endif

// Parts 10-11 (i.e. tag bits, and (un)boxed float) are documented as
// "not for general use", and can't be set via the `--grade' option;
// we therefore can't make them part of the grade option string.
//
// Single-precision floats do form part of the grade option string
// and they imply unboxed floats.

#if MR_TAGBITS == 0
  #error "MR_TAGBITS must be greater than zero"
#else
  #define MR_GRADE_PART_10      MR_PASTE2(MR_GRADE_PART_9,              \
                                        MR_PASTE2(_tags, MR_TAGBITS))
#endif
#define MR_GRADE_OPT_PART_10    MR_GRADE_OPT_PART_9

#if defined(MR_PREGENERATED_DIST)
  #define MR_GRADE_PART_11      MR_PASTE2(MR_GRADE_PART_10, _pregen)
  #define MR_GRADE_OPT_PART_11  MR_GRADE_OPT_PART_10 ".pregen"
  #if defined(MR_USE_SINGLE_PREC_FLOAT)
    #error "pregenerated code cannot use single-precision floats"
  #endif
#elif defined(MR_USE_SINGLE_PREC_FLOAT)
  #if defined(MR_BOXED_FLOAT)
    #error "single-precision floats implies unboxed floats"
  #endif
  #define MR_GRADE_PART_11      MR_PASTE2(MR_GRADE_PART_10, _spf)
  #define MR_GRADE_OPT_PART_11  MR_GRADE_OPT_PART_10 ".spf"
#elif defined(MR_BOXED_FLOAT)
  #define MR_GRADE_PART_11      MR_GRADE_PART_10
  #define MR_GRADE_OPT_PART_11  MR_GRADE_OPT_PART_10
#else                           // "ubf" stands for "unboxed float"
  #define MR_GRADE_PART_11      MR_PASTE2(MR_GRADE_PART_10, _ubf)
  #define MR_GRADE_OPT_PART_11  MR_GRADE_OPT_PART_10
#endif

// Part 12 (i.e. MR_NEW_MERCURYFILE_STRUCT) can't be set by the `--grade'
// option; it is intended to be set by the configure script at configuration
// time. We therefore don't include it in the grade option string.

#ifdef MR_NEW_MERCURYFILE_STRUCT
  #define MR_GRADE_PART_12      MR_PASTE2(MR_GRADE_PART_11, _file)
#else
  #define MR_GRADE_PART_12      MR_GRADE_PART_11
#endif
#define MR_GRADE_OPT_PART_12    MR_GRADE_OPT_PART_11

// Part 13 used to record the absence/presence of the .regparm grade component
// with MR_HIGHLEVEL_CODE, and the absence/presence of the .picreg grade
// component for !MR_HIGHLEVEL_CODE and MR_USE_GCC_GLOBAL_REGISTERS.
// Neither is in use anymore.
//
// The .regparm part specified an optimized calling convention of 32-bit x86
// machines, which turns out to be a pessimizing calling convention on x86/64.
//
// Once upon a time, on x86 machines with MR_USE_GCC_GLOBAL_REGISTERS,
// we reserved three global registers if we could, but had to be content
// with two if we were generating position independent code, since PIC needed
// a register for itself. However, the difference caused problems in linking
// object files compiled with different numbers of global registers reserved,
// and gcc bugs kept generating crashes with three reserved registers,
// so we now always reserve only two global registers. Therefore the
// distinction that .picreg used to record has vanished.

#define MR_GRADE_PART_13      MR_GRADE_PART_12
#define MR_GRADE_OPT_PART_13  MR_GRADE_OPT_PART_12

#if defined(MR_DECL_DEBUG)
  #define MR_GRADE_PART_14              MR_PASTE3(MR_GRADE_PART_13, _decldebug, MR_GRADE_EXEC_TRACE_VERSION_NO)
  #define MR_GRADE_OPT_PART_14          MR_GRADE_OPT_PART_13 ".decldebug"
  #if ! defined(MR_EXEC_TRACE)
    #error "declarative debugging requires execution tracing"
  #endif
#else
  #if defined(MR_EXEC_TRACE)
    #define MR_GRADE_PART_14            MR_PASTE3(MR_GRADE_PART_13, _debug, MR_GRADE_EXEC_TRACE_VERSION_NO)
    #define MR_GRADE_OPT_PART_14        MR_GRADE_OPT_PART_13 ".debug"
    #else
      #if defined(MR_SS_DEBUG)
        #define MR_GRADE_PART_14        MR_PASTE3(MR_GRADE_PART_13, _ssdebug, MR_GRADE_EXEC_TRACE_VERSION_NO)
        #define MR_GRADE_OPT_PART_14    MR_GRADE_OPT_PART_13 ".ssdebug"
      #else
        #define MR_GRADE_PART_14        MR_GRADE_PART_13
        #define MR_GRADE_OPT_PART_14    MR_GRADE_OPT_PART_13
      #endif
    #endif
#endif

#if defined(MR_C_DEBUG_GRADE)
  #define MR_GRADE_PART_15      MR_PASTE2(MR_GRADE_PART_14, _c_debug)
  #define MR_GRADE_OPT_PART_15  MR_GRADE_OPT_PART_14 ".c_debug"
#else
  #define MR_GRADE_PART_15      MR_GRADE_PART_14
  #define MR_GRADE_OPT_PART_15  MR_GRADE_OPT_PART_14
#endif

#if defined(MR_EXTEND_STACKS_WHEN_NEEDED)
  #define MR_GRADE_PART_16      MR_PASTE2(MR_GRADE_PART_15, _exts)
  #define MR_GRADE_OPT_PART_16  MR_GRADE_OPT_PART_15 ".exts"
  #if defined(MR_HIGHLEVEL_CODE)
    #error "--extend-stacks-when-needed and --high-level-code are not compatible"
  #endif
  #if defined(MR_STACK_SEGMENTS)
    #error "--extend-stacks-when-needed and --stack-segments are not compatible"
  #endif
#elif defined(MR_STACK_SEGMENTS)
  #define MR_GRADE_PART_16      MR_PASTE2(MR_GRADE_PART_15, _stseg)
  #define MR_GRADE_OPT_PART_16  MR_GRADE_OPT_PART_15 ".stseg"
  #if defined(MR_HIGHLEVEL_CODE)
    #error "--stack-segments and --high-level-code are not compatible"
  #endif
#else
  #define MR_GRADE_PART_16      MR_GRADE_PART_15
  #define MR_GRADE_OPT_PART_16  MR_GRADE_OPT_PART_15
#endif

#if defined(MR_USE_REGIONS)
  #if defined(MR_RBMM_DEBUG)
    #if defined(MR_RBMM_PROFILING)
      #define MR_GRADE_PART_17          MR_PASTE2(MR_GRADE_PART_16, _rbmmdp)
      #define MR_GRADE_OPT_PART_17      MR_GRADE_OPT_PART_16 ".rbmmdp"
    #else
      #define MR_GRADE_PART_17          MR_PASTE2(MR_GRADE_PART_16, _rbmmd)
      #define MR_GRADE_OPT_PART_17      MR_GRADE_OPT_PART_16 ".rbmmd"
    #endif
  #else
    #if defined(MR_RBMM_PROFILING)
      #define MR_GRADE_PART_17          MR_PASTE2(MR_GRADE_PART_16, _rbmmp)
      #define MR_GRADE_OPT_PART_17      MR_GRADE_OPT_PART_16 ".rbmmp"
    #else
      #define MR_GRADE_PART_17          MR_PASTE2(MR_GRADE_PART_16, _rbmm)
      #define MR_GRADE_OPT_PART_17      MR_GRADE_OPT_PART_16 ".rbmm"
    #endif
  #endif
#else
  #define MR_GRADE_PART_17      MR_GRADE_PART_16
  #define MR_GRADE_OPT_PART_17  MR_GRADE_OPT_PART_16
#endif

#if defined(MR_THREADSCOPE)
  #define MR_GRADE_PART_18      MR_PASTE2(MR_GRADE_PART_17, _threadscope)
  #define MR_GRADE_OPT_PART_18  MR_GRADE_OPT_PART_17 ".threadscope"
  #if !defined(MR_THREAD_SAFE)
    #error "threadscope-style profiling may be used only with parallel grades"
  #endif
#else
  #define MR_GRADE_PART_18      MR_GRADE_PART_17
  #define MR_GRADE_OPT_PART_18  MR_GRADE_OPT_PART_17
#endif

#if defined(MR_BOXED_INT64S)
  #define MR_GRADE_PART_19     MR_GRADE_PART_18
  #define MR_GRADE_OPT_PART_19 MR_GRADE_OPT_PART_18
#else                          // "ubi64" stands for "unboxed integers 64-bit"
  #define MR_GRADE_PART_19     MR_PASTE2(MR_GRADE_PART_18, _ubi64)
  #define MR_GRADE_OPT_PART_19 MR_GRADE_OPT_PART_18
#endif

#define MR_GRADE                MR_GRADE_PART_19
#define MR_GRADE_OPT            MR_GRADE_OPT_PART_19

#define MR_GRADE_VAR            MR_PASTE2(MR_grade_,MR_GRADE)
#define MR_GRADE_STRING         MR_STRINGIFY(MR_GRADE)

extern const char MR_GRADE_VAR;

#endif // MERCURY_GRADES_H
