3.1.3.2 Control flow

This function has a loop, so we need to build some basic blocks to handle the control flow. In this case, we need 4 blocks:

  1. before the loop (initializing the locals)
  2. the conditional at the top of the loop (comparing i < n)
  3. the body of the loop
  4. after the loop terminates (return sum)

so we create these as gccjit;;block instances within the gccjit;;function:

gccjit::block b_initial = func.new_block ("initial");
gccjit::block b_loop_cond = func.new_block ("loop_cond");
gccjit::block b_loop_body = func.new_block ("loop_body");
gccjit::block b_after_loop = func.new_block ("after_loop");

We now populate each block with statements.

The entry block b_initial consists of initializations followed by a jump to the conditional. We assign 0 to i and to sum, using gccjit;;block;;add_assignment() to add an assignment statement, and using gccjit;;context;;zero() to get the constant value 0 for the relevant type for the right-hand side of the assignment:

/* sum = 0; */
b_initial.add_assignment (sum, ctxt.zero (the_type));

/* i = 0; */
b_initial.add_assignment (i, ctxt.zero (the_type));

We can then terminate the entry block by jumping to the conditional:

b_initial.end_with_jump (b_loop_cond);

The conditional block is equivalent to the line while (i < n) from our C example. It contains a single statement: a conditional, which jumps to one of two destination blocks depending on a boolean gccjit;;rvalue, in this case the comparison of i and n.

We could build the comparison using gccjit;;context;;new_comparison():

gccjit::rvalue guard =
  ctxt.new_comparison (GCC_JIT_COMPARISON_GE,
                       i, n);

and can then use this to add b_loop_cond’s sole statement, via gccjit;;block;;end_with_conditional():

b_loop_cond.end_with_conditional (guard,
                                  b_after_loop, // on_true
                                  b_loop_body); // on_false

However gccjit;;rvalue has overloaded operators for this, so we express the conditional as

gccjit::rvalue guard = (i >= n);

and hence we can write the block more concisely as:

b_loop_cond.end_with_conditional (
  i >= n,
  b_after_loop, // on_true
  b_loop_body); // on_false

Next, we populate the body of the loop.

The C statement sum += i * i; is an assignment operation, where an lvalue is modified “in-place”. We use gccjit;;block;;add_assignment_op() to handle these operations:

/* sum += i * i */
b_loop_body.add_assignment_op (sum,
                               GCC_JIT_BINARY_OP_PLUS,
                               i * i);

The i++ can be thought of as i += 1, and can thus be handled in a similar way. We use gcc_jit_context_one() to get the constant value 1 (for the relevant type) for the right-hand side of the assignment.

/* i++ */
b_loop_body.add_assignment_op (i,
                               GCC_JIT_BINARY_OP_PLUS,
                               ctxt.one (the_type));

Note: For numeric constants other than 0 or 1, we could use gccjit;;context;;new_rvalue(), which has overloads for both int and double.

The loop body completes by jumping back to the conditional:

b_loop_body.end_with_jump (b_loop_cond);

Finally, we populate the b_after_loop block, reached when the loop conditional is false. We want to generate the equivalent of:

return sum;

so the block is just one statement:

/* return sum */
b_after_loop.end_with_return (sum);

Note: You can intermingle block creation with statement creation, but given that the terminator statements generally include references to other blocks, I find it’s clearer to create all the blocks, `then' all the statements.

We’ve finished populating the function. As before, we can now compile it to machine code:

gcc_jit_result *result;
result = ctxt.compile ();

ctxt.release ();

if (!result)
  {
    fprintf (stderr, "NULL result");
    return 1;
  }

typedef int (*loop_test_fn_type) (int);
loop_test_fn_type loop_test =
 (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test");
if (!loop_test)
  {
    fprintf (stderr, "NULL loop_test");
    gcc_jit_result_release (result);
    return 1;
  }
printf ("result: %d", loop_test (10));
result: 285