131
|
1 .. Copyright (C) 2014-2018 Free Software Foundation, Inc.
|
111
|
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 Tutorial part 3: Loops and variables
|
|
19 ------------------------------------
|
|
20 Consider 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
|
|
32 This example demonstrates some more features of libgccjit, with local
|
|
33 variables and a loop.
|
|
34
|
|
35 To break this down into libgccjit terms, it's usually easier to reword
|
|
36 the `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
|
|
52 Here'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
|
|
57 As 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
|
|
69 The 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
|
|
77 though 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
|
|
84 Let'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
|
|
98 Expressions: lvalues and rvalues
|
|
99 ********************************
|
|
100
|
|
101 The base class of expression is the :c:type:`gcc_jit_rvalue *`,
|
|
102 representing an expression that can be on the *right*-hand side of
|
|
103 an 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
|
|
107 Anothe important class is :c:type:`gcc_jit_lvalue *`.
|
|
108 A :c:type:`gcc_jit_lvalue *`. is something that can of the *left*-hand
|
|
109 side of an assignment: a storage area (such as a variable).
|
|
110
|
|
111 In other words, every assignment can be thought of as:
|
|
112
|
|
113 .. code-block:: c
|
|
114
|
|
115 LVALUE = RVALUE;
|
|
116
|
|
117 Note 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
|
|
124 the `LVALUE_B` implies reading the current value of that storage
|
|
125 area, assigning it into the `LVALUE_A`.
|
|
126
|
|
127 So 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
|
|
138 which is a :c:type:`gcc_jit_rvalue *`, and the various function
|
|
139 parameters: `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 *`):
|
|
142 we can both read from and write to function parameters within the
|
|
143 body of a function.
|
|
144
|
|
145 Our new example has a couple of local variables. We create them by
|
|
146 calling :c:func:`gcc_jit_function_new_local`, supplying a type and a
|
|
147 name:
|
|
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
|
|
157 These are instances of :c:type:`gcc_jit_lvalue *` - they can be read from
|
|
158 and written to.
|
|
159
|
|
160 Note that there is no precanned way to create *and* initialize a variable
|
|
161 like in C:
|
|
162
|
|
163 .. code-block:: c
|
|
164
|
|
165 int i = 0;
|
|
166
|
|
167 Instead, having added the local to the function, we have to separately add
|
|
168 an assignment of `0` to `local_i` at the beginning of the function.
|
|
169
|
|
170 Control flow
|
|
171 ************
|
|
172
|
|
173 This function has a loop, so we need to build some basic blocks to
|
|
174 handle the control flow. In this case, we need 4 blocks:
|
|
175
|
|
176 1. before the loop (initializing the locals)
|
|
177 2. the conditional at the top of the loop (comparing `i < n`)
|
|
178 3. the body of the loop
|
|
179 4. after the loop terminates (`return sum`)
|
|
180
|
|
181 so 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
|
|
195 We now populate each block with statements.
|
|
196
|
|
197 The entry block `b_initial` consists of initializations followed by a jump
|
|
198 to the conditional. We assign `0` to `i` and to `sum`, using
|
|
199 :c:func:`gcc_jit_block_add_assignment` to add
|
|
200 an assignment statement, and using :c:func:`gcc_jit_context_zero` to get
|
|
201 the constant value `0` for the relevant type for the right-hand side of
|
|
202 the 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
|
|
218 We 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
|
|
224 The conditional block is equivalent to the line `while (i < n)` from our
|
|
225 C example. It contains a single statement: a conditional, which jumps to
|
|
226 one of two destination blocks depending on a boolean
|
|
227 :c:type:`gcc_jit_rvalue *`, in this case the comparison of `i` and `n`.
|
|
228 We 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
|
|
240 and 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
|
|
256 Next, we populate the body of the loop.
|
|
257
|
|
258 The C statement `sum += i * i;` is an assignment operation, where an
|
|
259 lvalue 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
|
|
275 The `i++` can be thought of as `i += 1`, and can thus be handled in
|
|
276 a similar way. We use :c:func:`gcc_jit_context_one` to get the constant
|
|
277 value `1` (for the relevant type) for the right-hand side
|
|
278 of 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
|
|
295 The 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
|
|
301 Finally, we populate the `b_after_loop` block, reached when the loop
|
|
302 conditional is false. We want to generate the equivalent of:
|
|
303
|
|
304 .. code-block:: c
|
|
305
|
|
306 return sum;
|
|
307
|
|
308 so 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
|
|
325 We've finished populating the function. As before, we can now compile it
|
|
326 to 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
|
|
345 Visualizing the control flow graph
|
|
346 **********************************
|
|
347
|
|
348 You 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
|
|
355 giving a .dot file in GraphViz format.
|
|
356
|
|
357 You 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
|
|
363 or use a viewer (my preferred one is xdot.py; see
|
|
364 https://github.com/jrfonseca/xdot.py; on Fedora you can
|
|
365 install it with `yum install python-xdot`):
|
|
366
|
|
367 .. figure:: sum-of-squares.png
|
|
368 :alt: image of a control flow graph
|
|
369
|
|
370 Full example
|
|
371 ************
|
|
372
|
|
373 .. literalinclude:: ../examples/tut03-sum-of-squares.c
|
|
374 :lines: 1-
|
|
375 :language: c
|
|
376
|
|
377 Building 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
|