]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/jit/docs/examples/tut04-toyvm/toyvm.c
Update copyright years.
[thirdparty/gcc.git] / gcc / jit / docs / examples / tut04-toyvm / toyvm.c
CommitLineData
35485da9
DM
1/* A simple stack-based virtual machine to demonstrate
2 JIT-compilation.
8d9254fc 3 Copyright (C) 2014-2020 Free Software Foundation, Inc.
35485da9
DM
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include <assert.h>
22#include <errno.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <dejagnu.h>
28
29#include <libgccjit.h>
30
31/* Typedefs. */
32typedef struct toyvm_op toyvm_op;
33typedef struct toyvm_function toyvm_function;
34typedef struct toyvm_frame toyvm_frame;
35typedef struct compilation_state compilation_state;
52b9468f 36typedef struct toyvm_compiled_function toyvm_compiled_function;
35485da9
DM
37
38/* Functions are compiled to this function ptr type. */
52b9468f 39typedef int (*toyvm_compiled_code) (int);
35485da9
DM
40
41enum opcode {
42 /* Ops taking no operand. */
43 DUP,
44 ROT,
45 BINARY_ADD,
46 BINARY_SUBTRACT,
47 BINARY_MULT,
48 BINARY_COMPARE_LT,
49 RECURSE,
50 RETURN,
51
52 /* Ops taking an operand. */
53 PUSH_CONST,
54 JUMP_ABS_IF_TRUE
55};
56
57#define FIRST_UNARY_OPCODE (PUSH_CONST)
58
59const char * const opcode_names[] = {
60 "DUP",
61 "ROT",
62 "BINARY_ADD",
63 "BINARY_SUBTRACT",
64 "BINARY_MULT",
65 "BINARY_COMPARE_LT",
66 "RECURSE",
67 "RETURN",
68
69 "PUSH_CONST",
70 "JUMP_ABS_IF_TRUE",
71};
72
73struct toyvm_op
74{
75 /* Which operation. */
76 enum opcode op_opcode;
77
78 /* Some opcodes take an argument. */
79 int op_operand;
80
81 /* The line number of the operation within the source file. */
82 int op_linenum;
83};
84
85#define MAX_OPS (64)
86
87struct toyvm_function
88{
89 const char *fn_filename;
90 int fn_num_ops;
91 toyvm_op fn_ops[MAX_OPS];
92};
93
94#define MAX_STACK_DEPTH (8)
95
96struct toyvm_frame
97{
98 toyvm_function *frm_function;
99 int frm_pc;
100 int frm_stack[MAX_STACK_DEPTH];
101 int frm_cur_depth;
102};
103
104static void
105add_op (toyvm_function *fn, enum opcode opcode,
106 int operand, int linenum)
107{
108 toyvm_op *op;
109 assert (fn->fn_num_ops < MAX_OPS);
110 op = &fn->fn_ops[fn->fn_num_ops++];
111 op->op_opcode = opcode;
112 op->op_operand = operand;
113 op->op_linenum = linenum;
114}
115
116static void
117add_unary_op (toyvm_function *fn, enum opcode opcode,
118 const char *rest_of_line, int linenum)
119{
120 int operand = atoi (rest_of_line);
121 add_op (fn, opcode, operand, linenum);
122}
123
5cd614ce
DM
124static char *
125get_function_name (const char *filename)
126{
127 /* Skip any path separators. */
128 const char *pathsep = strrchr (filename, '/');
129 if (pathsep)
130 filename = pathsep + 1;
131
132 /* Copy filename to funcname. */
133 char *funcname = (char *)malloc (strlen (filename) + 1);
134
135 strcpy (funcname, filename);
136
137 /* Convert "." to NIL terminator. */
138 *(strchr (funcname, '.')) = '\0';
139
140 return funcname;
141}
142
35485da9
DM
143static toyvm_function *
144toyvm_function_parse (const char *filename, const char *name)
145{
146 FILE *f = NULL;
147 toyvm_function *fn = NULL;
148 char *line = NULL;
149 ssize_t linelen;
150 size_t bufsize;
151 int linenum = 0;
152
153 assert (filename);
154 assert (name);
155
156 f = fopen (filename, "r");
157 if (!f)
158 {
159 fprintf (stderr,
160 "cannot open file %s: %s\n",
161 filename, strerror (errno));
162 goto error;
163 }
164
165 fn = (toyvm_function *)calloc (1, sizeof (toyvm_function));
166 if (!fn)
167 {
168 fprintf (stderr, "out of memory allocating toyvm_function\n");
169 goto error;
170 }
5cd614ce 171 fn->fn_filename = filename;
35485da9
DM
172
173 /* Read the lines of the file. */
174 while ((linelen = getline (&line, &bufsize, f)) != -1)
175 {
176 /* Note that this is a terrible parser, but it avoids the need to
177 bring in lex/yacc as a dependency. */
178 linenum++;
179
180 if (0)
181 fprintf (stdout, "%3d: %s", linenum, line);
182
183 /* Lines beginning with # are comments. */
184 if (line[0] == '#')
185 continue;
186
187 /* Skip blank lines. */
188 if (line[0] == '\n')
189 continue;
190
191#define LINE_MATCHES(OPCODE) (0 == strncmp ((OPCODE), line, strlen (OPCODE)))
192 if (LINE_MATCHES ("DUP\n"))
193 add_op (fn, DUP, 0, linenum);
194 else if (LINE_MATCHES ("ROT\n"))
195 add_op (fn, ROT, 0, linenum);
196 else if (LINE_MATCHES ("BINARY_ADD\n"))
197 add_op (fn, BINARY_ADD, 0, linenum);
198 else if (LINE_MATCHES ("BINARY_SUBTRACT\n"))
199 add_op (fn, BINARY_SUBTRACT, 0, linenum);
200 else if (LINE_MATCHES ("BINARY_MULT\n"))
201 add_op (fn, BINARY_MULT, 0, linenum);
202 else if (LINE_MATCHES ("BINARY_COMPARE_LT\n"))
203 add_op (fn, BINARY_COMPARE_LT, 0, linenum);
204 else if (LINE_MATCHES ("RECURSE\n"))
205 add_op (fn, RECURSE, 0, linenum);
206 else if (LINE_MATCHES ("RETURN\n"))
207 add_op (fn, RETURN, 0, linenum);
208 else if (LINE_MATCHES ("PUSH_CONST "))
209 add_unary_op (fn, PUSH_CONST,
210 line + strlen ("PUSH_CONST "), linenum);
211 else if (LINE_MATCHES ("JUMP_ABS_IF_TRUE "))
212 add_unary_op (fn, JUMP_ABS_IF_TRUE,
213 line + strlen("JUMP_ABS_IF_TRUE "), linenum);
214 else
215 {
216 fprintf (stderr, "%s:%d: parse error\n", filename, linenum);
217 free (fn);
218 fn = NULL;
219 goto error;
220 }
221#undef LINE_MATCHES
222 }
223 free (line);
224 fclose (f);
225
226 return fn;
227
228 error:
229 free (line);
5cd614ce
DM
230 if (f)
231 fclose (f);
35485da9
DM
232 free (fn);
233 return NULL;
234}
235
236static void
237toyvm_function_disassemble_op (toyvm_function *fn, toyvm_op *op, int index, FILE *out)
238{
239 fprintf (out, "%s:%d: index %d: %s",
240 fn->fn_filename, op->op_linenum, index,
241 opcode_names[op->op_opcode]);
242 if (op->op_opcode >= FIRST_UNARY_OPCODE)
243 fprintf (out, " %d", op->op_operand);
244 fprintf (out, "\n");
245}
246
247static void
248toyvm_function_disassemble (toyvm_function *fn, FILE *out)
249{
250 int i;
251 for (i = 0; i < fn->fn_num_ops; i++)
252 {
253 toyvm_op *op = &fn->fn_ops[i];
254 toyvm_function_disassemble_op (fn, op, i, out);
255 }
256}
257
258static void
259toyvm_frame_push (toyvm_frame *frame, int arg)
260{
261 assert (frame->frm_cur_depth < MAX_STACK_DEPTH);
262 frame->frm_stack[frame->frm_cur_depth++] = arg;
263}
264
265static int
266toyvm_frame_pop (toyvm_frame *frame)
267{
268 assert (frame->frm_cur_depth > 0);
269 return frame->frm_stack[--frame->frm_cur_depth];
270}
271
272static void
273toyvm_frame_dump_stack (toyvm_frame *frame, FILE *out)
274{
275 int i;
276 fprintf (out, "stack:");
277 for (i = 0; i < frame->frm_cur_depth; i++)
278 {
279 fprintf (out, " %d", frame->frm_stack[i]);
280 }
281 fprintf (out, "\n");
282}
283
284/* Execute the given function. */
285
286static int
287toyvm_function_interpret (toyvm_function *fn, int arg, FILE *trace)
288{
289 toyvm_frame frame;
290#define PUSH(ARG) (toyvm_frame_push (&frame, (ARG)))
291#define POP(ARG) (toyvm_frame_pop (&frame))
292
293 frame.frm_function = fn;
294 frame.frm_pc = 0;
295 frame.frm_cur_depth = 0;
296
297 PUSH (arg);
298
299 while (1)
300 {
301 toyvm_op *op;
302 int x, y;
303 assert (frame.frm_pc < fn->fn_num_ops);
304 op = &fn->fn_ops[frame.frm_pc++];
305
306 if (trace)
307 {
308 toyvm_frame_dump_stack (&frame, trace);
309 toyvm_function_disassemble_op (fn, op, frame.frm_pc, trace);
310 }
311
312 switch (op->op_opcode)
313 {
314 /* Ops taking no operand. */
315 case DUP:
316 x = POP ();
317 PUSH (x);
318 PUSH (x);
319 break;
320
321 case ROT:
322 y = POP ();
323 x = POP ();
324 PUSH (y);
325 PUSH (x);
326 break;
327
328 case BINARY_ADD:
329 y = POP ();
330 x = POP ();
331 PUSH (x + y);
332 break;
333
334 case BINARY_SUBTRACT:
335 y = POP ();
336 x = POP ();
337 PUSH (x - y);
338 break;
339
340 case BINARY_MULT:
341 y = POP ();
342 x = POP ();
343 PUSH (x * y);
344 break;
345
346 case BINARY_COMPARE_LT:
347 y = POP ();
348 x = POP ();
349 PUSH (x < y);
350 break;
351
352 case RECURSE:
353 x = POP ();
354 x = toyvm_function_interpret (fn, x, trace);
355 PUSH (x);
356 break;
357
358 case RETURN:
359 return POP ();
360
361 /* Ops taking an operand. */
362 case PUSH_CONST:
363 PUSH (op->op_operand);
364 break;
365
366 case JUMP_ABS_IF_TRUE:
367 x = POP ();
368 if (x)
369 frame.frm_pc = op->op_operand;
370 break;
371
372 default:
373 assert (0); /* unknown opcode */
374
375 } /* end of switch on opcode */
376 } /* end of while loop */
377
378#undef PUSH
379#undef POP
380}
381
382/* JIT compilation. */
383
384struct compilation_state
385{
386 gcc_jit_context *ctxt;
387
388 gcc_jit_type *int_type;
389 gcc_jit_type *bool_type;
390 gcc_jit_type *stack_type; /* int[MAX_STACK_DEPTH] */
391
392 gcc_jit_rvalue *const_one;
393
394 gcc_jit_function *fn;
395 gcc_jit_param *param_arg;
396 gcc_jit_lvalue *stack;
397 gcc_jit_lvalue *stack_depth;
398 gcc_jit_lvalue *x;
399 gcc_jit_lvalue *y;
400
401 gcc_jit_location *op_locs[MAX_OPS];
402 gcc_jit_block *initial_block;
403 gcc_jit_block *op_blocks[MAX_OPS];
404
405};
406
407/* Stack manipulation. */
408
409static void
410add_push (compilation_state *state,
411 gcc_jit_block *block,
412 gcc_jit_rvalue *rvalue,
413 gcc_jit_location *loc)
414{
415 /* stack[stack_depth] = RVALUE */
416 gcc_jit_block_add_assignment (
417 block,
418 loc,
419 /* stack[stack_depth] */
420 gcc_jit_context_new_array_access (
421 state->ctxt,
422 loc,
423 gcc_jit_lvalue_as_rvalue (state->stack),
424 gcc_jit_lvalue_as_rvalue (state->stack_depth)),
425 rvalue);
426
427 /* "stack_depth++;". */
428 gcc_jit_block_add_assignment_op (
429 block,
430 loc,
431 state->stack_depth,
432 GCC_JIT_BINARY_OP_PLUS,
433 state->const_one);
434}
435
436static void
437add_pop (compilation_state *state,
438 gcc_jit_block *block,
439 gcc_jit_lvalue *lvalue,
440 gcc_jit_location *loc)
441{
442 /* "--stack_depth;". */
443 gcc_jit_block_add_assignment_op (
444 block,
445 loc,
446 state->stack_depth,
447 GCC_JIT_BINARY_OP_MINUS,
448 state->const_one);
449
450 /* "LVALUE = stack[stack_depth];". */
451 gcc_jit_block_add_assignment (
452 block,
453 loc,
454 lvalue,
455 /* stack[stack_depth] */
456 gcc_jit_lvalue_as_rvalue (
457 gcc_jit_context_new_array_access (
458 state->ctxt,
459 loc,
460 gcc_jit_lvalue_as_rvalue (state->stack),
461 gcc_jit_lvalue_as_rvalue (state->stack_depth))));
462}
463
52b9468f
DM
464/* A struct to hold the compilation results. */
465
466struct toyvm_compiled_function
467{
468 gcc_jit_result *cf_jit_result;
469 toyvm_compiled_code cf_code;
470};
471
35485da9
DM
472/* The main compilation hook. */
473
52b9468f 474static toyvm_compiled_function *
35485da9
DM
475toyvm_function_compile (toyvm_function *fn)
476{
477 compilation_state state;
478 int pc;
479 char *funcname;
480
481 memset (&state, 0, sizeof (state));
482
5cd614ce 483 funcname = get_function_name (fn->fn_filename);
35485da9
DM
484
485 state.ctxt = gcc_jit_context_acquire ();
486
487 gcc_jit_context_set_bool_option (state.ctxt,
488 GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
489 0);
490 gcc_jit_context_set_bool_option (state.ctxt,
491 GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
492 0);
493 gcc_jit_context_set_int_option (state.ctxt,
494 GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
495 3);
496 gcc_jit_context_set_bool_option (state.ctxt,
497 GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
498 0);
499 gcc_jit_context_set_bool_option (state.ctxt,
500 GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
501 0);
502 gcc_jit_context_set_bool_option (state.ctxt,
503 GCC_JIT_BOOL_OPTION_DEBUGINFO,
504 1);
505
506 /* Create types. */
507 state.int_type =
508 gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_INT);
509 state.bool_type =
510 gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_BOOL);
511 state.stack_type =
512 gcc_jit_context_new_array_type (state.ctxt, NULL,
513 state.int_type, MAX_STACK_DEPTH);
514
515 /* The constant value 1. */
516 state.const_one = gcc_jit_context_one (state.ctxt, state.int_type);
517
518 /* Create locations. */
519 for (pc = 0; pc < fn->fn_num_ops; pc++)
520 {
521 toyvm_op *op = &fn->fn_ops[pc];
522
523 state.op_locs[pc] = gcc_jit_context_new_location (state.ctxt,
524 fn->fn_filename,
525 op->op_linenum,
526 0); /* column */
527 }
528
529 /* Creating the function. */
530 state.param_arg =
531 gcc_jit_context_new_param (state.ctxt, state.op_locs[0],
532 state.int_type, "arg");
533 state.fn =
534 gcc_jit_context_new_function (state.ctxt,
535 state.op_locs[0],
536 GCC_JIT_FUNCTION_EXPORTED,
537 state.int_type,
538 funcname,
539 1, &state.param_arg, 0);
540
541 /* Create stack lvalues. */
542 state.stack =
543 gcc_jit_function_new_local (state.fn, NULL,
544 state.stack_type, "stack");
545 state.stack_depth =
546 gcc_jit_function_new_local (state.fn, NULL,
547 state.int_type, "stack_depth");
548 state.x =
549 gcc_jit_function_new_local (state.fn, NULL,
550 state.int_type, "x");
551 state.y =
552 gcc_jit_function_new_local (state.fn, NULL,
553 state.int_type, "y");
554
555 /* 1st pass: create blocks, one per opcode. */
556
557 /* We need an entry block to do one-time initialization, so create that
558 first. */
559 state.initial_block = gcc_jit_function_new_block (state.fn, "initial");
560
561 /* Create a block per operation. */
562 for (pc = 0; pc < fn->fn_num_ops; pc++)
563 {
564 char buf[16];
565 sprintf (buf, "instr%i", pc);
566 state.op_blocks[pc] = gcc_jit_function_new_block (state.fn, buf);
567 }
568
569 /* Populate the initial block. */
570
571 /* "stack_depth = 0;". */
572 gcc_jit_block_add_assignment (
573 state.initial_block,
574 state.op_locs[0],
575 state.stack_depth,
576 gcc_jit_context_zero (state.ctxt, state.int_type));
577
578 /* "PUSH (arg);". */
579 add_push (&state,
580 state.initial_block,
581 gcc_jit_param_as_rvalue (state.param_arg),
582 state.op_locs[0]);
583
584 /* ...and jump to insn 0. */
585 gcc_jit_block_end_with_jump (state.initial_block,
586 state.op_locs[0],
587 state.op_blocks[0]);
588
589 /* 2nd pass: fill in instructions. */
590 for (pc = 0; pc < fn->fn_num_ops; pc++)
591 {
592 gcc_jit_location *loc = state.op_locs[pc];
593
594 gcc_jit_block *block = state.op_blocks[pc];
595 gcc_jit_block *next_block = (pc < fn->fn_num_ops
596 ? state.op_blocks[pc + 1]
597 : NULL);
598
599 toyvm_op *op;
600 op = &fn->fn_ops[pc];
601
602 /* Helper macros. */
603
604#define X_EQUALS_POP()\
605 add_pop (&state, block, state.x, loc)
606#define Y_EQUALS_POP()\
607 add_pop (&state, block, state.y, loc)
608#define PUSH_RVALUE(RVALUE)\
609 add_push (&state, block, (RVALUE), loc)
610#define PUSH_X()\
611 PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.x))
612#define PUSH_Y() \
613 PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.y))
614
615 gcc_jit_block_add_comment (block, loc, opcode_names[op->op_opcode]);
616
617 /* Handle the individual opcodes. */
618
619 switch (op->op_opcode)
620 {
621 case DUP:
622 X_EQUALS_POP ();
623 PUSH_X ();
624 PUSH_X ();
625 break;
626
627 case ROT:
628 Y_EQUALS_POP ();
629 X_EQUALS_POP ();
630 PUSH_Y ();
631 PUSH_X ();
632 break;
633
634 case BINARY_ADD:
635 Y_EQUALS_POP ();
636 X_EQUALS_POP ();
637 PUSH_RVALUE (
638 gcc_jit_context_new_binary_op (
639 state.ctxt,
640 loc,
641 GCC_JIT_BINARY_OP_PLUS,
642 state.int_type,
643 gcc_jit_lvalue_as_rvalue (state.x),
644 gcc_jit_lvalue_as_rvalue (state.y)));
645 break;
646
647 case BINARY_SUBTRACT:
648 Y_EQUALS_POP ();
649 X_EQUALS_POP ();
650 PUSH_RVALUE (
651 gcc_jit_context_new_binary_op (
652 state.ctxt,
653 loc,
654 GCC_JIT_BINARY_OP_MINUS,
655 state.int_type,
656 gcc_jit_lvalue_as_rvalue (state.x),
657 gcc_jit_lvalue_as_rvalue (state.y)));
658 break;
659
660 case BINARY_MULT:
661 Y_EQUALS_POP ();
662 X_EQUALS_POP ();
663 PUSH_RVALUE (
664 gcc_jit_context_new_binary_op (
665 state.ctxt,
666 loc,
667 GCC_JIT_BINARY_OP_MULT,
668 state.int_type,
669 gcc_jit_lvalue_as_rvalue (state.x),
670 gcc_jit_lvalue_as_rvalue (state.y)));
671 break;
672
673 case BINARY_COMPARE_LT:
674 Y_EQUALS_POP ();
675 X_EQUALS_POP ();
676 PUSH_RVALUE (
677 /* cast of bool to int */
678 gcc_jit_context_new_cast (
679 state.ctxt,
680 loc,
681 /* (x < y) as a bool */
682 gcc_jit_context_new_comparison (
683 state.ctxt,
684 loc,
685 GCC_JIT_COMPARISON_LT,
686 gcc_jit_lvalue_as_rvalue (state.x),
687 gcc_jit_lvalue_as_rvalue (state.y)),
688 state.int_type));
689 break;
690
691 case RECURSE:
692 {
693 X_EQUALS_POP ();
694 gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue (state.x);
695 PUSH_RVALUE (
696 gcc_jit_context_new_call (
697 state.ctxt,
698 loc,
699 state.fn,
700 1, &arg));
701 break;
702 }
703
704 case RETURN:
705 X_EQUALS_POP ();
706 gcc_jit_block_end_with_return (
707 block,
708 loc,
709 gcc_jit_lvalue_as_rvalue (state.x));
710 break;
711
712 /* Ops taking an operand. */
713 case PUSH_CONST:
714 PUSH_RVALUE (
715 gcc_jit_context_new_rvalue_from_int (
716 state.ctxt,
717 state.int_type,
718 op->op_operand));
719 break;
720
721 case JUMP_ABS_IF_TRUE:
722 X_EQUALS_POP ();
723 gcc_jit_block_end_with_conditional (
724 block,
725 loc,
726 /* "(bool)x". */
727 gcc_jit_context_new_cast (
728 state.ctxt,
729 loc,
730 gcc_jit_lvalue_as_rvalue (state.x),
731 state.bool_type),
732 state.op_blocks[op->op_operand], /* on_true */
733 next_block); /* on_false */
734 break;
735
736 default:
737 assert(0);
738 } /* end of switch on opcode */
739
740 /* Go to the next block. */
741 if (op->op_opcode != JUMP_ABS_IF_TRUE
742 && op->op_opcode != RETURN)
743 gcc_jit_block_end_with_jump (
744 block,
745 loc,
746 next_block);
747
748 } /* end of loop on PC locations. */
749
750 /* We've now finished populating the context. Compile it. */
52b9468f 751 gcc_jit_result *jit_result = gcc_jit_context_compile (state.ctxt);
35485da9
DM
752 gcc_jit_context_release (state.ctxt);
753
52b9468f
DM
754 toyvm_compiled_function *toyvm_result =
755 (toyvm_compiled_function *)calloc (1, sizeof (toyvm_compiled_function));
756 if (!toyvm_result)
757 {
758 fprintf (stderr, "out of memory allocating toyvm_compiled_function\n");
759 gcc_jit_result_release (jit_result);
760 return NULL;
761 }
762
763 toyvm_result->cf_jit_result = jit_result;
764 toyvm_result->cf_code =
765 (toyvm_compiled_code)gcc_jit_result_get_code (jit_result,
766 funcname);
767
768 free (funcname);
769
770 return toyvm_result;
35485da9
DM
771}
772
773char test[1024];
774
775#define CHECK_NON_NULL(PTR) \
776 do { \
777 if ((PTR) != NULL) \
778 { \
779 pass ("%s: %s is non-null", test, #PTR); \
780 } \
781 else \
782 { \
783 fail ("%s: %s is NULL", test, #PTR); \
784 abort (); \
785 } \
786 } while (0)
787
788#define CHECK_VALUE(ACTUAL, EXPECTED) \
789 do { \
790 if ((ACTUAL) == (EXPECTED)) \
791 { \
792 pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \
793 } \
794 else \
795 { \
796 fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \
797 fprintf (stderr, "incorrect value\n"); \
798 abort (); \
799 } \
800 } while (0)
801
802static void
803test_script (const char *scripts_dir, const char *script_name, int input,
804 int expected_result)
805{
806 char *script_path;
807 toyvm_function *fn;
808 int interpreted_result;
52b9468f
DM
809 toyvm_compiled_function *compiled_fn;
810 toyvm_compiled_code code;
35485da9
DM
811 int compiled_result;
812
813 snprintf (test, sizeof (test), "toyvm.c: %s", script_name);
814
815 script_path = (char *)malloc (strlen (scripts_dir)
816 + strlen (script_name) + 1);
817 CHECK_NON_NULL (script_path);
818 sprintf (script_path, "%s%s", scripts_dir, script_name);
819
820 fn = toyvm_function_parse (script_path, script_name);
821 CHECK_NON_NULL (fn);
822
823 interpreted_result = toyvm_function_interpret (fn, input, NULL);
824 CHECK_VALUE (interpreted_result, expected_result);
825
52b9468f
DM
826 compiled_fn = toyvm_function_compile (fn);
827 CHECK_NON_NULL (compiled_fn);
828
829 code = (toyvm_compiled_code)compiled_fn->cf_code;
35485da9
DM
830 CHECK_NON_NULL (code);
831
832 compiled_result = code (input);
833 CHECK_VALUE (compiled_result, expected_result);
834
52b9468f
DM
835 gcc_jit_result_release (compiled_fn->cf_jit_result);
836 free (compiled_fn);
837 free (fn);
35485da9
DM
838 free (script_path);
839}
840
841#define PATH_TO_SCRIPTS ("/jit/docs/examples/tut04-toyvm/")
842
843static void
844test_suite (void)
845{
846 const char *srcdir;
847 char *scripts_dir;
848
849 snprintf (test, sizeof (test), "toyvm.c");
850
851 /* We need to locate the test scripts.
852 Rely on "srcdir" being set in the environment. */
853
854 srcdir = getenv ("srcdir");
855 CHECK_NON_NULL (srcdir);
856
857 scripts_dir = (char *)malloc (strlen (srcdir) + strlen(PATH_TO_SCRIPTS)
858 + 1);
859 CHECK_NON_NULL (scripts_dir);
860 sprintf (scripts_dir, "%s%s", srcdir, PATH_TO_SCRIPTS);
861
862 test_script (scripts_dir, "factorial.toy", 10, 3628800);
863 test_script (scripts_dir, "fibonacci.toy", 10, 55);
864
865 free (scripts_dir);
866}
867
868int
869main (int argc, char **argv)
870{
871 const char *filename = NULL;
872 toyvm_function *fn = NULL;
873
874 /* If called with no args, assume we're being run by the test suite. */
875 if (argc < 3)
876 {
877 test_suite ();
878 return 0;
879 }
880
881 if (argc != 3)
882 {
883 fprintf (stdout,
884 "%s FILENAME INPUT: Parse and run a .toy file\n",
885 argv[0]);
886 exit (1);
887 }
888
889 filename = argv[1];
890 fn = toyvm_function_parse (filename, filename);
891 if (!fn)
892 exit (1);
893
894 if (0)
895 toyvm_function_disassemble (fn, stdout);
896
897 printf ("interpreter result: %d\n",
898 toyvm_function_interpret (fn, atoi (argv[2]), NULL));
899
900 /* JIT-compilation. */
52b9468f
DM
901 toyvm_compiled_function *compiled_fn
902 = toyvm_function_compile (fn);
903
904 toyvm_compiled_code code = compiled_fn->cf_code;
35485da9
DM
905 printf ("compiler result: %d\n",
906 code (atoi (argv[2])));
907
52b9468f
DM
908 gcc_jit_result_release (compiled_fn->cf_jit_result);
909 free (compiled_fn);
910
35485da9
DM
911 return 0;
912}