1/* A simple stack-based virtual machine to demonstrate
2   JIT-compilation.
3   Copyright (C) 2014-2015 Free Software Foundation, Inc.
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;
36typedef struct toyvm_compiled_function toyvm_compiled_function;
37
38/* Functions are compiled to this function ptr type.  */
39typedef int (*toyvm_compiled_code) (int);
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
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
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    }
171  fn->fn_filename = filename;
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);
230  if (f)
231    fclose (f);
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
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
472/* The main compilation hook.  */
473
474static toyvm_compiled_function *
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
483  funcname = get_function_name (fn->fn_filename);
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.  */
751  gcc_jit_result *jit_result = gcc_jit_context_compile (state.ctxt);
752  gcc_jit_context_release (state.ctxt);
753
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;
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;
809  toyvm_compiled_function *compiled_fn;
810  toyvm_compiled_code code;
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
826  compiled_fn = toyvm_function_compile (fn);
827  CHECK_NON_NULL (compiled_fn);
828
829  code = (toyvm_compiled_code)compiled_fn->cf_code;
830  CHECK_NON_NULL (code);
831
832  compiled_result = code (input);
833  CHECK_VALUE (compiled_result, expected_result);
834
835  gcc_jit_result_release (compiled_fn->cf_jit_result);
836  free (compiled_fn);
837  free (fn);
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.  */
901  toyvm_compiled_function *compiled_fn
902    = toyvm_function_compile (fn);
903
904  toyvm_compiled_code code = compiled_fn->cf_code;
905  printf ("compiler result: %d\n",
906	  code (atoi (argv[2])));
907
908  gcc_jit_result_release (compiled_fn->cf_jit_result);
909  free (compiled_fn);
910
911 return 0;
912}
913