Mercurial > hg > CbC > CbC_gcc
diff gcc/jit/docs/cp/intro/tutorial03.rst @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/jit/docs/cp/intro/tutorial03.rst Fri Oct 27 22:46:09 2017 +0900 @@ -0,0 +1,394 @@ +.. Copyright (C) 2014-2017 Free Software Foundation, Inc. + Originally contributed by David Malcolm <dmalcolm@redhat.com> + + This is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see + <http://www.gnu.org/licenses/>. + +.. default-domain:: cpp + +Tutorial part 3: Loops and variables +------------------------------------ +Consider this C function: + + .. code-block:: c + + 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: + + .. code-block:: c + + 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: + + .. figure:: ../../intro/sum-of-squares.png + :alt: image of a control flow graph + +As before, we include the libgccjit++ header and make a +:type:`gccjit::context`. + +.. code-block:: c++ + + #include <libgccjit++.h> + + void test (void) + { + gccjit::context ctxt; + ctxt = gccjit::context::acquire (); + +The function works with the C `int` type. + +In the previous tutorial we acquired this via + +.. code-block:: c++ + + gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_INT); + +though we could equally well make it work on, say, `double`: + +.. code-block:: c++ + + gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_DOUBLE); + +For integer types we can use :func:`gccjit::context::get_int_type<T>` +to directly bind a specific type: + +.. code-block:: c++ + + gccjit::type the_type = ctxt.get_int_type <int> (); + +Let's build the function: + +.. code-block:: c++ + + gcc_jit_param n = ctxt.new_param (the_type, "n"); + std::vector<gccjit::param> params; + params.push_back (n); + gccjit::function func = + ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, + return_type, + "loop_test", + params, 0); + +Expressions: lvalues and rvalues +******************************** + +The base class of expression is the :type:`gccjit::rvalue`, +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 +:type:`gccjit::type`. + +Anothe important class is :type:`gccjit::lvalue`. +A :type:`gccjit::lvalue`. 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: + +.. code-block:: c + + LVALUE = RVALUE; + +Note that :type:`gccjit::lvalue` is a subclass of +:type:`gccjit::rvalue`, where in an assignment of the form: + +.. code-block:: c + + 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 from the previous tutorial: + +1. the multiplication `i * i`: + + .. code-block:: c++ + + gccjit::rvalue expr = + ctxt.new_binary_op ( + GCC_JIT_BINARY_OP_MULT, int_type, + param_i, param_i); + + /* Alternatively, using operator-overloading: */ + gccjit::rvalue expr = param_i * param_i; + + which is a :type:`gccjit::rvalue`, and + +2. the various function parameters: `param_i` and `param_n`, instances of + :type:`gccjit::param`, which is a subclass of :type:`gccjit::lvalue` + (and, in turn, of :type:`gccjit::rvalue`): + we can both read from and write to function parameters within the + body of a function. + +Our new example has a new kind of expression: we have two local +variables. We create them by calling +:func:`gccjit::function::new_local`, supplying a type and a name: + +.. code-block:: c++ + + /* Build locals: */ + gccjit::lvalue i = func.new_local (the_type, "i"); + gccjit::lvalue sum = func.new_local (the_type, "sum"); + +These are instances of :type:`gccjit::lvalue` - they can be read from +and written to. + +Note that there is no precanned way to create *and* initialize a variable +like in C: + +.. code-block:: 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 :type:`gccjit::block` instances within the +:type:`gccjit::function`: + +.. code-block:: c++ + + gccjit::block b_initial = func.new_block ("initial"); + gccjit::block b_loop_cond = func.new_block ("loop_cond"); + gccjit::block b_loop_body = func.new_block ("loop_body"); + gccjit::block b_after_loop = func.new_block ("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 +:func:`gccjit::block::add_assignment` to add +an assignment statement, and using :func:`gccjit::context::zero` to get +the constant value `0` for the relevant type for the right-hand side of +the assignment: + +.. code-block:: c++ + + /* sum = 0; */ + b_initial.add_assignment (sum, ctxt.zero (the_type)); + + /* i = 0; */ + b_initial.add_assignment (i, ctxt.zero (the_type)); + +We can then terminate the entry block by jumping to the conditional: + +.. code-block:: c++ + + b_initial.end_with_jump (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 +:type:`gccjit::rvalue`, in this case the comparison of `i` and `n`. + +We could build the comparison using :func:`gccjit::context::new_comparison`: + +.. code-block:: c++ + + gccjit::rvalue guard = + ctxt.new_comparison (GCC_JIT_COMPARISON_GE, + i, n); + +and can then use this to add `b_loop_cond`'s sole statement, via +:func:`gccjit::block::end_with_conditional`: + +.. code-block:: c++ + + b_loop_cond.end_with_conditional (guard, + b_after_loop, // on_true + b_loop_body); // on_false + +However :type:`gccjit::rvalue` has overloaded operators for this, so we +express the conditional as + +.. code-block:: c++ + + gccjit::rvalue guard = (i >= n); + +and hence we can write the block more concisely as: + +.. code-block:: c++ + + b_loop_cond.end_with_conditional ( + i >= n, + 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 +:func:`gccjit::block::add_assignment_op` to handle these operations: + +.. code-block:: c++ + + /* sum += i * i */ + b_loop_body.add_assignment_op (sum, + GCC_JIT_BINARY_OP_PLUS, + i * i); + +The `i++` can be thought of as `i += 1`, and can thus be handled in +a similar way. We use :c:func:`gcc_jit_context_one` to get the constant +value `1` (for the relevant type) for the right-hand side +of the assignment. + +.. code-block:: c++ + + /* i++ */ + b_loop_body.add_assignment_op (i, + GCC_JIT_BINARY_OP_PLUS, + ctxt.one (the_type)); + +.. note:: + + For numeric constants other than 0 or 1, we could use + :func:`gccjit::context::new_rvalue`, which has overloads + for both ``int`` and ``double``. + +The loop body completes by jumping back to the conditional: + +.. code-block:: c++ + + b_loop_body.end_with_jump (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: + +.. code-block:: c++ + + return sum; + +so the block is just one statement: + +.. code-block:: c++ + + /* return sum */ + b_after_loop.end_with_return (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: + +.. code-block:: c++ + + gcc_jit_result *result; + result = ctxt.compile (); + + ctxt.release (); + + if (!result) + { + fprintf (stderr, "NULL result"); + return 1; + } + + 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"); + gcc_jit_result_release (result); + return 1; + } + printf ("result: %d", loop_test (10)); + +.. code-block:: bash + + result: 285 + + +Visualizing the control flow graph +********************************** + +You can see the control flow graph of a function using +:func:`gccjit::function::dump_to_dot`: + +.. code-block:: c++ + + func.dump_to_dot ("/tmp/sum-of-squares.dot"); + +giving a .dot file in GraphViz format. + +You can convert this to an image using `dot`: + +.. code-block:: bash + + $ 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`): + + .. figure:: ../../intro/sum-of-squares.png + :alt: image of a control flow graph + +Full example +************ + + .. literalinclude:: ../../examples/tut03-sum-of-squares.cc + :lines: 1- + :language: c++ + +Building and running it: + +.. code-block:: console + + $ gcc \ + tut03-sum-of-squares.cc \ + -o tut03-sum-of-squares \ + -lgccjit + + # Run the built program: + $ ./tut03-sum-of-squares + loop_test returned: 285