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