xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/jit/docs/intro/tutorial03.rst (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1*8feb0f0bSmrg.. Copyright (C) 2014-2020 Free Software Foundation, Inc.
236ac495dSmrg   Originally contributed by David Malcolm <dmalcolm@redhat.com>
336ac495dSmrg
436ac495dSmrg   This is free software: you can redistribute it and/or modify it
536ac495dSmrg   under the terms of the GNU General Public License as published by
636ac495dSmrg   the Free Software Foundation, either version 3 of the License, or
736ac495dSmrg   (at your option) any later version.
836ac495dSmrg
936ac495dSmrg   This program is distributed in the hope that it will be useful, but
1036ac495dSmrg   WITHOUT ANY WARRANTY; without even the implied warranty of
1136ac495dSmrg   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1236ac495dSmrg   General Public License for more details.
1336ac495dSmrg
1436ac495dSmrg   You should have received a copy of the GNU General Public License
1536ac495dSmrg   along with this program.  If not, see
1636ac495dSmrg   <http://www.gnu.org/licenses/>.
1736ac495dSmrg
1836ac495dSmrgTutorial part 3: Loops and variables
1936ac495dSmrg------------------------------------
2036ac495dSmrgConsider this C function:
2136ac495dSmrg
2236ac495dSmrg .. code-block:: c
2336ac495dSmrg
2436ac495dSmrg  int loop_test (int n)
2536ac495dSmrg  {
2636ac495dSmrg    int sum = 0;
2736ac495dSmrg    for (int i = 0; i < n; i++)
2836ac495dSmrg      sum += i * i;
2936ac495dSmrg    return sum;
3036ac495dSmrg  }
3136ac495dSmrg
3236ac495dSmrgThis example demonstrates some more features of libgccjit, with local
3336ac495dSmrgvariables and a loop.
3436ac495dSmrg
3536ac495dSmrgTo break this down into libgccjit terms, it's usually easier to reword
3636ac495dSmrgthe `for` loop as a `while` loop, giving:
3736ac495dSmrg
3836ac495dSmrg .. code-block:: c
3936ac495dSmrg
4036ac495dSmrg  int loop_test (int n)
4136ac495dSmrg  {
4236ac495dSmrg    int sum = 0;
4336ac495dSmrg    int i = 0;
4436ac495dSmrg    while (i < n)
4536ac495dSmrg    {
4636ac495dSmrg      sum += i * i;
4736ac495dSmrg      i++;
4836ac495dSmrg    }
4936ac495dSmrg    return sum;
5036ac495dSmrg  }
5136ac495dSmrg
5236ac495dSmrgHere's what the final control flow graph will look like:
5336ac495dSmrg
5436ac495dSmrg    .. figure:: sum-of-squares.png
5536ac495dSmrg      :alt: image of a control flow graph
5636ac495dSmrg
5736ac495dSmrgAs before, we include the libgccjit header and make a
5836ac495dSmrg:c:type:`gcc_jit_context *`.
5936ac495dSmrg
6036ac495dSmrg.. code-block:: c
6136ac495dSmrg
6236ac495dSmrg  #include <libgccjit.h>
6336ac495dSmrg
6436ac495dSmrg  void test (void)
6536ac495dSmrg  {
6636ac495dSmrg    gcc_jit_context *ctxt;
6736ac495dSmrg    ctxt = gcc_jit_context_acquire ();
6836ac495dSmrg
6936ac495dSmrgThe function works with the C `int` type:
7036ac495dSmrg
7136ac495dSmrg.. code-block:: c
7236ac495dSmrg
7336ac495dSmrg  gcc_jit_type *the_type =
7436ac495dSmrg    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
7536ac495dSmrg  gcc_jit_type *return_type = the_type;
7636ac495dSmrg
7736ac495dSmrgthough we could equally well make it work on, say, `double`:
7836ac495dSmrg
7936ac495dSmrg.. code-block:: c
8036ac495dSmrg
8136ac495dSmrg  gcc_jit_type *the_type =
8236ac495dSmrg    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
8336ac495dSmrg
8436ac495dSmrgLet's build the function:
8536ac495dSmrg
8636ac495dSmrg.. code-block:: c
8736ac495dSmrg
8836ac495dSmrg  gcc_jit_param *n =
8936ac495dSmrg    gcc_jit_context_new_param (ctxt, NULL, the_type, "n");
9036ac495dSmrg  gcc_jit_param *params[1] = {n};
9136ac495dSmrg  gcc_jit_function *func =
9236ac495dSmrg    gcc_jit_context_new_function (ctxt, NULL,
9336ac495dSmrg				  GCC_JIT_FUNCTION_EXPORTED,
9436ac495dSmrg				  return_type,
9536ac495dSmrg				  "loop_test",
9636ac495dSmrg				  1, params, 0);
9736ac495dSmrg
9836ac495dSmrgExpressions: lvalues and rvalues
9936ac495dSmrg********************************
10036ac495dSmrg
10136ac495dSmrgThe base class of expression is the :c:type:`gcc_jit_rvalue *`,
10236ac495dSmrgrepresenting an expression that can be on the *right*-hand side of
10336ac495dSmrgan assignment: a value that can be computed somehow, and assigned
10436ac495dSmrg*to* a storage area (such as a variable).  It has a specific
10536ac495dSmrg:c:type:`gcc_jit_type *`.
10636ac495dSmrg
10736ac495dSmrgAnothe important class is :c:type:`gcc_jit_lvalue *`.
10836ac495dSmrgA :c:type:`gcc_jit_lvalue *`. is something that can of the *left*-hand
10936ac495dSmrgside of an assignment: a storage area (such as a variable).
11036ac495dSmrg
11136ac495dSmrgIn other words, every assignment can be thought of as:
11236ac495dSmrg
11336ac495dSmrg.. code-block:: c
11436ac495dSmrg
11536ac495dSmrg   LVALUE = RVALUE;
11636ac495dSmrg
11736ac495dSmrgNote that :c:type:`gcc_jit_lvalue *` is a subclass of
11836ac495dSmrg:c:type:`gcc_jit_rvalue *`, where in an assignment of the form:
11936ac495dSmrg
12036ac495dSmrg.. code-block:: c
12136ac495dSmrg
12236ac495dSmrg   LVALUE_A = LVALUE_B;
12336ac495dSmrg
12436ac495dSmrgthe `LVALUE_B` implies reading the current value of that storage
12536ac495dSmrgarea, assigning it into the `LVALUE_A`.
12636ac495dSmrg
12736ac495dSmrgSo far the only expressions we've seen are `i * i`:
12836ac495dSmrg
12936ac495dSmrg.. code-block:: c
13036ac495dSmrg
13136ac495dSmrg   gcc_jit_rvalue *expr =
13236ac495dSmrg     gcc_jit_context_new_binary_op (
13336ac495dSmrg       ctxt, NULL,
13436ac495dSmrg       GCC_JIT_BINARY_OP_MULT, int_type,
13536ac495dSmrg       gcc_jit_param_as_rvalue (param_i),
13636ac495dSmrg       gcc_jit_param_as_rvalue (param_i));
13736ac495dSmrg
13836ac495dSmrgwhich is a :c:type:`gcc_jit_rvalue *`, and the various function
13936ac495dSmrgparameters: `param_i` and `param_n`, instances of
14036ac495dSmrg:c:type:`gcc_jit_param *`, which is a subclass of
14136ac495dSmrg:c:type:`gcc_jit_lvalue *` (and, in turn, of :c:type:`gcc_jit_rvalue *`):
14236ac495dSmrgwe can both read from and write to function parameters within the
14336ac495dSmrgbody of a function.
14436ac495dSmrg
14536ac495dSmrgOur new example has a couple of local variables.  We create them by
14636ac495dSmrgcalling :c:func:`gcc_jit_function_new_local`, supplying a type and a
14736ac495dSmrgname:
14836ac495dSmrg
14936ac495dSmrg.. code-block:: c
15036ac495dSmrg
15136ac495dSmrg  /* Build locals:  */
15236ac495dSmrg  gcc_jit_lvalue *i =
15336ac495dSmrg    gcc_jit_function_new_local (func, NULL, the_type, "i");
15436ac495dSmrg  gcc_jit_lvalue *sum =
15536ac495dSmrg    gcc_jit_function_new_local (func, NULL, the_type, "sum");
15636ac495dSmrg
15736ac495dSmrgThese are instances of :c:type:`gcc_jit_lvalue *` - they can be read from
15836ac495dSmrgand written to.
15936ac495dSmrg
16036ac495dSmrgNote that there is no precanned way to create *and* initialize a variable
16136ac495dSmrglike in C:
16236ac495dSmrg
16336ac495dSmrg.. code-block:: c
16436ac495dSmrg
16536ac495dSmrg   int i = 0;
16636ac495dSmrg
16736ac495dSmrgInstead, having added the local to the function, we have to separately add
16836ac495dSmrgan assignment of `0` to `local_i` at the beginning of the function.
16936ac495dSmrg
17036ac495dSmrgControl flow
17136ac495dSmrg************
17236ac495dSmrg
17336ac495dSmrgThis function has a loop, so we need to build some basic blocks to
17436ac495dSmrghandle the control flow.  In this case, we need 4 blocks:
17536ac495dSmrg
17636ac495dSmrg1. before the loop (initializing the locals)
17736ac495dSmrg2. the conditional at the top of the loop (comparing `i < n`)
17836ac495dSmrg3. the body of the loop
17936ac495dSmrg4. after the loop terminates (`return sum`)
18036ac495dSmrg
18136ac495dSmrgso we create these as :c:type:`gcc_jit_block *` instances within the
18236ac495dSmrg:c:type:`gcc_jit_function *`:
18336ac495dSmrg
18436ac495dSmrg.. code-block:: c
18536ac495dSmrg
18636ac495dSmrg  gcc_jit_block *b_initial =
18736ac495dSmrg    gcc_jit_function_new_block (func, "initial");
18836ac495dSmrg  gcc_jit_block *b_loop_cond =
18936ac495dSmrg    gcc_jit_function_new_block (func, "loop_cond");
19036ac495dSmrg  gcc_jit_block *b_loop_body =
19136ac495dSmrg    gcc_jit_function_new_block (func, "loop_body");
19236ac495dSmrg  gcc_jit_block *b_after_loop =
19336ac495dSmrg    gcc_jit_function_new_block (func, "after_loop");
19436ac495dSmrg
19536ac495dSmrgWe now populate each block with statements.
19636ac495dSmrg
19736ac495dSmrgThe entry block `b_initial` consists of initializations followed by a jump
19836ac495dSmrgto the conditional.  We assign `0` to `i` and to `sum`, using
19936ac495dSmrg:c:func:`gcc_jit_block_add_assignment` to add
20036ac495dSmrgan assignment statement, and using :c:func:`gcc_jit_context_zero` to get
20136ac495dSmrgthe constant value `0` for the relevant type for the right-hand side of
20236ac495dSmrgthe assignment:
20336ac495dSmrg
20436ac495dSmrg.. code-block:: c
20536ac495dSmrg
20636ac495dSmrg  /* sum = 0; */
20736ac495dSmrg  gcc_jit_block_add_assignment (
20836ac495dSmrg    b_initial, NULL,
20936ac495dSmrg    sum,
21036ac495dSmrg    gcc_jit_context_zero (ctxt, the_type));
21136ac495dSmrg
21236ac495dSmrg  /* i = 0; */
21336ac495dSmrg  gcc_jit_block_add_assignment (
21436ac495dSmrg    b_initial, NULL,
21536ac495dSmrg    i,
21636ac495dSmrg    gcc_jit_context_zero (ctxt, the_type));
21736ac495dSmrg
21836ac495dSmrgWe can then terminate the entry block by jumping to the conditional:
21936ac495dSmrg
22036ac495dSmrg.. code-block:: c
22136ac495dSmrg
22236ac495dSmrg  gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond);
22336ac495dSmrg
22436ac495dSmrgThe conditional block is equivalent to the line `while (i < n)` from our
22536ac495dSmrgC example. It contains a single statement: a conditional, which jumps to
22636ac495dSmrgone of two destination blocks depending on a boolean
22736ac495dSmrg:c:type:`gcc_jit_rvalue *`, in this case the comparison of `i` and `n`.
22836ac495dSmrgWe build the comparison using :c:func:`gcc_jit_context_new_comparison`:
22936ac495dSmrg
23036ac495dSmrg.. code-block:: c
23136ac495dSmrg
23236ac495dSmrg  /* (i >= n) */
23336ac495dSmrg   gcc_jit_rvalue *guard =
23436ac495dSmrg     gcc_jit_context_new_comparison (
23536ac495dSmrg       ctxt, NULL,
23636ac495dSmrg       GCC_JIT_COMPARISON_GE,
23736ac495dSmrg       gcc_jit_lvalue_as_rvalue (i),
23836ac495dSmrg       gcc_jit_param_as_rvalue (n));
23936ac495dSmrg
24036ac495dSmrgand can then use this to add `b_loop_cond`'s sole statement, via
24136ac495dSmrg:c:func:`gcc_jit_block_end_with_conditional`:
24236ac495dSmrg
24336ac495dSmrg.. code-block:: c
24436ac495dSmrg
24536ac495dSmrg  /* Equivalent to:
24636ac495dSmrg       if (guard)
24736ac495dSmrg         goto after_loop;
24836ac495dSmrg       else
24936ac495dSmrg         goto loop_body;  */
25036ac495dSmrg  gcc_jit_block_end_with_conditional (
25136ac495dSmrg    b_loop_cond, NULL,
25236ac495dSmrg    guard,
25336ac495dSmrg    b_after_loop, /* on_true */
25436ac495dSmrg    b_loop_body); /* on_false */
25536ac495dSmrg
25636ac495dSmrgNext, we populate the body of the loop.
25736ac495dSmrg
25836ac495dSmrgThe C statement `sum += i * i;` is an assignment operation, where an
25936ac495dSmrglvalue is modified "in-place".  We use
26036ac495dSmrg:c:func:`gcc_jit_block_add_assignment_op` to handle these operations:
26136ac495dSmrg
26236ac495dSmrg.. code-block:: c
26336ac495dSmrg
26436ac495dSmrg  /* sum += i * i */
26536ac495dSmrg  gcc_jit_block_add_assignment_op (
26636ac495dSmrg    b_loop_body, NULL,
26736ac495dSmrg    sum,
26836ac495dSmrg    GCC_JIT_BINARY_OP_PLUS,
26936ac495dSmrg    gcc_jit_context_new_binary_op (
27036ac495dSmrg      ctxt, NULL,
27136ac495dSmrg      GCC_JIT_BINARY_OP_MULT, the_type,
27236ac495dSmrg      gcc_jit_lvalue_as_rvalue (i),
27336ac495dSmrg      gcc_jit_lvalue_as_rvalue (i)));
27436ac495dSmrg
27536ac495dSmrgThe `i++` can be thought of as `i += 1`, and can thus be handled in
27636ac495dSmrga similar way.  We use :c:func:`gcc_jit_context_one` to get the constant
27736ac495dSmrgvalue `1` (for the relevant type) for the right-hand side
27836ac495dSmrgof the assignment.
27936ac495dSmrg
28036ac495dSmrg.. code-block:: c
28136ac495dSmrg
28236ac495dSmrg  /* i++ */
28336ac495dSmrg  gcc_jit_block_add_assignment_op (
28436ac495dSmrg    b_loop_body, NULL,
28536ac495dSmrg    i,
28636ac495dSmrg    GCC_JIT_BINARY_OP_PLUS,
28736ac495dSmrg    gcc_jit_context_one (ctxt, the_type));
28836ac495dSmrg
28936ac495dSmrg.. note::
29036ac495dSmrg
29136ac495dSmrg  For numeric constants other than 0 or 1, we could use
29236ac495dSmrg  :c:func:`gcc_jit_context_new_rvalue_from_int` and
29336ac495dSmrg  :c:func:`gcc_jit_context_new_rvalue_from_double`.
29436ac495dSmrg
29536ac495dSmrgThe loop body completes by jumping back to the conditional:
29636ac495dSmrg
29736ac495dSmrg.. code-block:: c
29836ac495dSmrg
29936ac495dSmrg  gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond);
30036ac495dSmrg
30136ac495dSmrgFinally, we populate the `b_after_loop` block, reached when the loop
30236ac495dSmrgconditional is false.  We want to generate the equivalent of:
30336ac495dSmrg
30436ac495dSmrg.. code-block:: c
30536ac495dSmrg
30636ac495dSmrg   return sum;
30736ac495dSmrg
30836ac495dSmrgso the block is just one statement:
30936ac495dSmrg
31036ac495dSmrg.. code-block:: c
31136ac495dSmrg
31236ac495dSmrg  /* return sum */
31336ac495dSmrg  gcc_jit_block_end_with_return (
31436ac495dSmrg    b_after_loop,
31536ac495dSmrg    NULL,
31636ac495dSmrg    gcc_jit_lvalue_as_rvalue (sum));
31736ac495dSmrg
31836ac495dSmrg.. note::
31936ac495dSmrg
32036ac495dSmrg   You can intermingle block creation with statement creation,
32136ac495dSmrg   but given that the terminator statements generally include references
32236ac495dSmrg   to other blocks, I find it's clearer to create all the blocks,
32336ac495dSmrg   *then* all the statements.
32436ac495dSmrg
32536ac495dSmrgWe've finished populating the function.  As before, we can now compile it
32636ac495dSmrgto machine code:
32736ac495dSmrg
32836ac495dSmrg.. code-block:: c
32936ac495dSmrg
33036ac495dSmrg   gcc_jit_result *result;
33136ac495dSmrg   result = gcc_jit_context_compile (ctxt);
33236ac495dSmrg
33336ac495dSmrg   typedef int (*loop_test_fn_type) (int);
33436ac495dSmrg   loop_test_fn_type loop_test =
33536ac495dSmrg    (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test");
33636ac495dSmrg   if (!loop_test)
33736ac495dSmrg     goto error;
33836ac495dSmrg   printf ("result: %d", loop_test (10));
33936ac495dSmrg
34036ac495dSmrg.. code-block:: bash
34136ac495dSmrg
34236ac495dSmrg   result: 285
34336ac495dSmrg
34436ac495dSmrg
34536ac495dSmrgVisualizing the control flow graph
34636ac495dSmrg**********************************
34736ac495dSmrg
34836ac495dSmrgYou can see the control flow graph of a function using
34936ac495dSmrg:c:func:`gcc_jit_function_dump_to_dot`:
35036ac495dSmrg
35136ac495dSmrg.. code-block:: c
35236ac495dSmrg
35336ac495dSmrg  gcc_jit_function_dump_to_dot (func, "/tmp/sum-of-squares.dot");
35436ac495dSmrg
35536ac495dSmrggiving a .dot file in GraphViz format.
35636ac495dSmrg
35736ac495dSmrgYou can convert this to an image using `dot`:
35836ac495dSmrg
35936ac495dSmrg.. code-block:: bash
36036ac495dSmrg
36136ac495dSmrg   $ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png
36236ac495dSmrg
36336ac495dSmrgor use a viewer (my preferred one is xdot.py; see
36436ac495dSmrghttps://github.com/jrfonseca/xdot.py; on Fedora you can
36536ac495dSmrginstall it with `yum install python-xdot`):
36636ac495dSmrg
36736ac495dSmrg    .. figure:: sum-of-squares.png
36836ac495dSmrg      :alt: image of a control flow graph
36936ac495dSmrg
37036ac495dSmrgFull example
37136ac495dSmrg************
37236ac495dSmrg
37336ac495dSmrg   .. literalinclude:: ../examples/tut03-sum-of-squares.c
37436ac495dSmrg    :lines: 1-
37536ac495dSmrg    :language: c
37636ac495dSmrg
37736ac495dSmrgBuilding and running it:
37836ac495dSmrg
37936ac495dSmrg.. code-block:: console
38036ac495dSmrg
38136ac495dSmrg  $ gcc \
38236ac495dSmrg      tut03-sum-of-squares.c \
38336ac495dSmrg      -o tut03-sum-of-squares \
38436ac495dSmrg      -lgccjit
38536ac495dSmrg
38636ac495dSmrg  # Run the built program:
38736ac495dSmrg  $ ./tut03-sum-of-squares
38836ac495dSmrg  loop_test returned: 285
389