(root)/
gcc-13.2.0/
gcc/
jit/
docs/
examples/
tut04-toyvm/
toyvm.c
       1  /* A simple stack-based virtual machine to demonstrate
       2     JIT-compilation.
       3     Copyright (C) 2014-2023 Free Software Foundation, Inc.
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it
       8  under the terms of the GNU General Public License as published by
       9  the Free Software Foundation; either version 3, or (at your option)
      10  any later version.
      11  
      12  GCC is distributed in the hope that it will be useful, but
      13  WITHOUT ANY WARRANTY; without even the implied warranty of
      14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15  General Public License for more details.
      16  
      17  You should have received a copy of the GNU General Public License
      18  along 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 "jit-dejagnu.h"
      28  
      29  #include <libgccjit.h>
      30  
      31  /* Typedefs.  */
      32  typedef struct toyvm_op toyvm_op;
      33  typedef struct toyvm_function toyvm_function;
      34  typedef struct toyvm_frame toyvm_frame;
      35  typedef struct compilation_state compilation_state;
      36  typedef struct toyvm_compiled_function toyvm_compiled_function;
      37  
      38  /* Functions are compiled to this function ptr type.  */
      39  typedef int (*toyvm_compiled_code) (int);
      40  
      41  enum 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  
      59  const 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  
      73  struct 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  
      87  struct 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  
      96  struct 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  
     104  static void
     105  add_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  
     116  static void
     117  add_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  
     124  static char *
     125  get_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  
     143  static toyvm_function *
     144  toyvm_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  
     236  static void
     237  toyvm_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  
     247  static void
     248  toyvm_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  
     258  static void
     259  toyvm_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  
     265  static int
     266  toyvm_frame_pop (toyvm_frame *frame)
     267  {
     268    assert (frame->frm_cur_depth > 0);
     269    return frame->frm_stack[--frame->frm_cur_depth];
     270  }
     271  
     272  static void
     273  toyvm_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  
     286  static int
     287  toyvm_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  
     384  struct 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  
     409  static void
     410  add_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  
     436  static void
     437  add_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  
     466  struct 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  
     474  static toyvm_compiled_function *
     475  toyvm_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[100];
     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    /* (this leaks "jit_result" and "funcname") */
     768  
     769    free (funcname);
     770  
     771    return toyvm_result;
     772  }
     773  
     774  char test[1024];
     775  
     776  #define CHECK_NON_NULL(PTR) \
     777    do {                                       \
     778      if ((PTR) != NULL)                       \
     779        {                                      \
     780  	pass ("%s: %s is non-null", test, #PTR); \
     781        }                                      \
     782      else                                     \
     783        {                                      \
     784  	fail ("%s: %s is NULL", test, #PTR); \
     785  	abort ();                            \
     786      }                                        \
     787    } while (0)
     788  
     789  #define CHECK_VALUE(ACTUAL, EXPECTED) \
     790    do {                                       \
     791      if ((ACTUAL) == (EXPECTED))              \
     792        {                                      \
     793  	pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \
     794        }                                      \
     795      else                                     \
     796        {                                        \
     797  	fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \
     798  	fprintf (stderr, "incorrect value\n"); \
     799  	abort ();                              \
     800      }                                        \
     801    } while (0)
     802  
     803  static void
     804  test_script (const char *scripts_dir, const char *script_name, int input,
     805  	     int expected_result)
     806  {
     807    char *script_path;
     808    toyvm_function *fn;
     809    int interpreted_result;
     810    toyvm_compiled_function *compiled_fn;
     811    toyvm_compiled_code code;
     812    int compiled_result;
     813  
     814    snprintf (test, sizeof (test), "toyvm.c: %s", script_name);
     815  
     816    script_path = (char *)malloc (strlen (scripts_dir)
     817  				+ strlen (script_name) + 1);
     818    CHECK_NON_NULL (script_path);
     819    sprintf (script_path, "%s%s", scripts_dir, script_name);
     820  
     821    fn = toyvm_function_parse (script_path, script_name);
     822    CHECK_NON_NULL (fn);
     823  
     824    interpreted_result = toyvm_function_interpret (fn, input, NULL);
     825    CHECK_VALUE (interpreted_result, expected_result);
     826  
     827    compiled_fn = toyvm_function_compile (fn);
     828    CHECK_NON_NULL (compiled_fn);
     829  
     830    code = (toyvm_compiled_code)compiled_fn->cf_code;
     831    CHECK_NON_NULL (code);
     832  
     833    compiled_result = code (input);
     834    CHECK_VALUE (compiled_result, expected_result);
     835  
     836    gcc_jit_result_release (compiled_fn->cf_jit_result);
     837    free (compiled_fn);
     838    free (fn);
     839    free (script_path);
     840  }
     841  
     842  #define PATH_TO_SCRIPTS  ("/jit/docs/examples/tut04-toyvm/")
     843  
     844  static void
     845  test_suite (void)
     846  {
     847    const char *srcdir;
     848    char *scripts_dir;
     849  
     850    snprintf (test, sizeof (test), "toyvm.c");
     851  
     852    /* We need to locate the test scripts.
     853       Rely on "srcdir" being set in the environment.  */
     854  
     855    srcdir = getenv ("srcdir");
     856    CHECK_NON_NULL (srcdir);
     857  
     858    scripts_dir = (char *)malloc (strlen (srcdir) + strlen(PATH_TO_SCRIPTS)
     859  				+ 1);
     860    CHECK_NON_NULL (scripts_dir);
     861    sprintf (scripts_dir, "%s%s", srcdir, PATH_TO_SCRIPTS);
     862  
     863    test_script (scripts_dir, "factorial.toy", 10, 3628800);
     864    test_script (scripts_dir, "fibonacci.toy", 10, 55);
     865  
     866    free (scripts_dir);
     867  }
     868  
     869  int
     870  main (int argc, char **argv)
     871  {
     872    const char *filename = NULL;
     873    toyvm_function *fn = NULL;
     874  
     875    /* If called with no args, assume we're being run by the test suite.  */
     876    if (argc < 3)
     877      {
     878        test_suite ();
     879        return 0;
     880      }
     881  
     882    if (argc != 3)
     883      {
     884        fprintf (stdout,
     885  	"%s FILENAME INPUT: Parse and run a .toy file\n",
     886  	argv[0]);
     887        exit (1);
     888      }
     889  
     890    filename = argv[1];
     891    fn = toyvm_function_parse (filename, filename);
     892    if (!fn)
     893      exit (1);
     894  
     895    if (0)
     896      toyvm_function_disassemble (fn, stdout);
     897  
     898    printf ("interpreter result: %d\n",
     899  	  toyvm_function_interpret (fn, atoi (argv[2]), NULL));
     900  
     901    /* JIT-compilation.  */
     902    toyvm_compiled_function *compiled_fn
     903      = toyvm_function_compile (fn);
     904  
     905    toyvm_compiled_code code = compiled_fn->cf_code;
     906    printf ("compiler result: %d\n",
     907  	  code (atoi (argv[2])));
     908  
     909    gcc_jit_result_release (compiled_fn->cf_jit_result);
     910    free (compiled_fn);
     911  
     912   return 0;
     913  }