// 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