aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code
diff options
context:
space:
mode:
authorRichard Brooksby2012-09-01 10:05:30 +0100
committerRichard Brooksby2012-09-01 10:05:30 +0100
commit4265ff2c625a8655d682baf86a690bed9025623a (patch)
tree5e7ee53229609faa20ad0f421648207960beb8c7 /mps/code
parent82bc1374643e8e357a984fefa550650c99f222fd (diff)
downloademacs-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.h280
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
32extern unsigned CheckLevel; 75extern 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