tutorial03.rst revision 1.5
1.. Copyright (C) 2014-2019 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 18.. default-domain:: cpp 19 20Tutorial part 3: Loops and variables 21------------------------------------ 22Consider this C function: 23 24 .. code-block:: c 25 26 int loop_test (int n) 27 { 28 int sum = 0; 29 for (int i = 0; i < n; i++) 30 sum += i * i; 31 return sum; 32 } 33 34This example demonstrates some more features of libgccjit, with local 35variables and a loop. 36 37To break this down into libgccjit terms, it's usually easier to reword 38the `for` loop as a `while` loop, giving: 39 40 .. code-block:: c 41 42 int loop_test (int n) 43 { 44 int sum = 0; 45 int i = 0; 46 while (i < n) 47 { 48 sum += i * i; 49 i++; 50 } 51 return sum; 52 } 53 54Here's what the final control flow graph will look like: 55 56 .. figure:: ../../intro/sum-of-squares.png 57 :alt: image of a control flow graph 58 59As before, we include the libgccjit++ header and make a 60:type:`gccjit::context`. 61 62.. code-block:: c++ 63 64 #include <libgccjit++.h> 65 66 void test (void) 67 { 68 gccjit::context ctxt; 69 ctxt = gccjit::context::acquire (); 70 71The function works with the C `int` type. 72 73In the previous tutorial we acquired this via 74 75.. code-block:: c++ 76 77 gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_INT); 78 79though we could equally well make it work on, say, `double`: 80 81.. code-block:: c++ 82 83 gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_DOUBLE); 84 85For integer types we can use :func:`gccjit::context::get_int_type<T>` 86to directly bind a specific type: 87 88.. code-block:: c++ 89 90 gccjit::type the_type = ctxt.get_int_type <int> (); 91 92Let's build the function: 93 94.. code-block:: c++ 95 96 gcc_jit_param n = ctxt.new_param (the_type, "n"); 97 std::vector<gccjit::param> params; 98 params.push_back (n); 99 gccjit::function func = 100 ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, 101 return_type, 102 "loop_test", 103 params, 0); 104 105Expressions: lvalues and rvalues 106******************************** 107 108The base class of expression is the :type:`gccjit::rvalue`, 109representing an expression that can be on the *right*-hand side of 110an assignment: a value that can be computed somehow, and assigned 111*to* a storage area (such as a variable). It has a specific 112:type:`gccjit::type`. 113 114Anothe important class is :type:`gccjit::lvalue`. 115A :type:`gccjit::lvalue`. is something that can of the *left*-hand 116side of an assignment: a storage area (such as a variable). 117 118In other words, every assignment can be thought of as: 119 120.. code-block:: c 121 122 LVALUE = RVALUE; 123 124Note that :type:`gccjit::lvalue` is a subclass of 125:type:`gccjit::rvalue`, where in an assignment of the form: 126 127.. code-block:: c 128 129 LVALUE_A = LVALUE_B; 130 131the `LVALUE_B` implies reading the current value of that storage 132area, assigning it into the `LVALUE_A`. 133 134So far the only expressions we've seen are from the previous tutorial: 135 1361. the multiplication `i * i`: 137 138 .. code-block:: c++ 139 140 gccjit::rvalue expr = 141 ctxt.new_binary_op ( 142 GCC_JIT_BINARY_OP_MULT, int_type, 143 param_i, param_i); 144 145 /* Alternatively, using operator-overloading: */ 146 gccjit::rvalue expr = param_i * param_i; 147 148 which is a :type:`gccjit::rvalue`, and 149 1502. the various function parameters: `param_i` and `param_n`, instances of 151 :type:`gccjit::param`, which is a subclass of :type:`gccjit::lvalue` 152 (and, in turn, of :type:`gccjit::rvalue`): 153 we can both read from and write to function parameters within the 154 body of a function. 155 156Our new example has a new kind of expression: we have two local 157variables. We create them by calling 158:func:`gccjit::function::new_local`, supplying a type and a name: 159 160.. code-block:: c++ 161 162 /* Build locals: */ 163 gccjit::lvalue i = func.new_local (the_type, "i"); 164 gccjit::lvalue sum = func.new_local (the_type, "sum"); 165 166These are instances of :type:`gccjit::lvalue` - they can be read from 167and written to. 168 169Note that there is no precanned way to create *and* initialize a variable 170like in C: 171 172.. code-block:: c 173 174 int i = 0; 175 176Instead, having added the local to the function, we have to separately add 177an assignment of `0` to `local_i` at the beginning of the function. 178 179Control flow 180************ 181 182This function has a loop, so we need to build some basic blocks to 183handle the control flow. In this case, we need 4 blocks: 184 1851. before the loop (initializing the locals) 1862. the conditional at the top of the loop (comparing `i < n`) 1873. the body of the loop 1884. after the loop terminates (`return sum`) 189 190so we create these as :type:`gccjit::block` instances within the 191:type:`gccjit::function`: 192 193.. code-block:: c++ 194 195 gccjit::block b_initial = func.new_block ("initial"); 196 gccjit::block b_loop_cond = func.new_block ("loop_cond"); 197 gccjit::block b_loop_body = func.new_block ("loop_body"); 198 gccjit::block b_after_loop = func.new_block ("after_loop"); 199 200We now populate each block with statements. 201 202The entry block `b_initial` consists of initializations followed by a jump 203to the conditional. We assign `0` to `i` and to `sum`, using 204:func:`gccjit::block::add_assignment` to add 205an assignment statement, and using :func:`gccjit::context::zero` to get 206the constant value `0` for the relevant type for the right-hand side of 207the assignment: 208 209.. code-block:: c++ 210 211 /* sum = 0; */ 212 b_initial.add_assignment (sum, ctxt.zero (the_type)); 213 214 /* i = 0; */ 215 b_initial.add_assignment (i, ctxt.zero (the_type)); 216 217We can then terminate the entry block by jumping to the conditional: 218 219.. code-block:: c++ 220 221 b_initial.end_with_jump (b_loop_cond); 222 223The conditional block is equivalent to the line `while (i < n)` from our 224C example. It contains a single statement: a conditional, which jumps to 225one of two destination blocks depending on a boolean 226:type:`gccjit::rvalue`, in this case the comparison of `i` and `n`. 227 228We could build the comparison using :func:`gccjit::context::new_comparison`: 229 230.. code-block:: c++ 231 232 gccjit::rvalue guard = 233 ctxt.new_comparison (GCC_JIT_COMPARISON_GE, 234 i, n); 235 236and can then use this to add `b_loop_cond`'s sole statement, via 237:func:`gccjit::block::end_with_conditional`: 238 239.. code-block:: c++ 240 241 b_loop_cond.end_with_conditional (guard, 242 b_after_loop, // on_true 243 b_loop_body); // on_false 244 245However :type:`gccjit::rvalue` has overloaded operators for this, so we 246express the conditional as 247 248.. code-block:: c++ 249 250 gccjit::rvalue guard = (i >= n); 251 252and hence we can write the block more concisely as: 253 254.. code-block:: c++ 255 256 b_loop_cond.end_with_conditional ( 257 i >= n, 258 b_after_loop, // on_true 259 b_loop_body); // on_false 260 261Next, we populate the body of the loop. 262 263The C statement `sum += i * i;` is an assignment operation, where an 264lvalue is modified "in-place". We use 265:func:`gccjit::block::add_assignment_op` to handle these operations: 266 267.. code-block:: c++ 268 269 /* sum += i * i */ 270 b_loop_body.add_assignment_op (sum, 271 GCC_JIT_BINARY_OP_PLUS, 272 i * i); 273 274The `i++` can be thought of as `i += 1`, and can thus be handled in 275a similar way. We use :c:func:`gcc_jit_context_one` to get the constant 276value `1` (for the relevant type) for the right-hand side 277of the assignment. 278 279.. code-block:: c++ 280 281 /* i++ */ 282 b_loop_body.add_assignment_op (i, 283 GCC_JIT_BINARY_OP_PLUS, 284 ctxt.one (the_type)); 285 286.. note:: 287 288 For numeric constants other than 0 or 1, we could use 289 :func:`gccjit::context::new_rvalue`, which has overloads 290 for both ``int`` and ``double``. 291 292The loop body completes by jumping back to the conditional: 293 294.. code-block:: c++ 295 296 b_loop_body.end_with_jump (b_loop_cond); 297 298Finally, we populate the `b_after_loop` block, reached when the loop 299conditional is false. We want to generate the equivalent of: 300 301.. code-block:: c++ 302 303 return sum; 304 305so the block is just one statement: 306 307.. code-block:: c++ 308 309 /* return sum */ 310 b_after_loop.end_with_return (sum); 311 312.. note:: 313 314 You can intermingle block creation with statement creation, 315 but given that the terminator statements generally include references 316 to other blocks, I find it's clearer to create all the blocks, 317 *then* all the statements. 318 319We've finished populating the function. As before, we can now compile it 320to machine code: 321 322.. code-block:: c++ 323 324 gcc_jit_result *result; 325 result = ctxt.compile (); 326 327 ctxt.release (); 328 329 if (!result) 330 { 331 fprintf (stderr, "NULL result"); 332 return 1; 333 } 334 335 typedef int (*loop_test_fn_type) (int); 336 loop_test_fn_type loop_test = 337 (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test"); 338 if (!loop_test) 339 { 340 fprintf (stderr, "NULL loop_test"); 341 gcc_jit_result_release (result); 342 return 1; 343 } 344 printf ("result: %d", loop_test (10)); 345 346.. code-block:: bash 347 348 result: 285 349 350 351Visualizing the control flow graph 352********************************** 353 354You can see the control flow graph of a function using 355:func:`gccjit::function::dump_to_dot`: 356 357.. code-block:: c++ 358 359 func.dump_to_dot ("/tmp/sum-of-squares.dot"); 360 361giving a .dot file in GraphViz format. 362 363You can convert this to an image using `dot`: 364 365.. code-block:: bash 366 367 $ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png 368 369or use a viewer (my preferred one is xdot.py; see 370https://github.com/jrfonseca/xdot.py; on Fedora you can 371install it with `yum install python-xdot`): 372 373 .. figure:: ../../intro/sum-of-squares.png 374 :alt: image of a control flow graph 375 376Full example 377************ 378 379 .. literalinclude:: ../../examples/tut03-sum-of-squares.cc 380 :lines: 1- 381 :language: c++ 382 383Building and running it: 384 385.. code-block:: console 386 387 $ gcc \ 388 tut03-sum-of-squares.cc \ 389 -o tut03-sum-of-squares \ 390 -lgccjit 391 392 # Run the built program: 393 $ ./tut03-sum-of-squares 394 loop_test returned: 285 395