tutorial03.rst revision 1.4
1.. Copyright (C) 2014-2018 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