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 gcc_jit_block * instances within the gcc_jit_function *:

gcc_jit_block *b_initial =
  gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_loop_cond =
  gcc_jit_function_new_block (func, "loop_cond");
gcc_jit_block *b_loop_body =
  gcc_jit_function_new_block (func, "loop_body");
gcc_jit_block *b_after_loop =
  gcc_jit_function_new_block (func, "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 gcc_jit_block_add_assignment() to add an assignment statement, and using gcc_jit_context_zero() to get the constant value 0 for the relevant type for the right-hand side of the assignment:

/* sum = 0; */
gcc_jit_block_add_assignment (
  b_initial, NULL,
  sum,
  gcc_jit_context_zero (ctxt, the_type));

/* i = 0; */
gcc_jit_block_add_assignment (
  b_initial, NULL,
  i,
  gcc_jit_context_zero (ctxt, the_type));

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

gcc_jit_block_end_with_jump (b_initial, NULL, 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 gcc_jit_rvalue *, in this case the comparison of i and n. We build the comparison using gcc_jit_context_new_comparison():

/* (i >= n) */
 gcc_jit_rvalue *guard =
   gcc_jit_context_new_comparison (
     ctxt, NULL,
     GCC_JIT_COMPARISON_GE,
     gcc_jit_lvalue_as_rvalue (i),
     gcc_jit_param_as_rvalue (n));

and can then use this to add b_loop_cond’s sole statement, via gcc_jit_block_end_with_conditional():

/* Equivalent to:
     if (guard)
       goto after_loop;
     else
       goto loop_body;  */
gcc_jit_block_end_with_conditional (
  b_loop_cond, NULL,
  guard,
  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 gcc_jit_block_add_assignment_op() to handle these operations:

/* sum += i * i */
gcc_jit_block_add_assignment_op (
  b_loop_body, NULL,
  sum,
  GCC_JIT_BINARY_OP_PLUS,
  gcc_jit_context_new_binary_op (
    ctxt, NULL,
    GCC_JIT_BINARY_OP_MULT, the_type,
    gcc_jit_lvalue_as_rvalue (i),
    gcc_jit_lvalue_as_rvalue (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++ */
gcc_jit_block_add_assignment_op (
  b_loop_body, NULL,
  i,
  GCC_JIT_BINARY_OP_PLUS,
  gcc_jit_context_one (ctxt, the_type));

Note: For numeric constants other than 0 or 1, we could use gcc_jit_context_new_rvalue_from_int() and gcc_jit_context_new_rvalue_from_double().

The loop body completes by jumping back to the conditional:

gcc_jit_block_end_with_jump (b_loop_body, NULL, 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 */
gcc_jit_block_end_with_return (
  b_after_loop,
  NULL,
  gcc_jit_lvalue_as_rvalue (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 = gcc_jit_context_compile (ctxt);

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)
  goto error;
printf ("result: %d", loop_test (10));
result: 285