3.1.4.3 Setting things up

First we create our types:


void
compilation_state::create_types ()
{
  /* Create types.  */
  int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
  bool_type = ctxt.get_type (GCC_JIT_TYPE_BOOL);
  stack_type = ctxt.new_array_type (int_type, MAX_STACK_DEPTH);

along with extracting a useful int constant:

  const_one = ctxt.one (int_type);

}

We’ll implement push and pop in terms of the stack array and stack_depth. Here are helper functions for adding statements to a block, implementing pushing and popping values:


void
compilation_state::add_push (gccjit::block block,
                             gccjit::rvalue rvalue,
                             gccjit::location loc)
{
  /* stack[stack_depth] = RVALUE */
  block.add_assignment (
    /* stack[stack_depth] */
    ctxt.new_array_access (
      stack,
      stack_depth,
      loc),
    rvalue,
    loc);

  /* "stack_depth++;".  */
  block.add_assignment_op (
    stack_depth,
    GCC_JIT_BINARY_OP_PLUS,
    const_one,
    loc);
}

void
compilation_state::add_pop (gccjit::block block,
                            gccjit::lvalue lvalue,
                            gccjit::location loc)
{
  /* "--stack_depth;".  */
  block.add_assignment_op (
    stack_depth,
    GCC_JIT_BINARY_OP_MINUS,
    const_one,
    loc);

  /* "LVALUE = stack[stack_depth];".  */
  block.add_assignment (
    lvalue,
    /* stack[stack_depth] */
    ctxt.new_array_access (stack,
                           stack_depth,
                           loc),
    loc);
}

We will support single-stepping through the generated code in the debugger, so we need to create gccjit;;location instances, one per operation in the source code. These will reference the lines of e.g. factorial.toy.


void
compilation_state::create_locations ()
{
  for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++)
    {
      toyvm_op *op = &toyvmfn.fn_ops[pc];

      op_locs[pc] = ctxt.new_location (toyvmfn.fn_filename,
                                       op->op_linenum,
                                       0); /* column */
    }
}

Let’s create the function itself. As usual, we create its parameter first, then use the parameter to create the function:


void
compilation_state::create_function (const char *funcname)
{
  std::vector <gccjit::param> params;
  param_arg = ctxt.new_param (int_type, "arg", op_locs[0]);
  params.push_back (param_arg);
  fn = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
                          int_type,
                          funcname,
                          params, 0,
                          op_locs[0]);

We create the locals within the function.

  stack = fn.new_local (stack_type, "stack");
  stack_depth = fn.new_local (int_type, "stack_depth");
  x = fn.new_local (int_type, "x");
  y = fn.new_local (int_type, "y");