diff options
| author | Richard Brooksby | 2012-09-01 10:05:30 +0100 |
|---|---|---|
| committer | Richard Brooksby | 2012-09-01 10:05:30 +0100 |
| commit | 4265ff2c625a8655d682baf86a690bed9025623a (patch) | |
| tree | 5e7ee53229609faa20ad0f421648207960beb8c7 /mps/code | |
| parent | 82bc1374643e8e357a984fefa550650c99f222fd (diff) | |
| download | emacs-4265ff2c625a8655d682baf86a690bed9025623a.tar.gz emacs-4265ff2c625a8655d682baf86a690bed9025623a.zip | |
Tidying up check.h and adding lots of design documentation.
Copied from Perforce
Change: 179154
ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code')
| -rw-r--r-- | mps/code/check.h | 280 |
1 files changed, 156 insertions, 124 deletions
diff --git a/mps/code/check.h b/mps/code/check.h index 26ce0ea965e..78334f8d7d3 100644 --- a/mps/code/check.h +++ b/mps/code/check.h | |||
| @@ -17,6 +17,18 @@ | |||
| 17 | * will throw the code away, but check its syntax. | 17 | * will throw the code away, but check its syntax. |
| 18 | * | 18 | * |
| 19 | * .trans.level-check: CheckLevel itself is not checked anywhere. | 19 | * .trans.level-check: CheckLevel itself is not checked anywhere. |
| 20 | * | ||
| 21 | * .careful: BE CAREFUL when changing this file. It is easy to make mistakes | ||
| 22 | * and change the checking level in a variety and thereby its performance | ||
| 23 | * without realising it. This has happened before. Eyeball the preprocessor | ||
| 24 | * output for each variety. For example: | ||
| 25 | * | ||
| 26 | * cc -E -DCONFIG_PROD_MPS -DCONFIG_VAR_WE trace.c | ||
| 27 | * cc -E -DCONFIG_PROD_MPS -DCONFIG_VAR_HE trace.c | ||
| 28 | * cc -E -DCONFIG_PROD_MPS -DCONFIG_VAR_CI trace.c | ||
| 29 | * | ||
| 30 | * Then look at TraceCheck to make sure checking is right, TraceAddWhite | ||
| 31 | * for general assertions, and TraceFix for the critical path assertions. | ||
| 20 | */ | 32 | */ |
| 21 | 33 | ||
| 22 | #ifndef check_h | 34 | #ifndef check_h |
| @@ -27,7 +39,38 @@ | |||
| 27 | #include "mpslib.h" | 39 | #include "mpslib.h" |
| 28 | 40 | ||
| 29 | 41 | ||
| 30 | /* CheckLevel -- Control check method behaviour */ | 42 | /* ASSERT -- basic assertion |
| 43 | * | ||
| 44 | * The ASSERT macro is equivalent to the ISO C assert() except that it is | ||
| 45 | * always defined, and uses the assertion handler from the MPS plinth, which | ||
| 46 | * can be replaced by the client code. | ||
| 47 | * | ||
| 48 | * It is not intended for direct use within the MPS. Use AVER and CHECK | ||
| 49 | * macros, which can be controlled by both build and run-time configuration. | ||
| 50 | */ | ||
| 51 | |||
| 52 | #define ASSERT(cond, condstring) \ | ||
| 53 | BEGIN \ | ||
| 54 | if (cond) NOOP; else \ | ||
| 55 | mps_lib_assert_fail(condstring "\n" __FILE__ "\n" STR(__LINE__)); \ | ||
| 56 | END | ||
| 57 | |||
| 58 | #define ASSERT_TYPECHECK(type, val) \ | ||
| 59 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val) | ||
| 60 | |||
| 61 | #define ASSERT_NULLCHECK(type, val) \ | ||
| 62 | ASSERT((val) != NULL, "NullCheck " #type ": " #val) | ||
| 63 | |||
| 64 | |||
| 65 | /* CheckLevel -- control for check method behaviour | ||
| 66 | * | ||
| 67 | * When the MPS is build with AVER_AND_CHECK_ALL (in a "cool" variety) the | ||
| 68 | * static variable CheckLevel controls the frequency and detail of | ||
| 69 | * consistency checking on structures. | ||
| 70 | * | ||
| 71 | * FIXME: This should be initialised from an environment variable and have | ||
| 72 | * an interface in mps.h. | ||
| 73 | */ | ||
| 31 | 74 | ||
| 32 | extern unsigned CheckLevel; | 75 | extern unsigned CheckLevel; |
| 33 | 76 | ||
| @@ -43,29 +86,34 @@ enum { | |||
| 43 | 86 | ||
| 44 | /* AVER, AVERT -- MPM assertions | 87 | /* AVER, AVERT -- MPM assertions |
| 45 | * | 88 | * |
| 46 | * AVER and AVERT are used to assert conditions in the code. | 89 | * AVER and AVERT are used to assert conditions in the code. AVER checks |
| 90 | * an expression. AVERT checks that a value is of the correct type and | ||
| 91 | * may perform consistency checks on the value. | ||
| 92 | * | ||
| 93 | * AVER and AVERT are on by default, and check conditions even in "hot" | ||
| 94 | * varieties intended to work in production. To avoid the cost of a check | ||
| 95 | * in critical parts of the code, use AVER_CRITICAL and AVERT_CRITICAL, | ||
| 96 | * but only when you've *proved* that this makes a difference to performance | ||
| 97 | * that affects requirements. | ||
| 47 | */ | 98 | */ |
| 48 | 99 | ||
| 49 | #if defined(AVER_AND_CHECK_NONE) | 100 | #if defined(AVER_AND_CHECK_NONE) |
| 50 | 101 | ||
| 51 | #define AVER(cond) DISCARD(cond) | 102 | #define AVER(cond) DISCARD(cond) |
| 52 | #define AVERT(type, val) DISCARD(type ## Check(val)) | 103 | #define AVERT(type, val) DISCARD(type ## Check(val)) |
| 53 | #define AVER_CRITICAL(cond) DISCARD(cond) | ||
| 54 | #define AVERT_CRITICAL(type, val) DISCARD(type ## Check(val)) | ||
| 55 | 104 | ||
| 56 | #elif defined(AVER_AND_CHECK) | 105 | #else |
| 57 | 106 | ||
| 58 | #define AVER(cond) ASSERT(cond, #cond) | 107 | #define AVER(cond) ASSERT(cond, #cond) |
| 59 | |||
| 60 | #define AVERT(type, val) \ | 108 | #define AVERT(type, val) \ |
| 61 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val) | 109 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val) |
| 62 | 110 | ||
| 63 | #if !defined(AVER_AND_CHECK_ALL) | 111 | #endif |
| 64 | 112 | ||
| 65 | #define AVER_CRITICAL DISCARD | 113 | #if defined(AVER_AND_CHECK_ALL) |
| 66 | #define AVERT_CRITICAL(type, val) DISCARD(type ## Check(val)) | ||
| 67 | 114 | ||
| 68 | #else /* AVER_AND_CHECK_ALL */ | 115 | /* FIXME: Find out whether these tests on checklevel have any performance |
| 116 | impact and remove them if possible. */ | ||
| 69 | 117 | ||
| 70 | #define AVER_CRITICAL(cond) \ | 118 | #define AVER_CRITICAL(cond) \ |
| 71 | BEGIN \ | 119 | BEGIN \ |
| @@ -79,38 +127,31 @@ enum { | |||
| 79 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val); \ | 127 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val); \ |
| 80 | END | 128 | END |
| 81 | 129 | ||
| 82 | #endif /* AVER_AND_CHECK_ALL */ | 130 | #else |
| 83 | |||
| 84 | #else /* AVER_AND_CHECK, not */ | ||
| 85 | 131 | ||
| 86 | #error "No checking defined." | 132 | #define AVER_CRITICAL DISCARD |
| 133 | #define AVERT_CRITICAL(type, val) DISCARD(type ## Check(val)) | ||
| 87 | 134 | ||
| 88 | #endif | 135 | #endif |
| 89 | 136 | ||
| 90 | 137 | ||
| 91 | /* internals for actually asserting */ | 138 | /* NOTREACHED -- control should never reach this statement |
| 92 | 139 | * | |
| 93 | #define ASSERT(cond, condstring) \ | 140 | * This is a sort of AVER; it is equivalent to AVER(FALSE), but will produce |
| 94 | BEGIN \ | 141 | * a more informative message. |
| 95 | if (cond) NOOP; else \ | 142 | */ |
| 96 | mps_lib_assert_fail(condstring "\n" __FILE__ "\n" STR(__LINE__)); \ | ||
| 97 | END | ||
| 98 | 143 | ||
| 144 | #if defined(AVER_AND_CHECK_NONE) | ||
| 99 | 145 | ||
| 100 | /* NOTREACHED -- control should never reach this statement */ | 146 | #define NOTREACHED NOOP |
| 101 | /* This is a sort of AVER; it is equivalent to AVER(FALSE). */ | ||
| 102 | 147 | ||
| 103 | #if defined(AVER_AND_CHECK) | 148 | #else |
| 104 | 149 | ||
| 105 | #define NOTREACHED \ | 150 | #define NOTREACHED \ |
| 106 | BEGIN \ | 151 | BEGIN \ |
| 107 | mps_lib_assert_fail("unreachable code" "\n" __FILE__ "\n" STR(__LINE__)); \ | 152 | mps_lib_assert_fail("unreachable code" "\n" __FILE__ "\n" STR(__LINE__)); \ |
| 108 | END | 153 | END |
| 109 | 154 | ||
| 110 | #else | ||
| 111 | |||
| 112 | #define NOTREACHED NOOP | ||
| 113 | |||
| 114 | #endif | 155 | #endif |
| 115 | 156 | ||
| 116 | 157 | ||
| @@ -126,126 +167,117 @@ enum { | |||
| 126 | #define CHECKT(type, val) ((val) != NULL && (val)->sig == type ## Sig) | 167 | #define CHECKT(type, val) ((val) != NULL && (val)->sig == type ## Sig) |
| 127 | 168 | ||
| 128 | 169 | ||
| 129 | #if defined(AVER_AND_CHECK_NONE) | 170 | /* CHECKS -- Check Signature |
| 171 | * | ||
| 172 | * (if CheckLevel == CheckLevelMINIMAL, this is all we check) | ||
| 173 | */ | ||
| 130 | 174 | ||
| 175 | #if defined(AVER_AND_CHECK_NONE) | ||
| 131 | #define CHECKS(type, val) DISCARD(CHECKT(type, val)) | 176 | #define CHECKS(type, val) DISCARD(CHECKT(type, val)) |
| 132 | #define CHECKL(cond) DISCARD(cond) | 177 | #else |
| 133 | #define CHECKD(type, val) DISCARD(CHECKT(type, val)) | ||
| 134 | #define CHECKD_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 135 | #define CHECKU(type, val) DISCARD(CHECKT(type, val)) | ||
| 136 | #define CHECKU_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 137 | |||
| 138 | #else /* AVER_AND_CHECK_NONE, not */ | ||
| 139 | |||
| 140 | /* CHECKS -- Check Signature */ | ||
| 141 | /* (if CheckLevel == CheckLevelMINIMAL, this is all we check) */ | ||
| 142 | |||
| 143 | #define CHECKS(type, val) \ | 178 | #define CHECKS(type, val) \ |
| 144 | ASSERT(CHECKT(type, val), "SigCheck " #type ": " #val) | 179 | ASSERT(CHECKT(type, val), "SigCheck " #type ": " #val) |
| 180 | #endif | ||
| 145 | 181 | ||
| 146 | #if !defined(AVER_AND_CHECK_ALL) | ||
| 147 | |||
| 148 | /* FIXME: This gives comparable performance to white-hot when compiling | ||
| 149 | using mps.c and -O (to get check methods inlined), but is it a bit | ||
| 150 | too minimal? How much do we rely on check methods? */ | ||
| 151 | |||
| 152 | #define CHECKL(cond) DISCARD(cond) | ||
| 153 | #define CHECKD(type, val) DISCARD(CHECKT(type, val)) | ||
| 154 | #define CHECKD_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 155 | #define CHECKU(type, val) DISCARD(CHECKT(type, val)) | ||
| 156 | #define CHECKU_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 157 | |||
| 158 | #else /* AVER_AND_CHECK_ALL */ | ||
| 159 | 182 | ||
| 160 | /* CHECKL -- Check Local Invariant | 183 | /* CHECKL, CHECKD, CHECKU -- local, "down", and "up" checks |
| 184 | * | ||
| 185 | * Each type should have a function defined called <type>Check that checks | ||
| 186 | * the consistency of the type. This function should return TRUE iff the | ||
| 187 | * value passes consistency checks. In general, it should assert otherwise, | ||
| 188 | * but we allow for the possibility of returning FALSE in this case for | ||
| 189 | * configuration adaptability. | ||
| 190 | * | ||
| 191 | * For structure types, the check function should: | ||
| 161 | * | 192 | * |
| 162 | * Could make this an expression using ?: | 193 | * - check its own signature with CHECKS |
| 194 | * | ||
| 195 | * - check fields that it "owns" with CHECKL, like asserts | ||
| 196 | * | ||
| 197 | * - check "down" values which are its "children" with CHEKCD | ||
| 198 | * | ||
| 199 | * - check "up" values which are its "parents" with CHECKU. | ||
| 200 | * | ||
| 201 | * These various checks will be compiled out or compiled to be controlled | ||
| 202 | * by CheckLevel. | ||
| 203 | * | ||
| 204 | * For example: | ||
| 205 | * | ||
| 206 | * Bool MessageCheck(Message message) | ||
| 207 | * { | ||
| 208 | * CHECKS(Message, message); | ||
| 209 | * CHECKU(Arena, message->arena); | ||
| 210 | * CHECKD(MessageClass, message->class); | ||
| 211 | * CHECKL(RingCheck(&message->queueRing)); | ||
| 212 | * CHECKL(MessageIsClocked(message) || (message->postedClock == 0)); | ||
| 213 | * return TRUE; | ||
| 214 | * } | ||
| 215 | * | ||
| 216 | * The parent/child distinction depends on the structure, but in the MPS | ||
| 217 | * the Arena has no parents, and has children which are Pools, which have | ||
| 218 | * children which are Segments, etc. | ||
| 219 | * | ||
| 220 | * The important thing is to have a partial order on types so that recursive | ||
| 221 | * checking will terminate. When CheckLevel is set to DEEP, checking will | ||
| 222 | * recurse into check methods for children, but will only do a shallow | ||
| 223 | * signature check on parents, avoiding infinite regression. | ||
| 224 | * | ||
| 225 | * FIXME: Switching on every CHECK line doesn't compile very well, because | ||
| 226 | * the compiler can't tell that CheckLevel won't change between function | ||
| 227 | * calls and can't lift out the test. Is there a better arrangement, | ||
| 228 | * perhaps by reading CheckLevel into a local variable? | ||
| 163 | */ | 229 | */ |
| 164 | 230 | ||
| 165 | #define CHECKL(cond) \ | 231 | #if defined(AVER_AND_CHECK_ALL) |
| 166 | BEGIN \ | ||
| 167 | switch(CheckLevel) { \ | ||
| 168 | case CheckLevelMINIMAL: \ | ||
| 169 | NOOP; \ | ||
| 170 | break; \ | ||
| 171 | case CheckLevelSHALLOW: \ | ||
| 172 | case CheckLevelDEEP: \ | ||
| 173 | ASSERT(cond, #cond); \ | ||
| 174 | break; \ | ||
| 175 | } \ | ||
| 176 | END | ||
| 177 | |||
| 178 | 232 | ||
| 179 | /* CHECKD -- Check Down */ | 233 | #define CHECK_BY_LEVEL(minimal, shallow, deep) \ |
| 180 | |||
| 181 | #define CHECKD(type, val) \ | ||
| 182 | BEGIN \ | 234 | BEGIN \ |
| 183 | switch(CheckLevel) { \ | 235 | switch (CheckLevel) { \ |
| 184 | case CheckLevelMINIMAL: \ | 236 | case CheckLevelDEEP: deep; break; \ |
| 185 | NOOP; \ | 237 | case CheckLevelSHALLOW: shallow; break; \ |
| 186 | break; \ | 238 | default: NOTREACHED; /* fall through */ \ |
| 187 | case CheckLevelSHALLOW: \ | 239 | case CheckLevelMINIMAL: minimal; break; \ |
| 188 | CHECKS(type, val); \ | ||
| 189 | break; \ | ||
| 190 | case CheckLevelDEEP: \ | ||
| 191 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val); \ | ||
| 192 | break; \ | ||
| 193 | } \ | 240 | } \ |
| 194 | END | 241 | END |
| 195 | 242 | ||
| 243 | #define CHECKL(cond) \ | ||
| 244 | CHECK_BY_LEVEL(NOOP, \ | ||
| 245 | ASSERT(cond, #cond), \ | ||
| 246 | ASSERT(cond, #cond)) | ||
| 196 | 247 | ||
| 197 | /* CHECKD_NOSIG -- Check Down for a type with no signature */ | 248 | #define CHECKD(type, val) \ |
| 249 | CHECK_BY_LEVEL(NOOP, \ | ||
| 250 | CHECKS(type, val), \ | ||
| 251 | ASSERT_TYPECHECK(type, val)) | ||
| 198 | 252 | ||
| 199 | #define CHECKD_NOSIG(type, val) \ | 253 | #define CHECKD_NOSIG(type, val) \ |
| 200 | BEGIN \ | 254 | CHECK_BY_LEVEL(NOOP, \ |
| 201 | switch(CheckLevel) { \ | 255 | ASSERT_NULLCHECK(type, val), \ |
| 202 | case CheckLevelMINIMAL: \ | 256 | ASSERT_TYPECHECK(type, val)) |
| 203 | NOOP; \ | ||
| 204 | break; \ | ||
| 205 | case CheckLevelSHALLOW: \ | ||
| 206 | ASSERT((val) != NULL, "NullCheck " #type ": " #val); \ | ||
| 207 | break; \ | ||
| 208 | case CheckLevelDEEP: \ | ||
| 209 | ASSERT(type ## Check(val), "TypeCheck " #type ": " #val); \ | ||
| 210 | break; \ | ||
| 211 | } \ | ||
| 212 | END | ||
| 213 | |||
| 214 | |||
| 215 | /* CHECKU -- Check Up */ | ||
| 216 | 257 | ||
| 217 | #define CHECKU(type, val) \ | 258 | #define CHECKU(type, val) \ |
| 218 | BEGIN \ | 259 | CHECK_BY_LEVEL(NOOP, \ |
| 219 | switch(CheckLevel) { \ | 260 | CHECKS(type, val), \ |
| 220 | case CheckLevelMINIMAL: \ | 261 | CHECKS(type, val)) |
| 221 | NOOP; \ | ||
| 222 | break; \ | ||
| 223 | case CheckLevelSHALLOW: \ | ||
| 224 | case CheckLevelDEEP: \ | ||
| 225 | CHECKS(type, val); \ | ||
| 226 | break; \ | ||
| 227 | } \ | ||
| 228 | END | ||
| 229 | 262 | ||
| 263 | #define CHECKU_NOSIG(type, val) \ | ||
| 264 | CHECK_BY_LEVEL(NOOP, \ | ||
| 265 | ASSERT_NULLCHECK(type, val), \ | ||
| 266 | ASSERT_NULLCHECK(type, val)) | ||
| 230 | 267 | ||
| 231 | /* CHECKU_NOSIG -- Check Up for a type with no signature */ | 268 | #else /* AVER_AND_CHECK_ALL, not */ |
| 232 | 269 | ||
| 233 | #define CHECKU_NOSIG(type, val) \ | 270 | /* FIXME: This gives comparable performance to white-hot when compiling |
| 234 | BEGIN \ | 271 | using mps.c and -O (to get check methods inlined), but is it a bit |
| 235 | switch(CheckLevel) { \ | 272 | too minimal? How much do we rely on check methods? */ |
| 236 | case CheckLevelMINIMAL: \ | ||
| 237 | NOOP; \ | ||
| 238 | break; \ | ||
| 239 | case CheckLevelSHALLOW: \ | ||
| 240 | case CheckLevelDEEP: \ | ||
| 241 | ASSERT((val) != NULL, "NullCheck " #type ": " #val); \ | ||
| 242 | break; \ | ||
| 243 | } \ | ||
| 244 | END | ||
| 245 | 273 | ||
| 246 | #endif /* AVER_AND_CHECK_ALL */ | 274 | #define CHECKL(cond) DISCARD(cond) |
| 275 | #define CHECKD(type, val) DISCARD(CHECKT(type, val)) | ||
| 276 | #define CHECKD_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 277 | #define CHECKU(type, val) DISCARD(CHECKT(type, val)) | ||
| 278 | #define CHECKU_NOSIG(type, val) DISCARD((val) != NULL) | ||
| 247 | 279 | ||
| 248 | #endif /* AVER_AND_CHECK_NONE */ | 280 | #endif /* AVER_AND_CHECK_ALL */ |
| 249 | 281 | ||
| 250 | 282 | ||
| 251 | /* CHECKLVALUE &c -- type compatibility checking | 283 | /* CHECKLVALUE &c -- type compatibility checking |