Tutorial part 1: “Hello world”

Before we look at the details of the API, let’s look at building and running programs that use the library.

Here’s a toy “hello world” program that uses the library’s C++ API to synthesize a call to printf and uses it to write a message to stdout.

Don’t worry about the content of the program for now; we’ll cover the details in later parts of this tutorial.

/* Smoketest example for libgccjit.so C++ API
   Copyright (C) 2014-2021 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include <libgccjit++.h>

#include <stdlib.h>
#include <stdio.h>

static void
create_code (gccjit::context ctxt)
{
  /* Let's try to inject the equivalent of this C code:
     void
     greet (const char *name)
     {
        printf ("hello %s\n", name);
     }
  */
  gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID);
  gccjit::type const_char_ptr_type =
    ctxt.get_type (GCC_JIT_TYPE_CONST_CHAR_PTR);
  gccjit::param param_name =
    ctxt.new_param (const_char_ptr_type, "name");
  std::vector<gccjit::param> func_params;
  func_params.push_back (param_name);
  gccjit::function func =
    ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
                       void_type,
                       "greet",
                       func_params, 0);

  gccjit::param param_format =
    ctxt.new_param (const_char_ptr_type, "format");
  std::vector<gccjit::param> printf_params;
  printf_params.push_back (param_format);
  gccjit::function printf_func =
    ctxt.new_function (GCC_JIT_FUNCTION_IMPORTED,
                       ctxt.get_type (GCC_JIT_TYPE_INT),
                       "printf",
                       printf_params, 1);

  gccjit::block block = func.new_block ();
  block.add_eval (ctxt.new_call (printf_func,
                                 ctxt.new_rvalue ("hello %s\n"),
                                 param_name));
  block.end_with_return ();
}

int
main (int argc, char **argv)
{
  gccjit::context ctxt;
  gcc_jit_result *result;

  /* Get a "context" object for working with the library.  */
  ctxt = gccjit::context::acquire ();

  /* Set some options on the context.
     Turn this on to see the code being generated, in assembler form.  */
  ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0);

  /* Populate the context.  */
  create_code (ctxt);

  /* Compile the code.  */
  result = ctxt.compile ();
  if (!result)
    {
      fprintf (stderr, "NULL result");
      exit (1);
    }

  ctxt.release ();

  /* Extract the generated code from "result".  */
  typedef void (*fn_type) (const char *);
  fn_type greet =
    (fn_type)gcc_jit_result_get_code (result, "greet");
  if (!greet)
    {
      fprintf (stderr, "NULL greet");
      exit (1);
    }

  /* Now call the generated function: */
  greet ("world");
  fflush (stdout);

  gcc_jit_result_release (result);
  return 0;
}

Copy the above to tut01-hello-world.cc.

Assuming you have the jit library installed, build the test program using:

$ gcc \
    tut01-hello-world.cc \
    -o tut01-hello-world \
    -lgccjit

You should then be able to run the built program:

$ ./tut01-hello-world
hello world