Creating and using functions#

Params#

class gccjit::param#

A gccjit::param represents a parameter to a function.

gccjit::param gccjit::context::new_param(gccjit::type type, const char *name, gccjit::location loc)#

In preparation for creating a function, create a new parameter of the given type and name.

gccjit::param is a subclass of gccjit::lvalue (and thus of gccjit::rvalue and gccjit::object). It is a thin wrapper around the C API’s gcc_jit_param*.

Functions#

class gccjit::function#

A gccjit::function represents a function - either one that we’re creating ourselves, or one that we’re referencing.

gccjit::function gccjit::context::new_function(enum gcc_jit_function_kind, gccjit::type return_type, const char *name, std::vector<param> &params, int is_variadic, gccjit::location loc)#

Create a gcc_jit_function with the given name and parameters.

Parameters “is_variadic” and “loc” are optional.

This is a wrapper around the C API’s gcc_jit_context_new_function().

gccjit::function gccjit::context::get_builtin_function(const char *name)#

This is a wrapper around the C API’s gcc_jit_context_get_builtin_function().

gccjit::param gccjit::function::get_param(int index) const#

Get the param of the given index (0-based).

void gccjit::function::dump_to_dot(const char *path)#

Emit the function in graphviz format to the given path.

gccjit::lvalue gccjit::function::new_local(gccjit::type type, const char *name, gccjit::location loc)#

Create a new local variable within the function, of the given type and name.

Blocks#

class gccjit::block#

A gccjit::block represents a basic block within a function i.e. a sequence of statements with a single entry point and a single exit point.

gccjit::block is a subclass of gccjit::object.

The first basic block that you create within a function will be the entrypoint.

Each basic block that you create within a function must be terminated, either with a conditional, a jump, a return, or a switch.

It’s legal to have multiple basic blocks that return within one function.

gccjit::block gccjit::function::new_block(const char *name)#

Create a basic block of the given name. The name may be NULL, but providing meaningful names is often helpful when debugging: it may show up in dumps of the internal representation, and in error messages.

Statements#

void gccjit::block::add_eval(gccjit::rvalue rvalue, gccjit::location loc)#

Add evaluation of an rvalue, discarding the result (e.g. a function call that “returns” void).

This is equivalent to this C code:

(void)expression;
void gccjit::block::add_assignment(gccjit::lvalue lvalue, gccjit::rvalue rvalue, gccjit::location loc)#

Add evaluation of an rvalue, assigning the result to the given lvalue.

This is roughly equivalent to this C code:

lvalue = rvalue;
void gccjit::block::add_assignment_op(gccjit::lvalue lvalue, enum gcc_jit_binary_op, gccjit::rvalue rvalue, gccjit::location loc)#

Add evaluation of an rvalue, using the result to modify an lvalue.

This is analogous to “+=” and friends:

lvalue += rvalue;
lvalue *= rvalue;
lvalue /= rvalue;

etc. For example:

/* "i++" */
loop_body.add_assignment_op (
  i,
  GCC_JIT_BINARY_OP_PLUS,
  ctxt.one (int_type));
void gccjit::block::add_comment(const char *text, gccjit::location loc)#

Add a no-op textual comment to the internal representation of the code. It will be optimized away, but will be visible in the dumps seen via GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE and GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, and thus may be of use when debugging how your project’s internal representation gets converted to the libgccjit IR.

Parameter “loc” is optional.

void gccjit::block::end_with_conditional(gccjit::rvalue boolval, gccjit::block on_true, gccjit::block on_false, gccjit::location loc)#

Terminate a block by adding evaluation of an rvalue, branching on the result to the appropriate successor block.

This is roughly equivalent to this C code:

if (boolval)
  goto on_true;
else
  goto on_false;

block, boolval, on_true, and on_false must be non-NULL.

void gccjit::block::end_with_jump(gccjit::block target, gccjit::location loc)#

Terminate a block by adding a jump to the given target block.

This is roughly equivalent to this C code:

goto target;
void gccjit::block::end_with_return(gccjit::rvalue rvalue, gccjit::location loc)#

Terminate a block.

Both params are optional.

