# Tutorial part 3: Loops and variables¶

Consider this C function:

```int loop_test (int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
sum += i * i;
return sum;
}
```

This example demonstrates some more features of libgccjit, with local variables and a loop.

To break this down into libgccjit terms, it’s usually easier to reword the for loop as a while loop, giving:

```int loop_test (int n)
{
int sum = 0;
int i = 0;
while (i < n)
{
sum += i * i;
i++;
}
return sum;
}
```

Here’s what the final control flow graph will look like:

As before, we include the libgccjit header and make a .

```#include <libgccjit.h>

void test (void)
{
gcc_jit_context *ctxt;
ctxt = gcc_jit_context_acquire ();
```

The function works with the C int type:

```gcc_jit_type *the_type =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = the_type;
```

though we could equally well make it work on, say, double:

```gcc_jit_type *the_type =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
```

Let’s build the function:

```gcc_jit_param *n =
gcc_jit_context_new_param (ctxt, NULL, the_type, "n");
gcc_jit_param *params[1] = {n};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"loop_test",
1, params, 0);
```

## Expressions: lvalues and rvalues¶

The base class of expression is the , representing an expression that can be on the right-hand side of an assignment: a value that can be computed somehow, and assigned to a storage area (such as a variable). It has a specific .

Anothe important class is . A . is something that can of the left-hand side of an assignment: a storage area (such as a variable).

In other words, every assignment can be thought of as:

```LVALUE = RVALUE;
```

Note that is a subclass of , where in an assignment of the form:

```LVALUE_A = LVALUE_B;
```

the LVALUE_B implies reading the current value of that storage area, assigning it into the LVALUE_A.

So far the only expressions we’ve seen are i * i:

```gcc_jit_rvalue *expr =
gcc_jit_context_new_binary_op (
ctxt, NULL,
GCC_JIT_BINARY_OP_MULT, int_type,
gcc_jit_param_as_rvalue (param_i),
gcc_jit_param_as_rvalue (param_i));
```

which is a , and the various function parameters: param_i and param_n, instances of , which is a subclass of (and, in turn, of ): we can both read from and write to function parameters within the body of a function.

Our new example has a couple of local variables. We create them by calling `gcc_jit_function_new_local()`, supplying a type and a name:

```/* Build locals:  */
gcc_jit_lvalue *i =
gcc_jit_function_new_local (func, NULL, the_type, "i");
gcc_jit_lvalue *sum =
gcc_jit_function_new_local (func, NULL, the_type, "sum");
```

These are instances of - they can be read from and written to.

Note that there is no precanned way to create and initialize a variable like in C:

```int i = 0;
```

Instead, having added the local to the function, we have to separately add an assignment of 0 to local_i at the beginning of the function.

## 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 instances within the :

```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; */
b_initial, NULL,
sum,
gcc_jit_context_zero (ctxt, the_type));

/* i = 0; */
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 , 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 */
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++ */
b_loop_body, NULL,
i,
GCC_JIT_BINARY_OP_PLUS,
gcc_jit_context_one (ctxt, the_type));
```

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
```

## Visualizing the control flow graph¶

You can see the control flow graph of a function using `gcc_jit_function_dump_to_dot()`:

```gcc_jit_function_dump_to_dot (func, "/tmp/sum-of-squares.dot");
```

giving a .dot file in GraphViz format.

You can convert this to an image using dot:

```\$ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png
```

or use a viewer (my preferred one is xdot.py; see https://github.com/jrfonseca/xdot.py; on Fedora you can install it with yum install python-xdot):

## Full example¶

```/* Usage example for libgccjit.so
Copyright (C) 2014-2023 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it
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

#include <libgccjit.h>

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

void
create_code (gcc_jit_context *ctxt)
{
/*
Simple sum-of-squares, to test conditionals and looping

int loop_test (int n)
{
int i;
int sum = 0;
for (i = 0; i < n ; i ++)
{
sum += i * i;
}
return sum;
*/
gcc_jit_type *the_type =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = the_type;

gcc_jit_param *n =
gcc_jit_context_new_param (ctxt, NULL, the_type, "n");
gcc_jit_param *params[1] = {n};
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
return_type,
"loop_test",
1, params, 0);

/* Build locals:  */
gcc_jit_lvalue *i =
gcc_jit_function_new_local (func, NULL, the_type, "i");
gcc_jit_lvalue *sum =
gcc_jit_function_new_local (func, NULL, the_type, "sum");

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");

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

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

gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond);

/* if (i >= n) */
gcc_jit_block_end_with_conditional (
b_loop_cond, NULL,
gcc_jit_context_new_comparison (
ctxt, NULL,
GCC_JIT_COMPARISON_GE,
gcc_jit_lvalue_as_rvalue (i),
gcc_jit_param_as_rvalue (n)),
b_after_loop,
b_loop_body);

/* sum += i * i */
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)));

/* i++ */
b_loop_body, NULL,
i,
GCC_JIT_BINARY_OP_PLUS,
gcc_jit_context_one (ctxt, the_type));

gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond);

/* return sum */
gcc_jit_block_end_with_return (
b_after_loop,
NULL,
gcc_jit_lvalue_as_rvalue (sum));
}

int
main (int argc, char **argv)
{
gcc_jit_context *ctxt = NULL;
gcc_jit_result *result = NULL;

/* Get a "context" object for working with the library.  */
ctxt = gcc_jit_context_acquire ();
if (!ctxt)
{
fprintf (stderr, "NULL ctxt");
goto error;
}

/* Set some options on the context.
Let's see the code being generated, in assembler form.  */
gcc_jit_context_set_bool_option (
ctxt,
GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
0);

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

/* Compile the code.  */
result = gcc_jit_context_compile (ctxt);
if (!result)
{
fprintf (stderr, "NULL result");
goto error;
}

/* Extract the generated code from "result".  */
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");
goto error;
}

/* Run the generated code.  */
int val = loop_test (10);
printf("loop_test returned: %d\n", val);

error:
gcc_jit_context_release (ctxt);
gcc_jit_result_release (result);
return 0;
}
```

Building and running it:

```\$ gcc \
tut03-sum-of-squares.c \
-o tut03-sum-of-squares \
-lgccjit

# Run the built program:
\$ ./tut03-sum-of-squares
loop_test returned: 285
```