Heap frames have a "frame kind" bit
[bpt/guile.git] / libguile / vm.c
CommitLineData
aab9d46c 1/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
a98cef7e 2 *
560b9c25 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
a98cef7e 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
560b9c25
AW
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
a98cef7e 12 *
560b9c25
AW
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
560b9c25 17 */
a98cef7e 18
13c47753
AW
19#if HAVE_CONFIG_H
20# include <config.h>
21#endif
22
da8b4747 23#include <stdlib.h>
6d14383e 24#include <alloca.h>
daccfef4 25#include <alignof.h>
17e90c5e 26#include <string.h>
e78d4bf9 27#include <stdint.h>
e3eb628d 28
1c44468d 29#include "libguile/bdw-gc.h"
e3eb628d
LC
30#include <gc/gc_mark.h>
31
560b9c25 32#include "_scm.h"
adaf86ec 33#include "control.h"
ac99cb0c 34#include "frames.h"
17e90c5e 35#include "instructions.h"
4cbc95f1 36#include "loader.h"
ac99cb0c 37#include "programs.h"
a98cef7e 38#include "vm.h"
486013d6 39#include "vm-builtins.h"
a98cef7e 40
aab9d46c
SIT
41#include "private-gc.h" /* scm_getenv_int */
42
97b18a66 43static int vm_default_engine = SCM_VM_REGULAR_ENGINE;
ea9f4f4b
AW
44
45/* Unfortunately we can't snarf these: snarfed things are only loaded up from
46 (system vm vm), which might not be loaded before an error happens. */
47static SCM sym_vm_run;
48static SCM sym_vm_error;
49static SCM sym_keyword_argument_error;
50static SCM sym_regular;
51static SCM sym_debug;
a98cef7e 52
11ea1aba
AW
53/* The VM has a number of internal assertions that shouldn't normally be
54 necessary, but might be if you think you found a bug in the VM. */
55#define VM_ENABLE_ASSERTIONS
56
53e28ed9
AW
57/* #define VM_ENABLE_PARANOID_ASSERTIONS */
58
e3eb628d
LC
59/* When defined, arrange so that the GC doesn't scan the VM stack beyond its
60 current SP. This should help avoid excess data retention. See
61 http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/3001
62 for a discussion. */
63#define VM_ENABLE_PRECISE_STACK_GC_SCAN
64
f1046e6b
LC
65/* Size in SCM objects of the stack reserve. The reserve is used to run
66 exception handling code in case of a VM stack overflow. */
67#define VM_STACK_RESERVE_SIZE 512
68
e3eb628d 69
a98cef7e 70\f
a98cef7e
KN
71/*
72 * VM Continuation
73 */
74
6f3b0cc2
AW
75void
76scm_i_vm_cont_print (SCM x, SCM port, scm_print_state *pstate)
77{
0607ebbf 78 scm_puts_unlocked ("#<vm-continuation ", port);
6f3b0cc2 79 scm_uintprint (SCM_UNPACK (x), 16, port);
0607ebbf 80 scm_puts_unlocked (">", port);
6f3b0cc2 81}
17e90c5e 82
d8873dfe
AW
83/* In theory, a number of vm instances can be active in the call trace, and we
84 only want to reify the continuations of those in the current continuation
85 root. I don't see a nice way to do this -- ideally it would involve dynwinds,
86 and previous values of the *the-vm* fluid within the current continuation
87 root. But we don't have access to continuation roots in the dynwind stack.
88 So, just punt for now, we just capture the continuation for the current VM.
89
90 While I'm on the topic, ideally we could avoid copying the C stack if the
91 continuation root is inside VM code, and call/cc was invoked within that same
92 call to vm_run; but that's currently not implemented.
93 */
cee1d22c 94SCM
9121d9f1 95scm_i_vm_capture_stack (SCM *stack_base, SCM *fp, SCM *sp, scm_t_uint32 *ra,
840ec334 96 scm_t_dynstack *dynstack, scm_t_uint32 flags)
a98cef7e 97{
d8873dfe
AW
98 struct scm_vm_cont *p;
99
100 p = scm_gc_malloc (sizeof (*p), "capture_vm_cont");
101 p->stack_size = sp - stack_base + 1;
d8eeb67c
LC
102 p->stack_base = scm_gc_malloc (p->stack_size * sizeof (SCM),
103 "capture_vm_cont");
d8873dfe 104 p->ra = ra;
d8873dfe
AW
105 p->sp = sp;
106 p->fp = fp;
107 memcpy (p->stack_base, stack_base, (sp + 1 - stack_base) * sizeof (SCM));
108 p->reloc = p->stack_base - stack_base;
9ede013f 109 p->dynstack = dynstack;
cee1d22c 110 p->flags = flags;
6f3b0cc2 111 return scm_cell (scm_tc7_vm_cont, (scm_t_bits)p);
a98cef7e
KN
112}
113
114static void
d8873dfe 115vm_return_to_continuation (SCM vm, SCM cont, size_t n, SCM *argv)
a98cef7e 116{
d8873dfe
AW
117 struct scm_vm *vp;
118 struct scm_vm_cont *cp;
119 SCM *argv_copy;
120
121 argv_copy = alloca (n * sizeof(SCM));
122 memcpy (argv_copy, argv, n * sizeof(SCM));
123
124 vp = SCM_VM_DATA (vm);
125 cp = SCM_VM_CONT_DATA (cont);
126
f8085163 127 if (vp->stack_size < cp->stack_size + n + 3)
29366989
AW
128 scm_misc_error ("vm-engine", "not enough space to reinstate continuation",
129 scm_list_2 (vm, cont));
130
d8873dfe
AW
131 vp->sp = cp->sp;
132 vp->fp = cp->fp;
133 memcpy (vp->stack_base, cp->stack_base, cp->stack_size * sizeof (SCM));
bfffd258 134
03f16599
AW
135 {
136 size_t i;
137
138 /* Push on an empty frame, as the continuation expects. */
f8085163 139 for (i = 0; i < 3; i++)
03f16599
AW
140 {
141 vp->sp++;
142 *vp->sp = SCM_BOOL_F;
143 }
144
145 /* Push the return values. */
146 for (i = 0; i < n; i++)
147 {
148 vp->sp++;
149 *vp->sp = argv_copy[i];
150 }
840ec334 151 vp->ip = cp->ra;
03f16599 152 }
d8873dfe 153}
bfffd258 154
bfffd258 155SCM
9ede013f 156scm_i_capture_current_stack (void)
bfffd258 157{
9ede013f
AW
158 scm_i_thread *thread;
159 SCM vm;
160 struct scm_vm *vp;
161
162 thread = SCM_I_CURRENT_THREAD;
163 vm = scm_the_vm ();
164 vp = SCM_VM_DATA (vm);
165
840ec334 166 return scm_i_vm_capture_stack (vp->stack_base, vp->fp, vp->sp, vp->ip,
9ede013f
AW
167 scm_dynstack_capture_all (&thread->dynstack),
168 0);
a98cef7e
KN
169}
170
ea0cd17d
AW
171static void vm_dispatch_apply_hook (SCM vm) SCM_NOINLINE;
172static void vm_dispatch_push_continuation_hook (SCM vm) SCM_NOINLINE;
173static void vm_dispatch_pop_continuation_hook (SCM vm, SCM *old_fp) SCM_NOINLINE;
174static void vm_dispatch_next_hook (SCM vm) SCM_NOINLINE;
175static void vm_dispatch_abort_hook (SCM vm) SCM_NOINLINE;
176static void vm_dispatch_restore_continuation_hook (SCM vm) SCM_NOINLINE;
c850a0ff 177
b1b942b7 178static void
c850a0ff 179vm_dispatch_hook (SCM vm, int hook_num, SCM *argv, int n)
b1b942b7 180{
7656f194
AW
181 struct scm_vm *vp;
182 SCM hook;
b3567435 183 struct scm_frame c_frame;
8e4c60ff 184 scm_t_cell *frame;
893fb8d0 185 int saved_trace_level;
b1b942b7 186
7656f194
AW
187 vp = SCM_VM_DATA (vm);
188 hook = vp->hooks[hook_num];
b1b942b7 189
7656f194
AW
190 if (SCM_LIKELY (scm_is_false (hook))
191 || scm_is_null (SCM_HOOK_PROCEDURES (hook)))
192 return;
b3567435 193
893fb8d0
AW
194 saved_trace_level = vp->trace_level;
195 vp->trace_level = 0;
b3567435
LC
196
197 /* Allocate a frame object on the stack. This is more efficient than calling
198 `scm_c_make_frame ()' to allocate on the heap, but it forces hooks to not
199 capture frame objects.
200
201 At the same time, procedures such as `frame-procedure' make sense only
202 while the stack frame represented by the frame object is visible, so it
203 seems reasonable to limit the lifetime of frame objects. */
204
205 c_frame.stack_holder = vm;
89b235af
AW
206 c_frame.fp_offset = vp->fp - vp->stack_base;
207 c_frame.sp_offset = vp->sp - vp->stack_base;
b3567435 208 c_frame.ip = vp->ip;
8e4c60ff
LC
209
210 /* Arrange for FRAME to be 8-byte aligned, like any other cell. */
211 frame = alloca (sizeof (*frame) + 8);
212 frame = (scm_t_cell *) ROUND_UP ((scm_t_uintptr) frame, 8UL);
213
050a40db 214 frame->word_0 = SCM_PACK (scm_tc7_frame | (SCM_VM_FRAME_KIND_VM << 8));
21041372 215 frame->word_1 = SCM_PACK_POINTER (&c_frame);
b3567435 216
c850a0ff
AW
217 if (n == 0)
218 {
219 SCM args[1];
220
221 args[0] = SCM_PACK_POINTER (frame);
222 scm_c_run_hookn (hook, args, 1);
223 }
224 else if (n == 1)
225 {
226 SCM args[2];
227
228 args[0] = SCM_PACK_POINTER (frame);
229 args[1] = argv[0];
230 scm_c_run_hookn (hook, args, 2);
231 }
232 else
233 {
234 SCM args = SCM_EOL;
235
236 while (n--)
237 args = scm_cons (argv[n], args);
238 scm_c_run_hook (hook, scm_cons (SCM_PACK_POINTER (frame), args));
239 }
b3567435 240
893fb8d0 241 vp->trace_level = saved_trace_level;
b1b942b7
AW
242}
243
ea0cd17d
AW
244static void
245vm_dispatch_apply_hook (SCM vm)
246{
247 return vm_dispatch_hook (vm, SCM_VM_APPLY_HOOK, NULL, 0);
248}
249static void vm_dispatch_push_continuation_hook (SCM vm)
250{
251 return vm_dispatch_hook (vm, SCM_VM_PUSH_CONTINUATION_HOOK, NULL, 0);
252}
253static void vm_dispatch_pop_continuation_hook (SCM vm, SCM *old_fp)
254{
255 struct scm_vm *vp = SCM_VM_DATA (vm);
256 return vm_dispatch_hook (vm, SCM_VM_POP_CONTINUATION_HOOK,
257 &SCM_FRAME_LOCAL (old_fp, 1),
258 SCM_FRAME_NUM_LOCALS (old_fp, vp->sp) - 1);
259}
260static void vm_dispatch_next_hook (SCM vm)
261{
262 return vm_dispatch_hook (vm, SCM_VM_NEXT_HOOK, NULL, 0);
263}
264static void vm_dispatch_abort_hook (SCM vm)
265{
266 struct scm_vm *vp = SCM_VM_DATA (vm);
267 return vm_dispatch_hook (vm, SCM_VM_ABORT_CONTINUATION_HOOK,
268 &SCM_FRAME_LOCAL (vp->fp, 1),
269 SCM_FRAME_NUM_LOCALS (vp->fp, vp->sp) - 1);
270}
271static void vm_dispatch_restore_continuation_hook (SCM vm)
272{
273 return vm_dispatch_hook (vm, SCM_VM_RESTORE_CONTINUATION_HOOK, NULL, 0);
274}
275
4f66bcde 276static void
99511cd0
AW
277vm_abort (SCM vm, SCM tag, size_t nstack, SCM *stack_args, SCM tail, SCM *sp,
278 scm_i_jmp_buf *current_registers) SCM_NORETURN;
9d381ba4
AW
279
280static void
99511cd0
AW
281vm_abort (SCM vm, SCM tag, size_t nstack, SCM *stack_args, SCM tail, SCM *sp,
282 scm_i_jmp_buf *current_registers)
4f66bcde 283{
eaefabee 284 size_t i;
2d026f04 285 ssize_t tail_len;
99511cd0 286 SCM *argv;
eaefabee 287
2d026f04
AW
288 tail_len = scm_ilength (tail);
289 if (tail_len < 0)
29366989
AW
290 scm_misc_error ("vm-engine", "tail values to abort should be a list",
291 scm_list_1 (tail));
292
99511cd0
AW
293 argv = alloca ((nstack + tail_len) * sizeof (SCM));
294 for (i = 0; i < nstack; i++)
295 argv[i] = stack_args[i];
296 for (; i < nstack + tail_len; i++, tail = scm_cdr (tail))
2d026f04 297 argv[i] = scm_car (tail);
eaefabee 298
99511cd0
AW
299 /* FIXME: NULLSTACK (SCM_VM_DATA (vp)->sp - sp) */
300 SCM_VM_DATA (vm)->sp = sp;
301
302 scm_c_abort (vm, tag, nstack + tail_len, argv, current_registers);
cee1d22c
AW
303}
304
9d381ba4
AW
305static void
306vm_reinstate_partial_continuation (SCM vm, SCM cont, size_t n, SCM *argv,
307 scm_t_dynstack *dynstack,
308 scm_i_jmp_buf *registers)
cee1d22c 309{
07801437
AW
310 struct scm_vm *vp;
311 struct scm_vm_cont *cp;
312 SCM *argv_copy, *base;
9ede013f 313 scm_t_ptrdiff reloc;
07801437
AW
314 size_t i;
315
316 argv_copy = alloca (n * sizeof(SCM));
317 memcpy (argv_copy, argv, n * sizeof(SCM));
318
319 vp = SCM_VM_DATA (vm);
320 cp = SCM_VM_CONT_DATA (cont);
b636cdb0 321 base = SCM_FRAME_LOCALS_ADDRESS (vp->fp);
9ede013f 322 reloc = cp->reloc + (base - cp->stack_base);
07801437 323
0fc9040f 324#define RELOC(scm_p) \
9ede013f 325 (((SCM *) (scm_p)) + reloc)
07801437
AW
326
327 if ((base - vp->stack_base) + cp->stack_size + n + 1 > vp->stack_size)
29366989
AW
328 scm_misc_error ("vm-engine",
329 "not enough space to instate partial continuation",
330 scm_list_2 (vm, cont));
07801437
AW
331
332 memcpy (base, cp->stack_base, cp->stack_size * sizeof (SCM));
333
334 /* now relocate frame pointers */
335 {
336 SCM *fp;
337 for (fp = RELOC (cp->fp);
338 SCM_FRAME_LOWER_ADDRESS (fp) > base;
339 fp = SCM_FRAME_DYNAMIC_LINK (fp))
340 SCM_FRAME_SET_DYNAMIC_LINK (fp, RELOC (SCM_FRAME_DYNAMIC_LINK (fp)));
341 }
342
343 vp->sp = base - 1 + cp->stack_size;
344 vp->fp = RELOC (cp->fp);
840ec334 345 vp->ip = cp->ra;
07801437 346
840ec334 347 /* Push the arguments. */
07801437
AW
348 for (i = 0; i < n; i++)
349 {
350 vp->sp++;
351 *vp->sp = argv_copy[i];
352 }
9a1c6f1f 353
9d381ba4
AW
354 /* The prompt captured a slice of the dynamic stack. Here we wind
355 those entries onto the current thread's stack. We also have to
356 relocate any prompts that we see along the way. */
357 {
358 scm_t_bits *walk;
359
360 for (walk = SCM_DYNSTACK_FIRST (cp->dynstack);
361 SCM_DYNSTACK_TAG (walk);
362 walk = SCM_DYNSTACK_NEXT (walk))
363 {
364 scm_t_bits tag = SCM_DYNSTACK_TAG (walk);
365
366 if (SCM_DYNSTACK_TAG_TYPE (tag) == SCM_DYNSTACK_TYPE_PROMPT)
367 scm_dynstack_wind_prompt (dynstack, walk, reloc, registers);
368 else
369 scm_dynstack_wind_1 (dynstack, walk);
370 }
371 }
adbdfd6d 372#undef RELOC
4f66bcde
AW
373}
374
375\f
17e90c5e
KN
376/*
377 * VM Internal functions
378 */
379
6f3b0cc2
AW
380void
381scm_i_vm_print (SCM x, SCM port, scm_print_state *pstate)
382{
0a935b2a
LC
383 const struct scm_vm *vm;
384
385 vm = SCM_VM_DATA (x);
386
0607ebbf 387 scm_puts_unlocked ("#<vm ", port);
0a935b2a
LC
388 switch (vm->engine)
389 {
390 case SCM_VM_REGULAR_ENGINE:
0607ebbf 391 scm_puts_unlocked ("regular-engine ", port);
0a935b2a
LC
392 break;
393
394 case SCM_VM_DEBUG_ENGINE:
0607ebbf 395 scm_puts_unlocked ("debug-engine ", port);
0a935b2a
LC
396 break;
397
398 default:
0607ebbf 399 scm_puts_unlocked ("unknown-engine ", port);
0a935b2a 400 }
6f3b0cc2 401 scm_uintprint (SCM_UNPACK (x), 16, port);
0607ebbf 402 scm_puts_unlocked (">", port);
6f3b0cc2
AW
403}
404
53bdfcf0
AW
405\f
406/*
407 * VM Error Handling
408 */
409
410static void vm_error (const char *msg, SCM arg) SCM_NORETURN;
4d497b62
AW
411static void vm_error_bad_instruction (scm_t_uint32 inst) SCM_NORETURN SCM_NOINLINE;
412static void vm_error_unbound (SCM proc, SCM sym) SCM_NORETURN SCM_NOINLINE;
413static void vm_error_unbound_fluid (SCM proc, SCM fluid) SCM_NORETURN SCM_NOINLINE;
414static void vm_error_not_a_variable (const char *func_name, SCM x) SCM_NORETURN SCM_NOINLINE;
415static void vm_error_apply_to_non_list (SCM x) SCM_NORETURN SCM_NOINLINE;
416static void vm_error_kwargs_length_not_even (SCM proc) SCM_NORETURN SCM_NOINLINE;
28d5d253
MW
417static void vm_error_kwargs_invalid_keyword (SCM proc, SCM obj) SCM_NORETURN SCM_NOINLINE;
418static void vm_error_kwargs_unrecognized_keyword (SCM proc, SCM kw) SCM_NORETURN SCM_NOINLINE;
4d497b62
AW
419static void vm_error_too_many_args (int nargs) SCM_NORETURN SCM_NOINLINE;
420static void vm_error_wrong_num_args (SCM proc) SCM_NORETURN SCM_NOINLINE;
421static void vm_error_wrong_type_apply (SCM proc) SCM_NORETURN SCM_NOINLINE;
422static void vm_error_stack_overflow (struct scm_vm *vp) SCM_NORETURN SCM_NOINLINE;
423static void vm_error_stack_underflow (void) SCM_NORETURN SCM_NOINLINE;
424static void vm_error_improper_list (SCM x) SCM_NORETURN SCM_NOINLINE;
425static void vm_error_not_a_pair (const char *subr, SCM x) SCM_NORETURN SCM_NOINLINE;
426static void vm_error_not_a_bytevector (const char *subr, SCM x) SCM_NORETURN SCM_NOINLINE;
427static void vm_error_not_a_struct (const char *subr, SCM x) SCM_NORETURN SCM_NOINLINE;
428static void vm_error_no_values (void) SCM_NORETURN SCM_NOINLINE;
429static void vm_error_not_enough_values (void) SCM_NORETURN SCM_NOINLINE;
82f4bac4 430static void vm_error_wrong_number_of_values (scm_t_uint32 expected) SCM_NORETURN SCM_NOINLINE;
4d497b62
AW
431static void vm_error_continuation_not_rewindable (SCM cont) SCM_NORETURN SCM_NOINLINE;
432static void vm_error_bad_wide_string_length (size_t len) SCM_NORETURN SCM_NOINLINE;
53bdfcf0
AW
433
434static void
435vm_error (const char *msg, SCM arg)
436{
437 scm_throw (sym_vm_error,
438 scm_list_3 (sym_vm_run, scm_from_latin1_string (msg),
439 SCM_UNBNDP (arg) ? SCM_EOL : scm_list_1 (arg)));
440 abort(); /* not reached */
441}
442
443static void
444vm_error_bad_instruction (scm_t_uint32 inst)
445{
446 vm_error ("VM: Bad instruction: ~s", scm_from_uint32 (inst));
447}
448
449static void
450vm_error_unbound (SCM proc, SCM sym)
451{
452 scm_error_scm (scm_misc_error_key, proc,
453 scm_from_latin1_string ("Unbound variable: ~s"),
454 scm_list_1 (sym), SCM_BOOL_F);
455}
456
457static void
458vm_error_unbound_fluid (SCM proc, SCM fluid)
459{
460 scm_error_scm (scm_misc_error_key, proc,
461 scm_from_latin1_string ("Unbound fluid: ~s"),
462 scm_list_1 (fluid), SCM_BOOL_F);
463}
464
465static void
466vm_error_not_a_variable (const char *func_name, SCM x)
467{
468 scm_error (scm_arg_type_key, func_name, "Not a variable: ~S",
469 scm_list_1 (x), scm_list_1 (x));
470}
471
53bdfcf0
AW
472static void
473vm_error_apply_to_non_list (SCM x)
474{
475 scm_error (scm_arg_type_key, "apply", "Apply to non-list: ~S",
476 scm_list_1 (x), scm_list_1 (x));
477}
478
479static void
480vm_error_kwargs_length_not_even (SCM proc)
481{
482 scm_error_scm (sym_keyword_argument_error, proc,
483 scm_from_latin1_string ("Odd length of keyword argument list"),
484 SCM_EOL, SCM_BOOL_F);
485}
486
487static void
4af0d97e 488vm_error_kwargs_invalid_keyword (SCM proc, SCM obj)
53bdfcf0
AW
489{
490 scm_error_scm (sym_keyword_argument_error, proc,
491 scm_from_latin1_string ("Invalid keyword"),
4af0d97e 492 SCM_EOL, scm_list_1 (obj));
53bdfcf0
AW
493}
494
495static void
4af0d97e 496vm_error_kwargs_unrecognized_keyword (SCM proc, SCM kw)
53bdfcf0
AW
497{
498 scm_error_scm (sym_keyword_argument_error, proc,
499 scm_from_latin1_string ("Unrecognized keyword"),
4af0d97e 500 SCM_EOL, scm_list_1 (kw));
53bdfcf0
AW
501}
502
503static void
504vm_error_too_many_args (int nargs)
505{
506 vm_error ("VM: Too many arguments", scm_from_int (nargs));
507}
508
509static void
510vm_error_wrong_num_args (SCM proc)
511{
512 scm_wrong_num_args (proc);
513}
514
515static void
516vm_error_wrong_type_apply (SCM proc)
517{
518 scm_error (scm_arg_type_key, NULL, "Wrong type to apply: ~S",
519 scm_list_1 (proc), scm_list_1 (proc));
520}
521
522static void
523vm_error_stack_overflow (struct scm_vm *vp)
524{
525 if (vp->stack_limit < vp->stack_base + vp->stack_size)
526 /* There are VM_STACK_RESERVE_SIZE bytes left. Make them available so
527 that `throw' below can run on this VM. */
528 vp->stack_limit = vp->stack_base + vp->stack_size;
529 else
530 /* There is no space left on the stack. FIXME: Do something more
531 sensible here! */
532 abort ();
533 vm_error ("VM: Stack overflow", SCM_UNDEFINED);
534}
535
536static void
537vm_error_stack_underflow (void)
538{
539 vm_error ("VM: Stack underflow", SCM_UNDEFINED);
540}
541
542static void
543vm_error_improper_list (SCM x)
544{
545 vm_error ("Expected a proper list, but got object with tail ~s", x);
546}
547
548static void
549vm_error_not_a_pair (const char *subr, SCM x)
550{
551 scm_wrong_type_arg_msg (subr, 1, x, "pair");
552}
553
554static void
555vm_error_not_a_bytevector (const char *subr, SCM x)
556{
557 scm_wrong_type_arg_msg (subr, 1, x, "bytevector");
558}
559
560static void
561vm_error_not_a_struct (const char *subr, SCM x)
562{
563 scm_wrong_type_arg_msg (subr, 1, x, "struct");
564}
565
566static void
567vm_error_no_values (void)
568{
569 vm_error ("Zero values returned to single-valued continuation",
570 SCM_UNDEFINED);
571}
572
573static void
574vm_error_not_enough_values (void)
575{
576 vm_error ("Too few values returned to continuation", SCM_UNDEFINED);
577}
578
82f4bac4
AW
579static void
580vm_error_wrong_number_of_values (scm_t_uint32 expected)
581{
582 vm_error ("Wrong number of values returned to continuation (expected ~a)",
583 scm_from_uint32 (expected));
584}
585
53bdfcf0
AW
586static void
587vm_error_continuation_not_rewindable (SCM cont)
588{
589 vm_error ("Unrewindable partial continuation", cont);
590}
591
592static void
593vm_error_bad_wide_string_length (size_t len)
594{
595 vm_error ("VM: Bad wide string length: ~S", scm_from_size_t (len));
596}
597
53bdfcf0
AW
598
599\f
28b119ee 600
ef6b7f71 601static SCM vm_boot_continuation;
486013d6
AW
602static SCM vm_builtin_apply;
603static SCM vm_builtin_values;
604static SCM vm_builtin_abort_to_prompt;
605static SCM vm_builtin_call_with_values;
606static SCM vm_builtin_call_with_current_continuation;
510ca126 607
ef6b7f71 608static const scm_t_uint32 vm_boot_continuation_code[] = {
095100bb 609 SCM_PACK_OP_24 (halt, 0)
510ca126
AW
610};
611
486013d6 612static const scm_t_uint32 vm_builtin_apply_code[] = {
095100bb
AW
613 SCM_PACK_OP_24 (assert_nargs_ge, 3),
614 SCM_PACK_OP_24 (tail_apply, 0), /* proc in r1, args from r2 */
510ca126
AW
615};
616
486013d6 617static const scm_t_uint32 vm_builtin_values_code[] = {
095100bb 618 SCM_PACK_OP_24 (return_values, 0) /* vals from r1 */
510ca126
AW
619};
620
486013d6 621static const scm_t_uint32 vm_builtin_abort_to_prompt_code[] = {
095100bb
AW
622 SCM_PACK_OP_24 (assert_nargs_ge, 2),
623 SCM_PACK_OP_24 (abort, 0), /* tag in r1, vals from r2 */
486013d6 624 /* FIXME: Partial continuation should capture caller regs. */
095100bb 625 SCM_PACK_OP_24 (return_values, 0) /* vals from r1 */
486013d6
AW
626};
627
628static const scm_t_uint32 vm_builtin_call_with_values_code[] = {
095100bb
AW
629 SCM_PACK_OP_24 (assert_nargs_ee, 3),
630 SCM_PACK_OP_24 (alloc_frame, 7),
631 SCM_PACK_OP_12_12 (mov, 6, 1),
632 SCM_PACK_OP_24 (call, 6), SCM_PACK_OP_ARG_8_24 (0, 1),
633 SCM_PACK_OP_12_12 (mov, 0, 2),
634 SCM_PACK_OP_24 (tail_call_shuffle, 7)
486013d6
AW
635};
636
637static const scm_t_uint32 vm_builtin_call_with_current_continuation_code[] = {
095100bb
AW
638 SCM_PACK_OP_24 (assert_nargs_ee, 2),
639 SCM_PACK_OP_24 (call_cc, 0)
486013d6
AW
640};
641
642
643static SCM
644scm_vm_builtin_ref (unsigned idx)
645{
646 switch (idx)
647 {
9f309e2c 648#define INDEX_TO_NAME(builtin, BUILTIN, req, opt, rest) \
486013d6
AW
649 case SCM_VM_BUILTIN_##BUILTIN: return vm_builtin_##builtin;
650 FOR_EACH_VM_BUILTIN(INDEX_TO_NAME)
651#undef INDEX_TO_NAME
652 default: abort();
653 }
654}
655
9f309e2c 656SCM scm_sym_apply;
486013d6
AW
657static SCM scm_sym_values;
658static SCM scm_sym_abort_to_prompt;
659static SCM scm_sym_call_with_values;
660static SCM scm_sym_call_with_current_continuation;
661
662SCM
663scm_vm_builtin_name_to_index (SCM name)
664#define FUNC_NAME "builtin-name->index"
665{
666 SCM_VALIDATE_SYMBOL (1, name);
667
9f309e2c 668#define NAME_TO_INDEX(builtin, BUILTIN, req, opt, rest) \
486013d6
AW
669 if (scm_is_eq (name, scm_sym_##builtin)) \
670 return scm_from_uint (SCM_VM_BUILTIN_##BUILTIN);
671 FOR_EACH_VM_BUILTIN(NAME_TO_INDEX)
672#undef NAME_TO_INDEX
673
674 return SCM_BOOL_F;
675}
676#undef FUNC_NAME
677
678SCM
679scm_vm_builtin_index_to_name (SCM index)
680#define FUNC_NAME "builtin-index->name"
681{
682 unsigned idx;
683
684 SCM_VALIDATE_UINT_COPY (1, index, idx);
685
686 switch (idx)
687 {
9f309e2c 688#define INDEX_TO_NAME(builtin, BUILTIN, req, opt, rest) \
486013d6
AW
689 case SCM_VM_BUILTIN_##BUILTIN: return scm_sym_##builtin;
690 FOR_EACH_VM_BUILTIN(INDEX_TO_NAME)
691#undef INDEX_TO_NAME
692 default: return SCM_BOOL_F;
693 }
694}
695#undef FUNC_NAME
696
697static void
698scm_init_vm_builtins (void)
699{
486013d6
AW
700 scm_c_define_gsubr ("builtin-name->index", 1, 0, 0,
701 scm_vm_builtin_name_to_index);
702 scm_c_define_gsubr ("builtin-index->name", 1, 0, 0,
703 scm_vm_builtin_index_to_name);
704}
705
706SCM
707scm_i_call_with_current_continuation (SCM proc)
708{
709 return scm_call_1 (vm_builtin_call_with_current_continuation, proc);
710}
510ca126 711
a98cef7e
KN
712\f
713/*
714 * VM
715 */
716
aab9d46c 717#define VM_MIN_STACK_SIZE (1024)
486013d6 718#define VM_DEFAULT_STACK_SIZE (256 * 1024)
aab9d46c
SIT
719static size_t vm_stack_size = VM_DEFAULT_STACK_SIZE;
720
721static void
722initialize_default_stack_size (void)
723{
724 int size = scm_getenv_int ("GUILE_STACK_SIZE", vm_stack_size);
725 if (size >= VM_MIN_STACK_SIZE)
726 vm_stack_size = size;
727}
17e90c5e 728
f42cfbf0
AW
729#define VM_NAME vm_regular_engine
730#define VM_USE_HOOKS 0
6d14383e 731#define FUNC_NAME "vm-regular-engine"
83495480 732#include "vm-engine.c"
6d14383e 733#undef FUNC_NAME
f42cfbf0
AW
734#undef VM_USE_HOOKS
735#undef VM_NAME
17e90c5e 736
f42cfbf0
AW
737#define VM_NAME vm_debug_engine
738#define VM_USE_HOOKS 1
6d14383e 739#define FUNC_NAME "vm-debug-engine"
83495480 740#include "vm-engine.c"
6d14383e 741#undef FUNC_NAME
f42cfbf0
AW
742#undef VM_USE_HOOKS
743#undef VM_NAME
17e90c5e 744
f42cfbf0 745typedef SCM (*scm_t_vm_engine) (SCM vm, SCM program, SCM *argv, size_t nargs);
73c3db66 746
f42cfbf0
AW
747static const scm_t_vm_engine vm_engines[SCM_VM_NUM_ENGINES] =
748 { vm_regular_engine, vm_debug_engine };
73c3db66 749
e3eb628d
LC
750#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
751
752/* The GC "kind" for the VM stack. */
753static int vm_stack_gc_kind;
754
755#endif
756
a98cef7e 757static SCM
17e90c5e
KN
758make_vm (void)
759#define FUNC_NAME "make_vm"
a98cef7e 760{
17e90c5e 761 int i;
7f991c7d 762 struct scm_vm *vp;
747a1635 763
7f991c7d 764 vp = scm_gc_malloc (sizeof (struct scm_vm), "vm");
d8eeb67c 765
aab9d46c 766 vp->stack_size= vm_stack_size;
e3eb628d
LC
767
768#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
4168aa46
TTN
769 vp->stack_base = (SCM *)
770 GC_generic_malloc (vp->stack_size * sizeof (SCM), vm_stack_gc_kind);
e3eb628d
LC
771
772 /* Keep a pointer to VP so that `vm_stack_mark ()' can know what the stack
773 top is. */
21041372 774 *vp->stack_base = SCM_PACK_POINTER (vp);
e3eb628d
LC
775 vp->stack_base++;
776 vp->stack_size--;
777#else
d8eeb67c
LC
778 vp->stack_base = scm_gc_malloc (vp->stack_size * sizeof (SCM),
779 "stack-base");
e3eb628d
LC
780#endif
781
f1046e6b 782 vp->stack_limit = vp->stack_base + vp->stack_size - VM_STACK_RESERVE_SIZE;
3616e9e9
KN
783 vp->ip = NULL;
784 vp->sp = vp->stack_base - 1;
785 vp->fp = NULL;
ea9f4f4b 786 vp->engine = vm_default_engine;
7656f194 787 vp->trace_level = 0;
17e90c5e 788 for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
3d5ee0cd 789 vp->hooks[i] = SCM_BOOL_F;
6f3b0cc2 790 return scm_cell (scm_tc7_vm, (scm_t_bits)vp);
a98cef7e 791}
17e90c5e 792#undef FUNC_NAME
a98cef7e 793
e3eb628d
LC
794#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
795
796/* Mark the VM stack region between its base and its current top. */
797static struct GC_ms_entry *
798vm_stack_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
799 struct GC_ms_entry *mark_stack_limit, GC_word env)
800{
801 GC_word *word;
802 const struct scm_vm *vm;
803
804 /* The first word of the VM stack should contain a pointer to the
805 corresponding VM. */
806 vm = * ((struct scm_vm **) addr);
807
8071c490 808 if (vm == NULL
f1046e6b 809 || (SCM *) addr != vm->stack_base - 1)
e3eb628d
LC
810 /* ADDR must be a pointer to a free-list element, which we must ignore
811 (see warning in <gc/gc_mark.h>). */
812 return mark_stack_ptr;
813
e3eb628d
LC
814 for (word = (GC_word *) vm->stack_base; word <= (GC_word *) vm->sp; word++)
815 mark_stack_ptr = GC_MARK_AND_PUSH ((* (GC_word **) word),
816 mark_stack_ptr, mark_stack_limit,
817 NULL);
818
819 return mark_stack_ptr;
820}
821
822#endif /* VM_ENABLE_PRECISE_STACK_GC_SCAN */
823
824
6d14383e 825SCM
4abef68f 826scm_c_vm_run (SCM vm, SCM program, SCM *argv, int nargs)
6d14383e 827{
4abef68f 828 struct scm_vm *vp = SCM_VM_DATA (vm);
b95d76fc 829 SCM_CHECK_STACK;
f42cfbf0 830 return vm_engines[vp->engine](vm, program, argv, nargs);
6d14383e
AW
831}
832
a222cbc9
AW
833SCM
834scm_the_vm (void)
271c3d31 835{
ea9f4f4b
AW
836 scm_i_thread *t = SCM_I_CURRENT_THREAD;
837
838 if (SCM_UNLIKELY (scm_is_false (t->vm)))
839 t->vm = make_vm ();
840
841 return t->vm;
271c3d31 842}
499a4c07 843
a222cbc9 844/* Scheme interface */
a98cef7e 845
17e90c5e
KN
846#define VM_DEFINE_HOOK(n) \
847{ \
3d5ee0cd 848 struct scm_vm *vp; \
972275ee 849 vp = SCM_VM_DATA (scm_the_vm ()); \
8b22ed7a 850 if (scm_is_false (vp->hooks[n])) \
238e7a11 851 vp->hooks[n] = scm_make_hook (SCM_I_MAKINUM (1)); \
3d5ee0cd 852 return vp->hooks[n]; \
17e90c5e
KN
853}
854
972275ee
AW
855SCM_DEFINE (scm_vm_apply_hook, "vm-apply-hook", 0, 0, 0,
856 (void),
17e90c5e 857 "")
c45d4d77 858#define FUNC_NAME s_scm_vm_apply_hook
a98cef7e 859{
c45d4d77 860 VM_DEFINE_HOOK (SCM_VM_APPLY_HOOK);
a98cef7e
KN
861}
862#undef FUNC_NAME
863
972275ee
AW
864SCM_DEFINE (scm_vm_push_continuation_hook, "vm-push-continuation-hook", 0, 0, 0,
865 (void),
17e90c5e 866 "")
c45d4d77 867#define FUNC_NAME s_scm_vm_push_continuation_hook
a98cef7e 868{
c45d4d77 869 VM_DEFINE_HOOK (SCM_VM_PUSH_CONTINUATION_HOOK);
a98cef7e
KN
870}
871#undef FUNC_NAME
872
972275ee
AW
873SCM_DEFINE (scm_vm_pop_continuation_hook, "vm-pop-continuation-hook", 0, 0, 0,
874 (void),
17e90c5e 875 "")
c45d4d77 876#define FUNC_NAME s_scm_vm_pop_continuation_hook
a98cef7e 877{
c45d4d77 878 VM_DEFINE_HOOK (SCM_VM_POP_CONTINUATION_HOOK);
a98cef7e
KN
879}
880#undef FUNC_NAME
881
972275ee
AW
882SCM_DEFINE (scm_vm_next_hook, "vm-next-hook", 0, 0, 0,
883 (void),
17e90c5e 884 "")
c45d4d77 885#define FUNC_NAME s_scm_vm_next_hook
a98cef7e 886{
c45d4d77 887 VM_DEFINE_HOOK (SCM_VM_NEXT_HOOK);
a98cef7e
KN
888}
889#undef FUNC_NAME
f3120251 890
972275ee
AW
891SCM_DEFINE (scm_vm_abort_continuation_hook, "vm-abort-continuation-hook", 0, 0, 0,
892 (void),
f3120251
AW
893 "")
894#define FUNC_NAME s_scm_vm_abort_continuation_hook
895{
896 VM_DEFINE_HOOK (SCM_VM_ABORT_CONTINUATION_HOOK);
897}
898#undef FUNC_NAME
899
972275ee
AW
900SCM_DEFINE (scm_vm_restore_continuation_hook, "vm-restore-continuation-hook", 0, 0, 0,
901 (void),
f3120251
AW
902 "")
903#define FUNC_NAME s_scm_vm_restore_continuation_hook
904{
905 VM_DEFINE_HOOK (SCM_VM_RESTORE_CONTINUATION_HOOK);
906}
907#undef FUNC_NAME
a98cef7e 908
972275ee
AW
909SCM_DEFINE (scm_vm_trace_level, "vm-trace-level", 0, 0, 0,
910 (void),
17e90c5e 911 "")
7656f194 912#define FUNC_NAME s_scm_vm_trace_level
a98cef7e 913{
972275ee 914 return scm_from_int (SCM_VM_DATA (scm_the_vm ())->trace_level);
7656f194
AW
915}
916#undef FUNC_NAME
917
972275ee
AW
918SCM_DEFINE (scm_set_vm_trace_level_x, "set-vm-trace-level!", 1, 0, 0,
919 (SCM level),
7656f194
AW
920 "")
921#define FUNC_NAME s_scm_set_vm_trace_level_x
922{
972275ee 923 SCM_VM_DATA (scm_the_vm ())->trace_level = scm_to_int (level);
7656f194 924 return SCM_UNSPECIFIED;
a98cef7e
KN
925}
926#undef FUNC_NAME
927
928\f
ea9f4f4b
AW
929/*
930 * VM engines
931 */
932
933static int
934symbol_to_vm_engine (SCM engine, const char *FUNC_NAME)
935{
936 if (scm_is_eq (engine, sym_regular))
937 return SCM_VM_REGULAR_ENGINE;
938 else if (scm_is_eq (engine, sym_debug))
939 return SCM_VM_DEBUG_ENGINE;
940 else
941 SCM_MISC_ERROR ("Unknown VM engine: ~a", scm_list_1 (engine));
942}
943
944static SCM
945vm_engine_to_symbol (int engine, const char *FUNC_NAME)
946{
947 switch (engine)
948 {
949 case SCM_VM_REGULAR_ENGINE:
950 return sym_regular;
951 case SCM_VM_DEBUG_ENGINE:
952 return sym_debug;
953 default:
954 /* ? */
955 SCM_MISC_ERROR ("Unknown VM engine: ~a",
956 scm_list_1 (scm_from_int (engine)));
957 }
958}
959
972275ee
AW
960SCM_DEFINE (scm_vm_engine, "vm-engine", 0, 0, 0,
961 (void),
ea9f4f4b
AW
962 "")
963#define FUNC_NAME s_scm_vm_engine
964{
972275ee 965 return vm_engine_to_symbol (SCM_VM_DATA (scm_the_vm ())->engine, FUNC_NAME);
ea9f4f4b
AW
966}
967#undef FUNC_NAME
968
969void
972275ee 970scm_c_set_vm_engine_x (int engine)
ea9f4f4b
AW
971#define FUNC_NAME "set-vm-engine!"
972{
ea9f4f4b
AW
973 if (engine < 0 || engine >= SCM_VM_NUM_ENGINES)
974 SCM_MISC_ERROR ("Unknown VM engine: ~a",
975 scm_list_1 (scm_from_int (engine)));
976
972275ee 977 SCM_VM_DATA (scm_the_vm ())->engine = engine;
ea9f4f4b
AW
978}
979#undef FUNC_NAME
980
972275ee
AW
981SCM_DEFINE (scm_set_vm_engine_x, "set-vm-engine!", 1, 0, 0,
982 (SCM engine),
ea9f4f4b
AW
983 "")
984#define FUNC_NAME s_scm_set_vm_engine_x
985{
972275ee 986 scm_c_set_vm_engine_x (symbol_to_vm_engine (engine, FUNC_NAME));
ea9f4f4b
AW
987 return SCM_UNSPECIFIED;
988}
989#undef FUNC_NAME
990
991void
992scm_c_set_default_vm_engine_x (int engine)
993#define FUNC_NAME "set-default-vm-engine!"
994{
995 if (engine < 0 || engine >= SCM_VM_NUM_ENGINES)
996 SCM_MISC_ERROR ("Unknown VM engine: ~a",
997 scm_list_1 (scm_from_int (engine)));
998
999 vm_default_engine = engine;
1000}
1001#undef FUNC_NAME
1002
1003SCM_DEFINE (scm_set_default_vm_engine_x, "set-default-vm-engine!", 1, 0, 0,
1004 (SCM engine),
1005 "")
1006#define FUNC_NAME s_scm_set_default_vm_engine_x
1007{
1008 scm_c_set_default_vm_engine_x (symbol_to_vm_engine (engine, FUNC_NAME));
1009 return SCM_UNSPECIFIED;
1010}
1011#undef FUNC_NAME
1012
972275ee
AW
1013/* FIXME: This function makes no sense, but we keep it to make sure we
1014 have a way of switching to the debug or regular VM. */
1015SCM_DEFINE (scm_call_with_vm, "call-with-vm", 1, 0, 1,
1016 (SCM proc, SCM args),
ea9f4f4b 1017 "Apply @var{proc} to @var{args} in a dynamic extent in which\n"
972275ee 1018 "@var{vm} is the current VM.")
ea9f4f4b
AW
1019#define FUNC_NAME s_scm_call_with_vm
1020{
972275ee 1021 return scm_apply_0 (proc, args);
ea9f4f4b
AW
1022}
1023#undef FUNC_NAME
1024
1025\f
a98cef7e 1026/*
17e90c5e 1027 * Initialize
a98cef7e
KN
1028 */
1029
07e56b27
AW
1030SCM scm_load_compiled_with_vm (SCM file)
1031{
b8bc86bc
AW
1032 SCM program = scm_load_thunk_from_file (file);
1033
4abef68f 1034 return scm_c_vm_run (scm_the_vm (), program, NULL, 0);
07e56b27
AW
1035}
1036
67b699cc 1037
9f309e2c
AW
1038void
1039scm_init_vm_builtin_properties (void)
1040{
1041 /* FIXME: Seems hacky to do this here, but oh well :/ */
1042 scm_sym_apply = scm_from_utf8_symbol ("apply");
1043 scm_sym_values = scm_from_utf8_symbol ("values");
1044 scm_sym_abort_to_prompt = scm_from_utf8_symbol ("abort-to-prompt");
1045 scm_sym_call_with_values = scm_from_utf8_symbol ("call-with-values");
1046 scm_sym_call_with_current_continuation =
1047 scm_from_utf8_symbol ("call-with-current-continuation");
1048
1049#define INIT_BUILTIN(builtin, BUILTIN, req, opt, rest) \
1050 scm_set_procedure_property_x (vm_builtin_##builtin, scm_sym_name, \
1051 scm_sym_##builtin); \
1052 scm_set_procedure_minimum_arity_x (vm_builtin_##builtin, \
1053 SCM_I_MAKINUM (req), \
1054 SCM_I_MAKINUM (opt), \
1055 scm_from_bool (rest));
1056 FOR_EACH_VM_BUILTIN (INIT_BUILTIN);
1057#undef INIT_BUILTIN
1058}
1059
17e90c5e 1060void
07e56b27 1061scm_bootstrap_vm (void)
17e90c5e 1062{
44602b08
AW
1063 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
1064 "scm_init_vm",
60ae5ca2 1065 (scm_t_extension_init_func)scm_init_vm, NULL);
486013d6
AW
1066 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
1067 "scm_init_vm_builtins",
1068 (scm_t_extension_init_func)scm_init_vm_builtins,
1069 NULL);
60ae5ca2 1070
aab9d46c
SIT
1071 initialize_default_stack_size ();
1072
4a655e50
AW
1073 sym_vm_run = scm_from_latin1_symbol ("vm-run");
1074 sym_vm_error = scm_from_latin1_symbol ("vm-error");
1075 sym_keyword_argument_error = scm_from_latin1_symbol ("keyword-argument-error");
1076 sym_regular = scm_from_latin1_symbol ("regular");
1077 sym_debug = scm_from_latin1_symbol ("debug");
0404c97d 1078
ef6b7f71
AW
1079 vm_boot_continuation = scm_i_make_program (vm_boot_continuation_code);
1080 SCM_SET_CELL_WORD_0 (vm_boot_continuation,
1081 (SCM_CELL_WORD_0 (vm_boot_continuation)
73c3db66 1082 | SCM_F_PROGRAM_IS_BOOT));
9f309e2c
AW
1083
1084#define DEFINE_BUILTIN(builtin, BUILTIN, req, opt, rest) \
80797145 1085 vm_builtin_##builtin = scm_i_make_program (vm_builtin_##builtin##_code);
9f309e2c
AW
1086 FOR_EACH_VM_BUILTIN (DEFINE_BUILTIN);
1087#undef DEFINE_BUILTIN
73c3db66 1088
e3eb628d
LC
1089#ifdef VM_ENABLE_PRECISE_STACK_GC_SCAN
1090 vm_stack_gc_kind =
1091 GC_new_kind (GC_new_free_list (),
1092 GC_MAKE_PROC (GC_new_proc (vm_stack_mark), 0),
1093 0, 1);
1094
1095#endif
07e56b27
AW
1096}
1097
1098void
1099scm_init_vm (void)
1100{
17e90c5e 1101#ifndef SCM_MAGIC_SNARFER
aeeff258 1102#include "libguile/vm.x"
17e90c5e 1103#endif
a98cef7e 1104}
17e90c5e
KN
1105
1106/*
1107 Local Variables:
1108 c-file-style: "gnu"
1109 End:
1110*/