comparison gcc/jit/docs/cp/intro/tutorial02.rst @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
comparison
equal deleted inserted replaced
68:561a7518be6b 111:04ced10e8804
1 .. Copyright (C) 2014-2017 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
20 Tutorial part 2: Creating a trivial machine code function
21 ---------------------------------------------------------
22
23 Consider this C function:
24
25 .. code-block:: c
26
27 int square (int i)
28 {
29 return i * i;
30 }
31
32 How can we construct this at run-time using libgccjit's C++ API?
33
34 First we need to include the relevant header:
35
36 .. code-block:: c++
37
38 #include <libgccjit++.h>
39
40 All state associated with compilation is associated with a
41 :type:`gccjit::context`, which is a thin C++ wrapper around the C API's
42 :c:type:`gcc_jit_context *`.
43
44 Create one using :func:`gccjit::context::acquire`:
45
46 .. code-block:: c++
47
48 gccjit::context ctxt;
49 ctxt = gccjit::context::acquire ();
50
51 The JIT library has a system of types. It is statically-typed: every
52 expression is of a specific type, fixed at compile-time. In our example,
53 all of the expressions are of the C `int` type, so let's obtain this from
54 the context, as a :type:`gccjit::type`, using
55 :func:`gccjit::context::get_type`:
56
57 .. code-block:: c++
58
59 gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
60
61 :type:`gccjit::type` is an example of a "contextual" object: every
62 entity in the API is associated with a :type:`gccjit::context`.
63
64 Memory management is easy: all such "contextual" objects are automatically
65 cleaned up for you when the context is released, using
66 :func:`gccjit::context::release`:
67
68 .. code-block:: c++
69
70 ctxt.release ();
71
72 so you don't need to manually track and cleanup all objects, just the
73 contexts.
74
75 All of the C++ classes in the API are thin wrappers around pointers to
76 types in the C API.
77
78 The C++ class hierarchy within the ``gccjit`` namespace looks like this::
79
80 +- object
81 +- location
82 +- type
83 +- struct
84 +- field
85 +- function
86 +- block
87 +- rvalue
88 +- lvalue
89 +- param
90
91 One thing you can do with a :type:`gccjit::object` is
92 to ask it for a human-readable description as a :type:`std::string`, using
93 :func:`gccjit::object::get_debug_string`:
94
95 .. code-block:: c++
96
97 printf ("obj: %s\n", obj.get_debug_string ().c_str ());
98
99 giving this text on stdout:
100
101 .. code-block:: bash
102
103 obj: int
104
105 This is invaluable when debugging.
106
107 Let's create the function. To do so, we first need to construct
108 its single parameter, specifying its type and giving it a name,
109 using :func:`gccjit::context::new_param`:
110
111 .. code-block:: c++
112
113 gccjit::param param_i = ctxt.new_param (int_type, "i");
114
115 and we can then make a vector of all of the params of the function,
116 in this case just one:
117
118 .. code-block:: c++
119
120 std::vector<gccjit::param> params;
121 params.push_back (param_i);
122
123 Now we can create the function, using
124 :c:func:`gccjit::context::new_function`:
125
126 .. code-block:: c++
127
128 gccjit::function func =
129 ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
130 int_type,
131 "square",
132 params,
133 0);
134
135 To define the code within the function, we must create basic blocks
136 containing statements.
137
138 Every basic block contains a list of statements, eventually terminated
139 by a statement that either returns, or jumps to another basic block.
140
141 Our function has no control-flow, so we just need one basic block:
142
143 .. code-block:: c++
144
145 gccjit::block block = func.new_block ();
146
147 Our basic block is relatively simple: it immediately terminates by
148 returning the value of an expression.
149
150 We can build the expression using :func:`gccjit::context::new_binary_op`:
151
152 .. code-block:: c++
153
154 gccjit::rvalue expr =
155 ctxt.new_binary_op (
156 GCC_JIT_BINARY_OP_MULT, int_type,
157 param_i, param_i);
158
159 A :type:`gccjit::rvalue` is another example of a
160 :type:`gccjit::object` subclass. As before, we can print it with
161 :func:`gccjit::object::get_debug_string`.
162
163 .. code-block:: c++
164
165 printf ("expr: %s\n", expr.get_debug_string ().c_str ());
166
167 giving this output:
168
169 .. code-block:: bash
170
171 expr: i * i
172
173 Note that :type:`gccjit::rvalue` provides numerous overloaded operators
174 which can be used to dramatically reduce the amount of typing needed.
175 We can build the above binary operation more directly with this one-liner:
176
177 .. code-block:: c++
178
179 gccjit::rvalue expr = param_i * param_i;
180
181 Creating the expression in itself doesn't do anything; we have to add
182 this expression to a statement within the block. In this case, we use it
183 to build a return statement, which terminates the basic block:
184
185 .. code-block:: c++
186
187 block.end_with_return (expr);
188
189 OK, we've populated the context. We can now compile it using
190 :func:`gccjit::context::compile`:
191
192 .. code-block:: c++
193
194 gcc_jit_result *result;
195 result = ctxt.compile ();
196
197 and get a :c:type:`gcc_jit_result *`.
198
199 We can now use :c:func:`gcc_jit_result_get_code` to look up a specific
200 machine code routine within the result, in this case, the function we
201 created above.
202
203 .. code-block:: c++
204
205 void *fn_ptr = gcc_jit_result_get_code (result, "square");
206 if (!fn_ptr)
207 {
208 fprintf (stderr, "NULL fn_ptr");
209 goto error;
210 }
211
212 We can now cast the pointer to an appropriate function pointer type, and
213 then call it:
214
215 .. code-block:: c++
216
217 typedef int (*fn_type) (int);
218 fn_type square = (fn_type)fn_ptr;
219 printf ("result: %d", square (5));
220
221 .. code-block:: bash
222
223 result: 25
224
225
226 Options
227 *******
228
229 To get more information on what's going on, you can set debugging flags
230 on the context using :func:`gccjit::context::set_bool_option`.
231
232 .. (I'm deliberately not mentioning
233 :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think
234 it's probably more of use to implementors than to users)
235
236 Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a
237 C-like representation to stderr when you compile (GCC's "GIMPLE"
238 representation):
239
240 .. code-block:: c++
241
242 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1);
243 result = ctxt.compile ();
244
245 .. code-block:: c
246
247 square (signed int i)
248 {
249 signed int D.260;
250
251 entry:
252 D.260 = i * i;
253 return D.260;
254 }
255
256 We can see the generated machine code in assembler form (on stderr) by
257 setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context
258 before compiling:
259
260 .. code-block:: c++
261
262 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1);
263 result = ctxt.compile ();
264
265 .. code-block:: gas
266
267 .file "fake.c"
268 .text
269 .globl square
270 .type square, @function
271 square:
272 .LFB6:
273 .cfi_startproc
274 pushq %rbp
275 .cfi_def_cfa_offset 16
276 .cfi_offset 6, -16
277 movq %rsp, %rbp
278 .cfi_def_cfa_register 6
279 movl %edi, -4(%rbp)
280 .L14:
281 movl -4(%rbp), %eax
282 imull -4(%rbp), %eax
283 popq %rbp
284 .cfi_def_cfa 7, 8
285 ret
286 .cfi_endproc
287 .LFE6:
288 .size square, .-square
289 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
290 .section .note.GNU-stack,"",@progbits
291
292 By default, no optimizations are performed, the equivalent of GCC's
293 `-O0` option. We can turn things up to e.g. `-O3` by calling
294 :func:`gccjit::context::set_int_option` with
295 :c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`:
296
297 .. code-block:: c++
298
299 ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
300
301 .. code-block:: gas
302
303 .file "fake.c"
304 .text
305 .p2align 4,,15
306 .globl square
307 .type square, @function
308 square:
309 .LFB7:
310 .cfi_startproc
311 .L16:
312 movl %edi, %eax
313 imull %edi, %eax
314 ret
315 .cfi_endproc
316 .LFE7:
317 .size square, .-square
318 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
319 .section .note.GNU-stack,"",@progbits
320
321 Naturally this has only a small effect on such a trivial function.
322
323
324 Full example
325 ************
326
327 Here's what the above looks like as a complete program:
328
329 .. literalinclude:: ../../examples/tut02-square.cc
330 :lines: 1-
331 :language: c++
332
333 Building and running it:
334
335 .. code-block:: console
336
337 $ gcc \
338 tut02-square.cc \
339 -o tut02-square \
340 -lgccjit
341
342 # Run the built program:
343 $ ./tut02-square
344 result: 25