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