An rvalue must be provided for a function returning non-void, and must not be provided by a function “returning” void.

If an rvalue is provided, the block is terminated by evaluating the rvalue and returning the value.

This is roughly equivalent to this C code:

return expression;

If an rvalue is not provided, the block is terminated by adding a valueless return, for use within a function with “void” return type.

This is equivalent to this C code:

return;
class gccjit::case_#

A gccjit::case_ represents a case within a switch statement, and is created within a particular gccjit::context using gccjit::context::new_case(). It is a subclass of gccjit::object.

Each case expresses a multivalued range of integer values. You can express single-valued cases by passing in the same value for both min_value and max_value.

gccjit::case_ *gccjit::context::new_case(gccjit::rvalue min_value, gccjit::rvalue max_value, gccjit::block dest_block)#

Create a new gccjit::case for use in a switch statement. min_value and max_value must be constants of an integer type, which must match that of the expression of the switch statement.

dest_block must be within the same function as the switch statement.

void gccjit::block::end_with_switch(gccjit::rvalue expr, gccjit::block default_block, std::vector<gccjit::case_> cases, gccjit::location loc)#

Terminate a block by adding evalation of an rvalue, then performing a multiway branch.

This is roughly equivalent to this C code:

switch (expr)
  {
  default:
    goto default_block;

  case C0.min_value ... C0.max_value:
    goto C0.dest_block;

  case C1.min_value ... C1.max_value:
    goto C1.dest_block;

  ...etc...

  case C[N - 1].min_value ... C[N - 1].max_value:
    goto C[N - 1].dest_block;
}

expr must be of the same integer type as all of the min_value and max_value within the cases.

The ranges of the cases must not overlap (or have duplicate values).

The API entrypoints relating to switch statements and cases:

were added in LIBGCCJIT_ABI_3; you can test for their presence using

#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS

Here’s an example of creating a switch statement:

void
create_code (gcc_jit_context *c_ctxt, void *user_data)
{
  /* Let's try to inject the equivalent of:
      int
      test_switch (int x)
      {
	switch (x)
	  {
	  case 0 ... 5:
	     return 3;

	  case 25 ... 27:
	     return 4;

	  case -42 ... -17:
	     return 83;

	  case 40:
	     return 8;

	  default:
	     return 10;
	  }
      }
   */
  gccjit::context ctxt (c_ctxt);
  gccjit::type t_int = ctxt.get_type (GCC_JIT_TYPE_INT);
  gccjit::type return_type = t_int;
  gccjit::param x = ctxt.new_param (t_int, "x");
  std::vector <gccjit::param> params;
  params.push_back (x);
  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
                                             return_type,
                                             "test_switch",
                                             params, 0);

  gccjit::block b_initial = func.new_block ("initial");

  gccjit::block b_default = func.new_block ("default");
  gccjit::block b_case_0_5 = func.new_block ("case_0_5");
  gccjit::block b_case_25_27 = func.new_block ("case_25_27");
  gccjit::block b_case_m42_m17 = func.new_block ("case_m42_m17");
  gccjit::block b_case_40 = func.new_block ("case_40");

  std::vector <gccjit::case_> cases;
  cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 0),
                                  ctxt.new_rvalue (t_int, 5),
                                  b_case_0_5));
  cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 25),
                                  ctxt.new_rvalue (t_int, 27),
                                  b_case_25_27));
  cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, -42),
                                  ctxt.new_rvalue (t_int, -17),
                                  b_case_m42_m17));
  cases.push_back (ctxt.new_case (ctxt.new_rvalue (t_int, 40),
                                  ctxt.new_rvalue (t_int, 40),
                                  b_case_40));
  b_initial.end_with_switch (x,
                             b_default,
                             cases);

  b_case_0_5.end_with_return (ctxt.new_rvalue (t_int, 3));
  b_case_25_27.end_with_return (ctxt.new_rvalue (t_int, 4));
  b_case_m42_m17.end_with_return (ctxt.new_rvalue (t_int, 83));
  b_case_40.end_with_return (ctxt.new_rvalue (t_int, 8));
  b_default.end_with_return (ctxt.new_rvalue (t_int, 10));
}