1.. Copyright (C) 2014-2020 Free Software Foundation, Inc.
2   Originally contributed by David Malcolm <dmalcolm@redhat.com>
3
4   This is free software: you can redistribute it and/or modify it
5   under the terms of the GNU General Public License as published by
6   the Free Software Foundation, either version 3 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program.  If not, see
16   <http://www.gnu.org/licenses/>.
17
18Tutorial part 3: Loops and variables
19------------------------------------
20Consider this C function:
21
22 .. code-block:: c
23
24  int loop_test (int n)
25  {
26    int sum = 0;
27    for (int i = 0; i < n; i++)
28      sum += i * i;
29    return sum;
30  }
31
32This example demonstrates some more features of libgccjit, with local
33variables and a loop.
34
35To break this down into libgccjit terms, it's usually easier to reword
36the `for` loop as a `while` loop, giving:
37
38 .. code-block:: c
39
40  int loop_test (int n)
41  {
42    int sum = 0;
43    int i = 0;
44    while (i < n)
45    {
46      sum += i * i;
47      i++;
48    }
49    return sum;
50  }
51
52Here's what the final control flow graph will look like:
53
54    .. figure:: sum-of-squares.png
55      :alt: image of a control flow graph
56
57As before, we include the libgccjit header and make a
58:c:type:`gcc_jit_context *`.
59
60.. code-block:: c
61
62  #include <libgccjit.h>
63
64  void test (void)
65  {
66    gcc_jit_context *ctxt;
67    ctxt = gcc_jit_context_acquire ();
68
69The function works with the C `int` type:
70
71.. code-block:: c
72
73  gcc_jit_type *the_type =
74    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
75  gcc_jit_type *return_type = the_type;
76
77though we could equally well make it work on, say, `double`:
78
79.. code-block:: c
80
81  gcc_jit_type *the_type =
82    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
83
84Let's build the function:
85
86.. code-block:: c
87
88  gcc_jit_param *n =
89    gcc_jit_context_new_param (ctxt, NULL, the_type, "n");
90  gcc_jit_param *params[1] = {n};
91  gcc_jit_function *func =
92    gcc_jit_context_new_function (ctxt, NULL,
93				  GCC_JIT_FUNCTION_EXPORTED,
94				  return_type,
95				  "loop_test",
96				  1, params, 0);
97
98Expressions: lvalues and rvalues
99********************************
100
101The base class of expression is the :c:type:`gcc_jit_rvalue *`,
102representing an expression that can be on the *right*-hand side of
103an assignment: a value that can be computed somehow, and assigned
104*to* a storage area (such as a variable).  It has a specific
105:c:type:`gcc_jit_type *`.
106
107Anothe important class is :c:type:`gcc_jit_lvalue *`.
108A :c:type:`gcc_jit_lvalue *`. is something that can of the *left*-hand
109side of an assignment: a storage area (such as a variable).
110
111In other words, every assignment can be thought of as:
112
113.. code-block:: c
114
115   LVALUE = RVALUE;
116
117Note that :c:type:`gcc_jit_lvalue *` is a subclass of
118:c:type:`gcc_jit_rvalue *`, where in an assignment of the form:
119
120.. code-block:: c
121
122   LVALUE_A = LVALUE_B;
123
124the `LVALUE_B` implies reading the current value of that storage
125area, assigning it into the `LVALUE_A`.
126
127So far the only expressions we've seen are `i * i`:
128
129.. code-block:: c
130
131   gcc_jit_rvalue *expr =
132     gcc_jit_context_new_binary_op (
133       ctxt, NULL,
134       GCC_JIT_BINARY_OP_MULT, int_type,
135       gcc_jit_param_as_rvalue (param_i),
136       gcc_jit_param_as_rvalue (param_i));
137
138which is a :c:type:`gcc_jit_rvalue *`, and the various function
139parameters: `param_i` and `param_n`, instances of
140:c:type:`gcc_jit_param *`, which is a subclass of
141:c:type:`gcc_jit_lvalue *` (and, in turn, of :c:type:`gcc_jit_rvalue *`):
142we can both read from and write to function parameters within the
143body of a function.
144
145Our new example has a couple of local variables.  We create them by
146calling :c:func:`gcc_jit_function_new_local`, supplying a type and a
147name:
148
149.. code-block:: c
150
151  /* Build locals:  */
152  gcc_jit_lvalue *i =
153    gcc_jit_function_new_local (func, NULL, the_type, "i");
154  gcc_jit_lvalue *sum =
155    gcc_jit_function_new_local (func, NULL, the_type, "sum");
156
157These are instances of :c:type:`gcc_jit_lvalue *` - they can be read from
158and written to.
159
160Note that there is no precanned way to create *and* initialize a variable
161like in C:
162
163.. code-block:: c
164
165   int i = 0;
166
167Instead, having added the local to the function, we have to separately add
168an assignment of `0` to `local_i` at the beginning of the function.
169
170Control flow
171************
172
173This function has a loop, so we need to build some basic blocks to
174handle the control flow.  In this case, we need 4 blocks:
175
1761. before the loop (initializing the locals)
1772. the conditional at the top of the loop (comparing `i < n`)
1783. the body of the loop
1794. after the loop terminates (`return sum`)
180
181so we create these as :c:type:`gcc_jit_block *` instances within the
182:c:type:`gcc_jit_function *`:
183
184.. code-block:: c
185
186  gcc_jit_block *b_initial =
187    gcc_jit_function_new_block (func, "initial");
188  gcc_jit_block *b_loop_cond =
189    gcc_jit_function_new_block (func, "loop_cond");
190  gcc_jit_block *b_loop_body =
191    gcc_jit_function_new_block (func, "loop_body");
192  gcc_jit_block *b_after_loop =
193    gcc_jit_function_new_block (func, "after_loop");
194
195We now populate each block with statements.
196
197The entry block `b_initial` consists of initializations followed by a jump
198to the conditional.  We assign `0` to `i` and to `sum`, using
199:c:func:`gcc_jit_block_add_assignment` to add
200an assignment statement, and using :c:func:`gcc_jit_context_zero` to get
201the constant value `0` for the relevant type for the right-hand side of
202the assignment:
203
204.. code-block:: c
205
206  /* sum = 0; */
207  gcc_jit_block_add_assignment (
208    b_initial, NULL,
209    sum,
210    gcc_jit_context_zero (ctxt, the_type));
211
212  /* i = 0; */
213  gcc_jit_block_add_assignment (
214    b_initial, NULL,
215    i,
216    gcc_jit_context_zero (ctxt, the_type));
217
218We can then terminate the entry block by jumping to the conditional:
219
220.. code-block:: c
221
222  gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond);
223
224The conditional block is equivalent to the line `while (i < n)` from our
225C example. It contains a single statement: a conditional, which jumps to
226one of two destination blocks depending on a boolean
227:c:type:`gcc_jit_rvalue *`, in this case the comparison of `i` and `n`.
228We build the comparison using :c:func:`gcc_jit_context_new_comparison`:
229
230.. code-block:: c
231
232  /* (i >= n) */
233   gcc_jit_rvalue *guard =
234     gcc_jit_context_new_comparison (
235       ctxt, NULL,
236       GCC_JIT_COMPARISON_GE,
237       gcc_jit_lvalue_as_rvalue (i),
238       gcc_jit_param_as_rvalue (n));
239
240and can then use this to add `b_loop_cond`'s sole statement, via
241:c:func:`gcc_jit_block_end_with_conditional`:
242
243.. code-block:: c
244
245  /* Equivalent to:
246       if (guard)
247         goto after_loop;
248       else
249         goto loop_body;  */
250  gcc_jit_block_end_with_conditional (
251    b_loop_cond, NULL,
252    guard,
253    b_after_loop, /* on_true */
254    b_loop_body); /* on_false */
255
256Next, we populate the body of the loop.
257
258The C statement `sum += i * i;` is an assignment operation, where an
259lvalue is modified "in-place".  We use
260:c:func:`gcc_jit_block_add_assignment_op` to handle these operations:
261
262.. code-block:: c
263
264  /* sum += i * i */
265  gcc_jit_block_add_assignment_op (
266    b_loop_body, NULL,
267    sum,
268    GCC_JIT_BINARY_OP_PLUS,
269    gcc_jit_context_new_binary_op (
270      ctxt, NULL,
271      GCC_JIT_BINARY_OP_MULT, the_type,
272      gcc_jit_lvalue_as_rvalue (i),
273      gcc_jit_lvalue_as_rvalue (i)));
274
275The `i++` can be thought of as `i += 1`, and can thus be handled in
276a similar way.  We use :c:func:`gcc_jit_context_one` to get the constant
277value `1` (for the relevant type) for the right-hand side
278of the assignment.
279
280.. code-block:: c
281
282  /* i++ */
283  gcc_jit_block_add_assignment_op (
284    b_loop_body, NULL,
285    i,
286    GCC_JIT_BINARY_OP_PLUS,
287    gcc_jit_context_one (ctxt, the_type));
288
289.. note::
290
291  For numeric constants other than 0 or 1, we could use
292  :c:func:`gcc_jit_context_new_rvalue_from_int` and
293  :c:func:`gcc_jit_context_new_rvalue_from_double`.
294
295The loop body completes by jumping back to the conditional:
296
297.. code-block:: c
298
299  gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond);
300
301Finally, we populate the `b_after_loop` block, reached when the loop
302conditional is false.  We want to generate the equivalent of:
303
304.. code-block:: c
305
306   return sum;
307
308so the block is just one statement:
309
310.. code-block:: c
311
312  /* return sum */
313  gcc_jit_block_end_with_return (
314    b_after_loop,
315    NULL,
316    gcc_jit_lvalue_as_rvalue (sum));
317
318.. note::
319
320   You can intermingle block creation with statement creation,
321   but given that the terminator statements generally include references
322   to other blocks, I find it's clearer to create all the blocks,
323   *then* all the statements.
324
325We've finished populating the function.  As before, we can now compile it
326to machine code:
327
328.. code-block:: c
329
330   gcc_jit_result *result;
331   result = gcc_jit_context_compile (ctxt);
332
333   typedef int (*loop_test_fn_type) (int);
334   loop_test_fn_type loop_test =
335    (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test");
336   if (!loop_test)
337     goto error;
338   printf ("result: %d", loop_test (10));
339
340.. code-block:: bash
341
342   result: 285
343
344
345Visualizing the control flow graph
346**********************************
347
348You can see the control flow graph of a function using
349:c:func:`gcc_jit_function_dump_to_dot`:
350
351.. code-block:: c
352
353  gcc_jit_function_dump_to_dot (func, "/tmp/sum-of-squares.dot");
354
355giving a .dot file in GraphViz format.
356
357You can convert this to an image using `dot`:
358
359.. code-block:: bash
360
361   $ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png
362
363or use a viewer (my preferred one is xdot.py; see
364https://github.com/jrfonseca/xdot.py; on Fedora you can
365install it with `yum install python-xdot`):
366
367    .. figure:: sum-of-squares.png
368      :alt: image of a control flow graph
369
370Full example
371************
372
373   .. literalinclude:: ../examples/tut03-sum-of-squares.c
374    :lines: 1-
375    :language: c
376
377Building and running it:
378
379.. code-block:: console
380
381  $ gcc \
382      tut03-sum-of-squares.c \
383      -o tut03-sum-of-squares \
384      -lgccjit
385
386  # Run the built program:
387  $ ./tut03-sum-of-squares
388  loop_test returned: 285
389