Commit | Line | Data |
---|---|---|
44986d67 MD |
1 | /* meas.c -- measure qt stuff. */ |
2 | ||
3 | #include "copyright.h" | |
4 | ||
5 | /* Need this to get assertions under Mach on the Sequent/i386: */ | |
6 | #ifdef __i386__ | |
7 | #define assert(ex) \ | |
8 | do { \ | |
9 | if (!(ex)) { \ | |
10 | fprintf (stderr, "[%s:%d] Assertion " #ex " failed\n", __FILE__, __LINE__); \ | |
11 | abort(); \ | |
12 | } \ | |
13 | } while (0) | |
14 | #else | |
15 | #include <assert.h> | |
16 | #endif | |
17 | ||
18 | /* This really ought to be defined in some ANSI include file (*I* | |
19 | think...), but it's defined here instead, which leads us to another | |
20 | machine dependency. | |
21 | ||
22 | The `iaddr_t' type is an integer representation of a pointer, | |
23 | suited for doing arithmetic on addresses, e.g. to round an address | |
24 | to an alignment boundary. */ | |
25 | typedef unsigned long iaddr_t; | |
26 | ||
27 | #include <stdarg.h> /* For varargs tryout. */ | |
28 | #include <stdio.h> | |
29 | #include "b.h" | |
30 | #include "qt.h" | |
31 | #include "stp.h" | |
32 | ||
33 | extern void exit (int status); | |
34 | extern int atoi (char const *s); | |
35 | extern int fprintf (FILE *out, char const *fmt, ...); | |
36 | extern int fputs (char const *s, FILE *fp); | |
37 | extern void free (void *sto); | |
38 | extern void *malloc (unsigned nbytes); | |
39 | extern void perror (char const *s); | |
40 | ||
41 | void usage (void); | |
42 | void tracer(void); | |
43 | ||
44 | /* Round `v' to be `a'-aligned, assuming `a' is a power of two. */ | |
45 | #define ROUND(v, a) (((v) + (a) - 1) & ~((a)-1)) | |
46 | ||
47 | typedef struct thread_t { | |
48 | qt_t *qt; /* Pointer to thread of function... */ | |
49 | void *stk; | |
50 | void *top; /* Set top of stack if reuse. */ | |
51 | struct thread_t *next; | |
52 | } thread_t; | |
53 | ||
54 | ||
55 | static thread_t * | |
56 | t_alloc (void) | |
57 | { | |
58 | thread_t *t; | |
59 | int ssz = 0x1000; | |
60 | ||
61 | t = malloc (sizeof(thread_t)); | |
62 | if (!t) { | |
63 | perror ("malloc"); | |
64 | exit (1); | |
65 | } | |
66 | assert (ssz > QT_STKBASE); | |
67 | t->stk = malloc (ssz); | |
68 | t->stk = (void *)ROUND (((iaddr_t)t->stk), QT_STKALIGN); | |
69 | if (!t->stk) { | |
70 | perror ("malloc"); | |
71 | exit (1); | |
72 | } | |
73 | assert ((((iaddr_t)t->stk) & (QT_STKALIGN-1)) == 0); | |
74 | t->top = QT_SP (t->stk, ssz - QT_STKBASE); | |
75 | ||
76 | return (t); | |
77 | } | |
78 | ||
79 | ||
80 | static thread_t * | |
81 | t_create (qt_only_t *starter, void *p0, qt_userf_t *f) | |
82 | { | |
83 | thread_t *t; | |
84 | ||
85 | t = t_alloc(); | |
86 | t->qt = QT_ARGS (t->top, p0, t, f, starter); | |
87 | return (t); | |
88 | } | |
89 | ||
90 | ||
91 | static void | |
92 | t_free (thread_t *t) | |
93 | { | |
94 | free (t->stk); | |
95 | free (t); | |
96 | } | |
97 | ||
98 | ||
99 | static void * | |
100 | t_null (qt_t *old, void *p1, void *p2) | |
101 | { | |
102 | /* return (garbage); */ | |
103 | } | |
104 | ||
105 | ||
106 | static void * | |
107 | t_splat (qt_t *old, void *oldp, void *null) | |
108 | { | |
109 | *(qt_t **)oldp = old; | |
110 | /* return (garbage); */ | |
111 | } | |
112 | ||
113 | ||
114 | static char const test01_msg[] = | |
115 | "*QT_SP(sto,sz), QT_ARGS(top,p0,p1,userf,first)"; | |
116 | ||
117 | static char const *test01_descr[] = { | |
118 | "Performs 1 QT_SP and one QT_ARGS per iteration.", | |
119 | NULL | |
120 | }; | |
121 | ||
122 | /* This test gives a guess on how long it takes to initalize | |
123 | a thread. */ | |
124 | ||
125 | static void | |
126 | test01 (int n) | |
127 | { | |
128 | char stack[QT_STKBASE+QT_STKALIGN]; | |
129 | char *stk; | |
130 | qt_t *top; | |
131 | ||
132 | stk = (char *)ROUND (((iaddr_t)stack), QT_STKALIGN); | |
133 | ||
134 | { | |
135 | int i; | |
136 | ||
137 | for (i=0; i<QT_STKBASE; ++i) { | |
138 | stk[i] = 0; | |
139 | } | |
140 | } | |
141 | ||
142 | while (n>0) { | |
143 | /* RETVALUSED */ | |
144 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
145 | #ifdef NDEF | |
146 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
147 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
148 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
149 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
150 | ||
151 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
152 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
153 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
154 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
155 | top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0); | |
156 | ||
157 | n -= 10; | |
158 | #else | |
159 | n -= 1; | |
160 | #endif | |
161 | } | |
162 | } | |
163 | ||
164 | ||
165 | static char const test02_msg[] = "QT_BLOCKI (0, 0, test02_aux, t->qt)"; | |
166 | static qt_t *rootthread; | |
167 | ||
168 | static void | |
169 | test02_aux1 (void *pu, void *pt, qt_userf_t *f) | |
170 | { | |
171 | QT_ABORT (t_null, 0, 0, rootthread); | |
172 | } | |
173 | ||
174 | static void * | |
175 | test02_aux2 (qt_t *old, void *farg1, void *farg2) | |
176 | { | |
177 | rootthread = old; | |
178 | /* return (garbage); */ | |
179 | } | |
180 | ||
181 | static void | |
182 | test02 (int n) | |
183 | { | |
184 | thread_t *t; | |
185 | ||
186 | while (n>0) { | |
187 | t = t_create (test02_aux1, 0, 0); | |
188 | QT_BLOCKI (test02_aux2, 0, 0, t->qt); | |
189 | t_free (t); | |
190 | t = t_create (test02_aux1, 0, 0); | |
191 | QT_BLOCKI (test02_aux2, 0, 0, t->qt); | |
192 | t_free (t); | |
193 | t = t_create (test02_aux1, 0, 0); | |
194 | QT_BLOCKI (test02_aux2, 0, 0, t->qt); | |
195 | t_free (t); | |
196 | t = t_create (test02_aux1, 0, 0); | |
197 | QT_BLOCKI (test02_aux2, 0, 0, t->qt); | |
198 | t_free (t); | |
199 | t = t_create (test02_aux1, 0, 0); | |
200 | QT_BLOCKI (test02_aux2, 0, 0, t->qt); | |
201 | t_free (t); | |
202 | ||
203 | n -= 5; | |
204 | } | |
205 | } | |
206 | ||
207 | ||
208 | static char const test03_msg[] = "QT_BLOCKI (...) test vals are right."; | |
209 | ||
210 | ||
211 | /* Called by the thread function when it wants to shut down. | |
212 | Return a value to the main thread. */ | |
213 | ||
214 | static void * | |
215 | test03_aux0 (qt_t *old_is_garbage, void *farg1, void *farg2) | |
216 | { | |
217 | assert (farg1 == (void *)5); | |
218 | assert (farg2 == (void *)6); | |
219 | return ((void *)15); /* Some unlikely value. */ | |
220 | } | |
221 | ||
222 | ||
223 | /* Called during new thread startup by main thread. Since the new | |
224 | thread has never run before, return value is ignored. */ | |
225 | ||
226 | static void * | |
227 | test03_aux1 (qt_t *old, void *farg1, void *farg2) | |
228 | { | |
229 | assert (old != NULL); | |
230 | assert (farg1 == (void *)5); | |
231 | assert (farg2 == (void *)6); | |
232 | rootthread = old; | |
233 | return ((void *)16); /* Different than `15'. */ | |
234 | } | |
235 | ||
236 | static void | |
237 | test03_aux2 (void *pu, void *pt, qt_userf_t *f) | |
238 | { | |
239 | assert (pu == (void *)1); | |
240 | assert (f == (qt_userf_t *)4); | |
241 | QT_ABORT (test03_aux0, (void *)5, (void *)6, rootthread); | |
242 | } | |
243 | ||
244 | static void | |
245 | test03 (int n) | |
246 | { | |
247 | thread_t *t; | |
248 | void *rv; | |
249 | ||
250 | while (n>0) { | |
251 | t = t_create (test03_aux2, (void *)1, (qt_userf_t *)4); | |
252 | rv = QT_BLOCKI (test03_aux1, (void *)5, (void *)6, t->qt); | |
253 | assert (rv == (void *)15); | |
254 | t_free (t); | |
255 | ||
256 | --n; | |
257 | } | |
258 | } | |
259 | ||
260 | ||
261 | static char const test04_msg[] = "stp_start w/ no threads."; | |
262 | ||
263 | static void | |
264 | test04 (int n) | |
265 | { | |
266 | while (n>0) { | |
267 | stp_init(); stp_start(); | |
268 | stp_init(); stp_start(); | |
269 | stp_init(); stp_start(); | |
270 | stp_init(); stp_start(); | |
271 | stp_init(); stp_start(); | |
272 | ||
273 | stp_init(); stp_start(); | |
274 | stp_init(); stp_start(); | |
275 | stp_init(); stp_start(); | |
276 | stp_init(); stp_start(); | |
277 | stp_init(); stp_start(); | |
278 | ||
279 | n -= 10; | |
280 | } | |
281 | } | |
282 | ||
283 | ||
284 | static char const test05_msg[] = "stp w/ 2 yielding thread."; | |
285 | ||
286 | static void | |
287 | test05_aux (void *null) | |
288 | { | |
289 | stp_yield(); | |
290 | stp_yield(); | |
291 | } | |
292 | ||
293 | static void | |
294 | test05 (int n) | |
295 | { | |
296 | while (n>0) { | |
297 | stp_init(); | |
298 | stp_create (test05_aux, 0); | |
299 | stp_create (test05_aux, 0); | |
300 | stp_start(); | |
301 | ||
302 | --n; | |
303 | } | |
304 | } | |
305 | ||
306 | ||
307 | static char const test06_msg[] = "*QT_ARGS(...), QT_BLOCKI one thread"; | |
308 | ||
309 | static char const *test06_descr[] = { | |
310 | "Does a QT_ARGS, QT_BLOCKI to a helper function that saves the", | |
311 | "stack pointer of the main thread, calls an `only' function that", | |
312 | "saves aborts the thread, calling a null helper function.", | |
313 | ":: start/stop = QT_ARGS + QT_BLOCKI + QT_ABORT + 3 procedure calls.", | |
314 | NULL | |
315 | }; | |
316 | ||
317 | /* This test initializes a thread, runs it, then returns to the main | |
318 | program, which reinitializes the thread, runs it again, etc. Each | |
319 | iteration corresponds to 1 init, 1 abort, 1 block. */ | |
320 | ||
321 | static qt_t *test06_sp; | |
322 | ||
323 | ||
324 | static void | |
325 | test06_aux2 (void *null0a, void *null1b, void *null2b, qt_userf_t *null) | |
326 | { | |
327 | QT_ABORT (t_null, 0, 0, test06_sp); | |
328 | } | |
329 | ||
330 | ||
331 | static void * | |
332 | test06_aux3 (qt_t *sp, void *null0c, void *null1c) | |
333 | { | |
334 | test06_sp = sp; | |
335 | /* return (garbage); */ | |
336 | } | |
337 | ||
338 | ||
339 | static void | |
340 | test06 (int n) | |
341 | { | |
342 | thread_t *t; | |
343 | ||
344 | t = t_create (0, 0, 0); | |
345 | ||
346 | while (n>0) { | |
347 | /* RETVALUSED */ | |
348 | QT_ARGS (t->top, 0, 0, 0, test06_aux2); | |
349 | QT_BLOCKI (test06_aux3, 0, 0, t->qt); | |
350 | #ifdef NDEF | |
351 | /* RETVALUSED */ | |
352 | QT_ARGS (t->top, 0, 0, 0, test06_aux2); | |
353 | QT_BLOCKI (test06_aux3, 0, 0, t->qt); | |
354 | ||
355 | /* RETVALUSED */ | |
356 | QT_ARGS (t->top, 0, 0, 0, test06_aux2); | |
357 | QT_BLOCKI (test06_aux3, 0, 0, t->qt); | |
358 | ||
359 | /* RETVALUSED */ | |
360 | QT_ARGS (t->top, 0, 0, 0, test06_aux2); | |
361 | QT_BLOCKI (test06_aux3, 0, 0, t->qt); | |
362 | ||
363 | /* RETVALUSED */ | |
364 | QT_ARGS (t->top, 0, 0, 0, test06_aux2); | |
365 | QT_BLOCKI (test06_aux3, 0, 0, t->qt); | |
366 | ||
367 | n -= 5; | |
368 | #else | |
369 | --n; | |
370 | #endif | |
371 | } | |
372 | } | |
373 | ||
374 | static char test07_msg[] = "*cswap between threads"; | |
375 | ||
376 | static char const *test07_descr[] = { | |
377 | "Build a chain of threads where each thread has a fixed successor.", | |
378 | "There is no scheduling performed. Each thread but one is a loop", | |
379 | "that simply blocks with QT_BLOCKI, calling a helper that saves the", | |
380 | "current stack pointer. The last thread decrements a count, and,", | |
381 | "if zero, aborts back to the main thread. Else it continues with", | |
382 | "the blocking chain. The count is divided by the number of threads", | |
383 | "in the chain, so `n' is the number of integer block operations.", | |
384 | ":: integer cswap = QT_BLOCKI + a procedure call.", | |
385 | NULL | |
386 | }; | |
387 | ||
388 | /* This test repeatedly blocks a bunch of threads. | |
389 | Each iteration corresponds to one block operation. | |
390 | ||
391 | The threads are arranged so that there are TEST07_N-1 of them that | |
392 | run `test07_aux2'. Each one of those blocks saving it's sp to | |
393 | storage owned by the preceding thread; a pointer to that storage is | |
394 | passed in via `mep'. Each thread has a handle on it's own storage | |
395 | for the next thread, referenced by `nxtp', and it blocks by passing | |
396 | control to `*nxtp', telling the helper function to save its state | |
397 | in `*mep'. The last thread in the chain decrements a count and, if | |
398 | it's gone below zero, returns to `test07'; otherwise, it invokes | |
399 | the first thread in the chain. */ | |
400 | ||
401 | static qt_t *test07_heavy; | |
402 | ||
403 | #define TEST07_N (4) | |
404 | ||
405 | ||
406 | static void | |
407 | test07_aux2 (void *null0, void *mep, void *nxtp, qt_userf_t *null) | |
408 | { | |
409 | qt_t *nxt; | |
410 | ||
411 | while (1) { | |
412 | nxt = *(qt_t **)nxtp; | |
413 | #ifdef NDEF | |
414 | printf ("Helper 0x%p\n", nxtp); | |
415 | #endif | |
416 | QT_BLOCKI (t_splat, mep, 0, nxt); | |
417 | } | |
418 | } | |
419 | ||
420 | static void | |
421 | test07_aux3 (void *np, void *mep, void *nxtp, qt_userf_t *null) | |
422 | { | |
423 | int n; | |
424 | ||
425 | n = *(int *)np; | |
426 | while (1) { | |
427 | n -= TEST07_N; | |
428 | if (n<0) { | |
429 | QT_ABORT (t_splat, mep, 0, test07_heavy); | |
430 | } | |
431 | QT_BLOCKI (t_splat, mep, 0, *(qt_t **)nxtp); | |
432 | } | |
433 | } | |
434 | ||
435 | ||
436 | static void | |
437 | test07 (int n) | |
438 | { | |
439 | int i; | |
440 | thread_t *t[TEST07_N]; | |
441 | ||
442 | for (i=0; i<TEST07_N; ++i) { | |
443 | t[i] = t_create (0, 0, 0); | |
444 | } | |
445 | for (i=0; i<TEST07_N-1; ++i) { | |
446 | /* RETVALUSED */ | |
447 | QT_ARGS (t[i]->top, 0, &t[i]->qt, &t[i+1]->qt, test07_aux2); | |
448 | } | |
449 | /* RETVALUSED */ | |
450 | QT_ARGS (t[i]->top, &n, &t[TEST07_N-1]->qt, &t[0]->qt, test07_aux3); | |
451 | QT_BLOCKI (t_splat, &test07_heavy, 0, t[0]->qt); | |
452 | } | |
453 | ||
454 | ||
455 | static char test08_msg[] = "Floating-point cswap between threads"; | |
456 | ||
457 | static char const *test08_descr[] = { | |
458 | "Measure context switch times including floating-point, use QT_BLOCK.", | |
459 | NULL | |
460 | }; | |
461 | ||
462 | static qt_t *test08_heavy; | |
463 | ||
464 | #define TEST08_N (4) | |
465 | ||
466 | ||
467 | static void | |
468 | test08_aux2 (void *null0, void *mep, void *nxtp, qt_userf_t *null) | |
469 | { | |
470 | qt_t *nxt; | |
471 | ||
472 | while (1) { | |
473 | nxt = *(qt_t **)nxtp; | |
474 | QT_BLOCK (t_splat, mep, 0, nxt); | |
475 | } | |
476 | } | |
477 | ||
478 | static void | |
479 | test08_aux3 (void *np, void *mep, void *nxtp, qt_userf_t *null) | |
480 | { | |
481 | int n; | |
482 | ||
483 | n = *(int *)np; | |
484 | while (1) { | |
485 | n -= TEST08_N; | |
486 | if (n<0) { | |
487 | QT_ABORT (t_splat, mep, 0, test08_heavy); | |
488 | } | |
489 | QT_BLOCK (t_splat, mep, 0, *(qt_t **)nxtp); | |
490 | } | |
491 | } | |
492 | ||
493 | ||
494 | static void | |
495 | test08 (int n) | |
496 | { | |
497 | int i; | |
498 | thread_t *t[TEST08_N]; | |
499 | ||
500 | for (i=0; i<TEST08_N; ++i) { | |
501 | t[i] = t_create (0, 0, 0); | |
502 | } | |
503 | for (i=0; i<TEST08_N-1; ++i) { | |
504 | /* RETVALUSED */ | |
505 | QT_ARGS (t[i]->top, 0, &t[i]->qt, &t[i+1]->qt, test08_aux2); | |
506 | } | |
507 | /* RETVALUSED */ | |
508 | QT_ARGS (t[i]->top, &n, &t[TEST08_N-1]->qt, &t[0]->qt, test08_aux3); | |
509 | QT_BLOCK (t_splat, &test08_heavy, 0, t[0]->qt); | |
510 | } | |
511 | ||
512 | ||
513 | /* Test the varargs procedure calling. */ | |
514 | ||
515 | char const test09_msg[] = { "Start and run threads using varargs." }; | |
516 | ||
517 | thread_t *test09_t0, *test09_t1, *test09_t2, *test09_main; | |
518 | ||
519 | thread_t * | |
520 | test09_create (qt_startup_t *start, qt_vuserf_t *f, | |
521 | qt_cleanup_t *cleanup, int nbytes, ...) | |
522 | { | |
523 | va_list ap; | |
524 | thread_t *t; | |
525 | ||
526 | t = t_alloc(); | |
527 | va_start (ap, nbytes); | |
528 | t->qt = QT_VARGS (t->top, nbytes, ap, t, start, f, cleanup); | |
529 | va_end (ap); | |
530 | return (t); | |
531 | } | |
532 | ||
533 | ||
534 | static void | |
535 | test09_cleanup (void *pt, void *vuserf_retval) | |
536 | { | |
537 | assert (vuserf_retval == (void *)17); | |
538 | QT_ABORT (t_splat, &((thread_t *)pt)->qt, 0, | |
539 | ((thread_t *)pt)->next->qt); | |
540 | } | |
541 | ||
542 | ||
543 | static void | |
544 | test09_start (void *pt) | |
545 | { | |
546 | } | |
547 | ||
548 | ||
549 | static void * | |
550 | test09_user0 (void) | |
551 | { | |
552 | QT_BLOCKI (t_splat, &test09_t0->qt, 0, test09_t1->qt); | |
553 | return ((void *)17); | |
554 | } | |
555 | ||
556 | static void * | |
557 | test09_user2 (int one, int two) | |
558 | { | |
559 | assert (one == 1); | |
560 | assert (two == 2); | |
561 | QT_BLOCKI (t_splat, &test09_t1->qt, 0, test09_t2->qt); | |
562 | assert (one == 1); | |
563 | assert (two == 2); | |
564 | return ((void *)17); | |
565 | } | |
566 | ||
567 | static void * | |
568 | test09_user10 (int one, int two, int three, int four, int five, | |
569 | int six, int seven, int eight, int nine, int ten) | |
570 | { | |
571 | assert (one == 1); | |
572 | assert (two == 2); | |
573 | assert (three == 3); | |
574 | assert (four == 4); | |
575 | assert (five == 5); | |
576 | assert (six == 6); | |
577 | assert (seven == 7); | |
578 | assert (eight == 8); | |
579 | assert (nine == 9); | |
580 | assert (ten == 10); | |
581 | QT_BLOCKI (t_splat, &test09_t2->qt, 0, test09_main->qt); | |
582 | assert (one == 1); | |
583 | assert (two == 2); | |
584 | assert (three == 3); | |
585 | assert (four == 4); | |
586 | assert (five == 5); | |
587 | assert (six == 6); | |
588 | assert (seven == 7); | |
589 | assert (eight == 8); | |
590 | assert (nine == 9); | |
591 | assert (ten == 10); | |
592 | return ((void *)17); | |
593 | } | |
594 | ||
595 | ||
596 | void | |
597 | test09 (int n) | |
598 | { | |
599 | thread_t main; | |
600 | ||
601 | test09_main = &main; | |
602 | ||
603 | while (--n >= 0) { | |
604 | test09_t0 = test09_create (test09_start, (qt_vuserf_t*)test09_user0, | |
605 | test09_cleanup, 0); | |
606 | test09_t1 = test09_create (test09_start, (qt_vuserf_t*)test09_user2, | |
607 | test09_cleanup, 2 * sizeof(qt_word_t), 1, 2); | |
608 | test09_t2 = test09_create (test09_start, (qt_vuserf_t*)test09_user10, | |
609 | test09_cleanup, 10 * sizeof(qt_word_t), | |
610 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | |
611 | ||
612 | /* Chaining used by `test09_cleanup' to determine who is next. */ | |
613 | test09_t0->next = test09_t1; | |
614 | test09_t1->next = test09_t2; | |
615 | test09_t2->next = test09_main; | |
616 | ||
617 | QT_BLOCKI (t_splat, &test09_main->qt, 0, test09_t0->qt); | |
618 | QT_BLOCKI (t_splat, &test09_main->qt, 0, test09_t0->qt); | |
619 | ||
620 | t_free (test09_t0); | |
621 | t_free (test09_t1); | |
622 | t_free (test09_t2); | |
623 | } | |
624 | } | |
625 | ||
626 | ||
627 | \f/* Test 10/11/12: time the cost of various number of args. */ | |
628 | ||
629 | char const test10_msg[] = { "*Test varargs init & startup w/ 0 args." }; | |
630 | ||
631 | char const *test10_descr[] = { | |
632 | "Start and stop threads that use variant argument lists (varargs).", | |
633 | "Each thread is initialized by calling a routine that calls", | |
634 | "QT_VARARGS. Then runs the thread by calling QT_BLOCKI to hald the", | |
635 | "main thread, a helper that saves the main thread's stack pointer,", | |
636 | "a null startup function, a null user function, a cleanup function", | |
637 | "that calls QT_ABORT and restarts the main thread. Copies no user", | |
638 | "parameters.", | |
639 | ":: varargs start/stop = QT_BLOCKI + QT_ABORT + 6 function calls.", | |
640 | NULL | |
641 | }; | |
642 | ||
643 | /* Helper function to send control back to main. | |
644 | Don't save anything. */ | |
645 | ||
646 | ||
647 | /* Helper function for starting the varargs thread. Save the stack | |
648 | pointer of the main thread so we can get back there eventually. */ | |
649 | ||
650 | ||
651 | /* Startup function for a varargs thread. */ | |
652 | ||
653 | static void | |
654 | test10_startup (void *pt) | |
655 | { | |
656 | } | |
657 | ||
658 | ||
659 | /* User function for a varargs thread. */ | |
660 | ||
661 | static void * | |
662 | test10_run (int arg0, ...) | |
663 | { | |
664 | /* return (garbage); */ | |
665 | } | |
666 | ||
667 | ||
668 | /* Cleanup function for a varargs thread. Send control | |
669 | back to the main thread. Don't save any state from the thread that | |
670 | is halting. */ | |
671 | ||
672 | void | |
673 | test10_cleanup (void *pt, void *vuserf_retval) | |
674 | { | |
675 | QT_ABORT (t_null, 0, 0, ((thread_t *)pt)->qt); | |
676 | } | |
677 | ||
678 | ||
679 | void | |
680 | test10_init (thread_t *new, thread_t *next, int nbytes, ...) | |
681 | { | |
682 | va_list ap; | |
683 | ||
684 | va_start (ap, nbytes); | |
685 | new->qt = QT_VARGS (new->top, nbytes, ap, next, test10_startup, | |
686 | test10_run, test10_cleanup); | |
687 | va_end (ap); | |
688 | } | |
689 | ||
690 | ||
691 | void | |
692 | test10 (int n) | |
693 | { | |
694 | thread_t main; | |
695 | thread_t *t; | |
696 | ||
697 | t = t_alloc(); | |
698 | t->next = &main; | |
699 | ||
700 | while (--n >= 0) { | |
701 | test10_init (t, &main, 0); | |
702 | QT_BLOCKI (t_splat, &main.qt, 0, t->qt); | |
703 | } | |
704 | t_free (t); | |
705 | } | |
706 | ||
707 | ||
708 | char const test11_msg[] = { "*Test varargs init & startup w/ 2 args." }; | |
709 | ||
710 | char const *test11_descr[] = { | |
711 | "Varargs initialization/run. Copies 2 user arguments.", | |
712 | ":: varargs 2 start/stop = QT_VARGS(2 args), QT_BLOCKI, QT_ABORT, 6 f() calls.", | |
713 | NULL | |
714 | }; | |
715 | ||
716 | ||
717 | void | |
718 | test11 (int n) | |
719 | { | |
720 | thread_t main; | |
721 | thread_t *t; | |
722 | ||
723 | t = t_alloc(); | |
724 | t->next = &main; | |
725 | ||
726 | while (--n >= 0) { | |
727 | test10_init (t, &main, 2 * sizeof(int), 2, 1); | |
728 | QT_BLOCKI (t_splat, &main.qt, 0, t->qt); | |
729 | } | |
730 | t_free (t); | |
731 | } | |
732 | ||
733 | char const test12_msg[] = { "*Test varargs init & startup w/ 4 args." }; | |
734 | ||
735 | char const *test12_descr[] = { | |
736 | "Varargs initialization/run. Copies 4 user arguments.", | |
737 | ":: varargs 4 start/stop = QT_VARGS(4 args), QT_BLOCKI, QT_ABORT, 6 f() calls.", | |
738 | NULL | |
739 | }; | |
740 | ||
741 | ||
742 | void | |
743 | test12 (int n) | |
744 | { | |
745 | thread_t main; | |
746 | thread_t *t; | |
747 | ||
748 | t = t_alloc(); | |
749 | t->next = &main; | |
750 | ||
751 | while (--n >= 0) { | |
752 | test10_init (t, &main, 4 * sizeof(int), 4, 3, 2, 1); | |
753 | QT_BLOCKI (t_splat, &main.qt, 0, t->qt); | |
754 | } | |
755 | t_free (t); | |
756 | } | |
757 | ||
758 | ||
759 | char const test13_msg[] = { "*Test varargs init & startup w/ 8 args." }; | |
760 | ||
761 | char const *test13_descr[] = { | |
762 | "Varargs initialization/run. Copies 8 user arguments.", | |
763 | ":: varargs 8 start/stop = QT_VARGS(8 args), QT_BLOCKI, QT_ABORT, 6 f() calls.", | |
764 | NULL | |
765 | }; | |
766 | ||
767 | void | |
768 | test13 (int n) | |
769 | { | |
770 | thread_t main; | |
771 | thread_t *t; | |
772 | ||
773 | t = t_alloc(); | |
774 | t->next = &main; | |
775 | ||
776 | while (--n >= 0) { | |
777 | test10_init (t, &main, 8 * sizeof(int), 8, 7, 6, 5, 4, 3, 2, 1); | |
778 | QT_BLOCKI (t_splat, &main.qt, 0, t->qt); | |
779 | } | |
780 | t_free (t); | |
781 | } | |
782 | ||
783 | ||
784 | char const test14_msg[] = { "*Test varargs initialization w/ 0 args." }; | |
785 | ||
786 | char const *test14_descr[] = { | |
787 | "Varargs initialization without running the thread. Just calls", | |
788 | "QT_VARGS.", | |
789 | ":: varargs 0 init = QT_VARGS()", | |
790 | NULL | |
791 | }; | |
792 | ||
793 | void | |
794 | test14 (int n) | |
795 | { | |
796 | thread_t main; | |
797 | thread_t *t; | |
798 | ||
799 | t = t_alloc(); | |
800 | t->next = &main; | |
801 | ||
802 | while (--n >= 0) { | |
803 | test10_init (t, &main, 0 * sizeof(int)); | |
804 | } | |
805 | t_free (t); | |
806 | } | |
807 | ||
808 | ||
809 | char const test15_msg[] = { "*Test varargs initialization w/ 2 args." }; | |
810 | ||
811 | char const *test15_descr[] = { | |
812 | "Varargs initialization without running the thread. Just calls", | |
813 | "QT_VARGS.", | |
814 | ":: varargs 2 init = QT_VARGS(2 args)", | |
815 | NULL | |
816 | }; | |
817 | ||
818 | void | |
819 | test15 (int n) | |
820 | { | |
821 | thread_t main; | |
822 | thread_t *t; | |
823 | ||
824 | t = t_alloc(); | |
825 | t->next = &main; | |
826 | ||
827 | while (--n >= 0) { | |
828 | test10_init (t, &main, 2 * sizeof(int), 2, 1); | |
829 | } | |
830 | t_free (t); | |
831 | } | |
832 | ||
833 | char const test16_msg[] = { "*Test varargs initialization w/ 4 args." }; | |
834 | ||
835 | char const *test16_descr[] = { | |
836 | "Varargs initialization without running the thread. Just calls", | |
837 | "QT_VARGS.", | |
838 | ":: varargs 4 init = QT_VARGS(4 args)", | |
839 | NULL | |
840 | }; | |
841 | ||
842 | ||
843 | void | |
844 | test16 (int n) | |
845 | { | |
846 | thread_t main; | |
847 | thread_t *t; | |
848 | ||
849 | t = t_alloc(); | |
850 | t->next = &main; | |
851 | ||
852 | while (--n >= 0) { | |
853 | test10_init (t, &main, 4 * sizeof(int), 4, 3, 2, 1); | |
854 | } | |
855 | t_free (t); | |
856 | } | |
857 | ||
858 | ||
859 | char const test17_msg[] = { "*Test varargs initialization w/ 8 args." }; | |
860 | ||
861 | char const *test17_descr[] = { | |
862 | "Varargs initialization without running the thread. Just calls", | |
863 | "QT_VARGS.", | |
864 | ":: varargs 8 init = QT_VARGS(8 args)", | |
865 | NULL | |
866 | }; | |
867 | ||
868 | ||
869 | void | |
870 | test17 (int n) | |
871 | { | |
872 | thread_t main; | |
873 | thread_t *t; | |
874 | ||
875 | t = t_alloc(); | |
876 | t->next = &main; | |
877 | ||
878 | while (--n >= 0) { | |
879 | test10_init (t, &main, 8 * sizeof(int), 8, 7, 6, 5, 4, 3, 2, 1); | |
880 | } | |
881 | t_free (t); | |
882 | } | |
883 | ||
884 | \f/* Test times for basic machine operations. */ | |
885 | ||
886 | char const test18_msg[] = { "*Call register indirect." }; | |
887 | char const *test18_descr[] = { NULL }; | |
888 | ||
889 | void | |
890 | test18 (int n) | |
891 | { | |
892 | b_call_reg (n); | |
893 | } | |
894 | ||
895 | ||
896 | char const test19_msg[] = { "*Call immediate." }; | |
897 | char const *test19_descr[] = { NULL }; | |
898 | ||
899 | void | |
900 | test19 (int n) | |
901 | { | |
902 | b_call_imm (n); | |
903 | } | |
904 | ||
905 | ||
906 | char const test20_msg[] = { "*Add register-to-register." }; | |
907 | char const *test20_descr[] = { NULL }; | |
908 | ||
909 | void | |
910 | test20 (int n) | |
911 | { | |
912 | b_add (n); | |
913 | } | |
914 | ||
915 | ||
916 | char const test21_msg[] = { "*Load memory to a register." }; | |
917 | char const *test21_descr[] = { NULL }; | |
918 | ||
919 | void | |
920 | test21 (int n) | |
921 | { | |
922 | b_load (n); | |
923 | } | |
924 | ||
925 | \f/* Driver. */ | |
926 | ||
927 | typedef struct foo_t { | |
928 | char const *msg; /* Message to print for generic help. */ | |
929 | char const **descr; /* A description of what is done by the test. */ | |
930 | void (*f)(int n); | |
931 | } foo_t; | |
932 | ||
933 | ||
934 | static foo_t foo[] = { | |
935 | { "Usage:\n", NULL, (void(*)(int n))usage }, | |
936 | { test01_msg, test01_descr, test01 }, | |
937 | { test02_msg, NULL, test02 }, | |
938 | { test03_msg, NULL, test03 }, | |
939 | { test04_msg, NULL, test04 }, | |
940 | { test05_msg, NULL, test05 }, | |
941 | { test06_msg, test06_descr, test06 }, | |
942 | { test07_msg, test07_descr, test07 }, | |
943 | { test08_msg, test08_descr, test08 }, | |
944 | { test09_msg, NULL, test09 }, | |
945 | { test10_msg, test10_descr, test10 }, | |
946 | { test11_msg, test11_descr, test11 }, | |
947 | { test12_msg, test12_descr, test12 }, | |
948 | { test13_msg, test13_descr, test13 }, | |
949 | { test14_msg, test14_descr, test14 }, | |
950 | { test15_msg, test15_descr, test15 }, | |
951 | { test16_msg, test16_descr, test16 }, | |
952 | { test17_msg, test17_descr, test17 }, | |
953 | { test18_msg, test18_descr, test18 }, | |
954 | { test19_msg, test19_descr, test19 }, | |
955 | { test20_msg, test20_descr, test20 }, | |
956 | { test21_msg, test21_descr, test21 }, | |
957 | { 0, 0 } | |
958 | }; | |
959 | ||
960 | static int tv = 0; | |
961 | ||
962 | void | |
963 | tracer () | |
964 | { | |
965 | ||
966 | fprintf (stderr, "tracer\t%d\n", tv++); | |
967 | fflush (stderr); | |
968 | } | |
969 | ||
970 | void | |
971 | tracer2 (void *val) | |
972 | { | |
973 | fprintf (stderr, "tracer2\t%d val=0x%p", tv++, val); | |
974 | fflush (stderr); | |
975 | } | |
976 | ||
977 | ||
978 | void | |
979 | describe() | |
980 | { | |
981 | int i; | |
982 | FILE *out = stdout; | |
983 | ||
984 | for (i=0; foo[i].msg; ++i) { | |
985 | if (foo[i].descr) { | |
986 | int j; | |
987 | ||
988 | putc ('\n', out); | |
989 | fprintf (out, "[%d]\n", i); | |
990 | for (j=0; foo[i].descr[j]; ++j) { | |
991 | fputs (foo[i].descr[j], out); | |
992 | putc ('\n', out); | |
993 | } | |
994 | } | |
995 | } | |
996 | exit (0); | |
997 | } | |
998 | ||
999 | ||
1000 | void | |
1001 | usage() | |
1002 | { | |
1003 | int i; | |
1004 | ||
1005 | fputs (foo[0].msg, stderr); | |
1006 | for (i=1; foo[i].msg; ++i) { | |
1007 | fprintf (stderr, "%2d\t%s\n", i, foo[i].msg); | |
1008 | } | |
1009 | exit (1); | |
1010 | } | |
1011 | ||
1012 | ||
1013 | void | |
1014 | args (int *which, int *n, int argc, char **argv) | |
1015 | { | |
1016 | static int nfuncs = 0; | |
1017 | ||
1018 | if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'h') { | |
1019 | describe(); | |
1020 | } | |
1021 | ||
1022 | if (nfuncs == 0) { | |
1023 | for (nfuncs=0; foo[nfuncs].msg; ++nfuncs) | |
1024 | ; | |
1025 | } | |
1026 | ||
1027 | if (argc != 2 && argc != 3) { | |
1028 | usage(); | |
1029 | } | |
1030 | ||
1031 | *which = atoi (argv[1]); | |
1032 | if (*which < 0 || *which >= nfuncs) { | |
1033 | usage(); | |
1034 | } | |
1035 | *n = (argc == 3) | |
1036 | ? atoi (argv[2]) | |
1037 | : 1; | |
1038 | } | |
1039 | ||
1040 | ||
1041 | int | |
1042 | main (int argc, char **argv) | |
1043 | { | |
1044 | int which, n; | |
1045 | args (&which, &n, argc, argv); | |
1046 | (*(foo[which].f))(n); | |
1047 | exit (0); | |
1048 | return (0); | |
1049 | } |