]>
Commit | Line | Data |
---|---|---|
f1717362 | 1 | .. Copyright (C) 2014-2016 Free Software Foundation, Inc. |
36b809a0 | 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 | ||
20 | Tutorial part 3: Loops and variables | |
21 | ------------------------------------ | |
22 | Consider 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 | ||
34 | This example demonstrates some more features of libgccjit, with local | |
35 | variables and a loop. | |
36 | ||
37 | To break this down into libgccjit terms, it's usually easier to reword | |
38 | the `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 | ||
54 | Here'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 | ||
59 | As 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 | ||
71 | The function works with the C `int` type. | |
72 | ||
73 | In 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 | ||
79 | though 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 | ||
85 | For integer types we can use :func:`gccjit::context::get_int_type<T>` | |
86 | to directly bind a specific type: | |
87 | ||
88 | .. code-block:: c++ | |
89 | ||
90 | gccjit::type the_type = ctxt.get_int_type <int> (); | |
91 | ||
92 | Let'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 | ||
105 | Expressions: lvalues and rvalues | |
106 | ******************************** | |
107 | ||
108 | The base class of expression is the :type:`gccjit::rvalue`, | |
109 | representing an expression that can be on the *right*-hand side of | |
110 | an 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 | ||
114 | Anothe important class is :type:`gccjit::lvalue`. | |
115 | A :type:`gccjit::lvalue`. is something that can of the *left*-hand | |
116 | side of an assignment: a storage area (such as a variable). | |
117 | ||
118 | In other words, every assignment can be thought of as: | |
119 | ||
120 | .. code-block:: c | |
121 | ||
122 | LVALUE = RVALUE; | |
123 | ||
124 | Note 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 | ||
131 | the `LVALUE_B` implies reading the current value of that storage | |
132 | area, assigning it into the `LVALUE_A`. | |
133 | ||
134 | So far the only expressions we've seen are from the previous tutorial: | |
135 | ||
136 | 1. 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 | ||
150 | 2. 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 | ||
156 | Our new example has a new kind of expression: we have two local | |
157 | variables. 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 | ||
166 | These are instances of :type:`gccjit::lvalue` - they can be read from | |
167 | and written to. | |
168 | ||
169 | Note that there is no precanned way to create *and* initialize a variable | |
170 | like in C: | |
171 | ||
172 | .. code-block:: c | |
173 | ||
174 | int i = 0; | |
175 | ||
176 | Instead, having added the local to the function, we have to separately add | |
177 | an assignment of `0` to `local_i` at the beginning of the function. | |
178 | ||
179 | Control flow | |
180 | ************ | |
181 | ||
182 | This function has a loop, so we need to build some basic blocks to | |
183 | handle the control flow. In this case, we need 4 blocks: | |
184 | ||
185 | 1. before the loop (initializing the locals) | |
186 | 2. the conditional at the top of the loop (comparing `i < n`) | |
187 | 3. the body of the loop | |
188 | 4. after the loop terminates (`return sum`) | |
189 | ||
190 | so 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 | ||
200 | We now populate each block with statements. | |
201 | ||
202 | The entry block `b_initial` consists of initializations followed by a jump | |
203 | to the conditional. We assign `0` to `i` and to `sum`, using | |
204 | :func:`gccjit::block::add_assignment` to add | |
205 | an assignment statement, and using :func:`gccjit::context::zero` to get | |
206 | the constant value `0` for the relevant type for the right-hand side of | |
207 | the 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 | ||
217 | We 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 | ||
223 | The conditional block is equivalent to the line `while (i < n)` from our | |
224 | C example. It contains a single statement: a conditional, which jumps to | |
225 | one of two destination blocks depending on a boolean | |
226 | :type:`gccjit::rvalue`, in this case the comparison of `i` and `n`. | |
227 | ||
228 | We 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 | ||
236 | and 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 | ||
625691b3 | 241 | b_loop_cond.end_with_conditional (guard, |
242 | b_after_loop, // on_true | |
243 | b_loop_body); // on_false | |
36b809a0 | 244 | |
245 | However :type:`gccjit::rvalue` has overloaded operators for this, so we | |
246 | express the conditional as | |
247 | ||
248 | .. code-block:: c++ | |
249 | ||
250 | gccjit::rvalue guard = (i >= n); | |
251 | ||
625691b3 | 252 | and hence we can write the block more concisely as: |
36b809a0 | 253 | |
254 | .. code-block:: c++ | |
255 | ||
256 | b_loop_cond.end_with_conditional ( | |
257 | i >= n, | |
625691b3 | 258 | b_after_loop, // on_true |
259 | b_loop_body); // on_false | |
36b809a0 | 260 | |
261 | Next, we populate the body of the loop. | |
262 | ||
263 | The C statement `sum += i * i;` is an assignment operation, where an | |
264 | lvalue 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 | ||
274 | The `i++` can be thought of as `i += 1`, and can thus be handled in | |
275 | a similar way. We use :c:func:`gcc_jit_context_one` to get the constant | |
276 | value `1` (for the relevant type) for the right-hand side | |
277 | of 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 | ||
292 | The 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 | ||
298 | Finally, we populate the `b_after_loop` block, reached when the loop | |
299 | conditional is false. We want to generate the equivalent of: | |
300 | ||
301 | .. code-block:: c++ | |
302 | ||
303 | return sum; | |
304 | ||
305 | so 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 | ||
319 | We've finished populating the function. As before, we can now compile it | |
320 | to 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 | ||
351 | Visualizing the control flow graph | |
352 | ********************************** | |
353 | ||
354 | You 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 | ||
361 | giving a .dot file in GraphViz format. | |
362 | ||
363 | You 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 | ||
369 | or use a viewer (my preferred one is xdot.py; see | |
370 | https://github.com/jrfonseca/xdot.py; on Fedora you can | |
371 | install it with `yum install python-xdot`): | |
372 | ||
373 | .. figure:: ../../intro/sum-of-squares.png | |
374 | :alt: image of a control flow graph | |
375 | ||
376 | Full example | |
377 | ************ | |
378 | ||
379 | .. literalinclude:: ../../examples/tut03-sum-of-squares.cc | |
380 | :lines: 1- | |
381 | :language: c++ | |
382 | ||
383 | Building 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 |