]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/jit/docs/cp/intro/tutorial02.rst
Update copyright years.
[thirdparty/gcc.git] / gcc / jit / docs / cp / intro / tutorial02.rst
CommitLineData
a945c346 1.. Copyright (C) 2014-2024 Free Software Foundation, Inc.
29df5715
DM
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
786973ce 16 <https://www.gnu.org/licenses/>.
29df5715
DM
17
18.. default-domain:: cpp
19
20Tutorial part 2: Creating a trivial machine code function
21---------------------------------------------------------
22
23Consider this C function:
24
25.. code-block:: c
26
27 int square (int i)
28 {
29 return i * i;
30 }
31
32How can we construct this at run-time using libgccjit's C++ API?
33
34First we need to include the relevant header:
35
36.. code-block:: c++
37
38 #include <libgccjit++.h>
39
40All state associated with compilation is associated with a
41:type:`gccjit::context`, which is a thin C++ wrapper around the C API's
85c943f3 42:c:expr:`gcc_jit_context *`.
29df5715
DM
43
44Create one using :func:`gccjit::context::acquire`:
45
46.. code-block:: c++
47
48 gccjit::context ctxt;
49 ctxt = gccjit::context::acquire ();
50
51The JIT library has a system of types. It is statically-typed: every
52expression is of a specific type, fixed at compile-time. In our example,
53all of the expressions are of the C `int` type, so let's obtain this from
54the 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
62entity in the API is associated with a :type:`gccjit::context`.
63
64Memory management is easy: all such "contextual" objects are automatically
65cleaned up for you when the context is released, using
66:func:`gccjit::context::release`:
67
68.. code-block:: c++
69
70 ctxt.release ();
71
72so you don't need to manually track and cleanup all objects, just the
73contexts.
74
75All of the C++ classes in the API are thin wrappers around pointers to
76types in the C API.
77
78The 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
91One thing you can do with a :type:`gccjit::object` is
92to 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
99giving this text on stdout:
100
101.. code-block:: bash
102
103 obj: int
104
105This is invaluable when debugging.
106
107Let's create the function. To do so, we first need to construct
108its single parameter, specifying its type and giving it a name,
109using :func:`gccjit::context::new_param`:
110
111.. code-block:: c++
112
113 gccjit::param param_i = ctxt.new_param (int_type, "i");
114
115and we can then make a vector of all of the params of the function,
116in this case just one:
117
118.. code-block:: c++
119
120 std::vector<gccjit::param> params;
121 params.push_back (param_i);
122
123Now we can create the function, using
a8a282d5 124:cpp:func:`gccjit::context::new_function`:
29df5715
DM
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
135To define the code within the function, we must create basic blocks
136containing statements.
137
138Every basic block contains a list of statements, eventually terminated
139by a statement that either returns, or jumps to another basic block.
140
141Our 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
147Our basic block is relatively simple: it immediately terminates by
148returning the value of an expression.
149
150We 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
159A :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
167giving this output:
168
169.. code-block:: bash
170
171 expr: i * i
172
173Note that :type:`gccjit::rvalue` provides numerous overloaded operators
174which can be used to dramatically reduce the amount of typing needed.
175We 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
181Creating the expression in itself doesn't do anything; we have to add
182this expression to a statement within the block. In this case, we use it
183to build a return statement, which terminates the basic block:
184
185.. code-block:: c++
186
187 block.end_with_return (expr);
188
189OK, 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
85c943f3 197and get a :c:expr:`gcc_jit_result *`.
29df5715
DM
198
199We can now use :c:func:`gcc_jit_result_get_code` to look up a specific
200machine code routine within the result, in this case, the function we
201created 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
212We can now cast the pointer to an appropriate function pointer type, and
213then 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
226Options
227*******
228
229To get more information on what's going on, you can set debugging flags
230on 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
236Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a
237C-like representation to stderr when you compile (GCC's "GIMPLE"
238representation):
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
256We can see the generated machine code in assembler form (on stderr) by
257setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context
258before 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
06520873 289 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2)"
29df5715
DM
290 .section .note.GNU-stack,"",@progbits
291
292By 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
06520873 318 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2)"
29df5715
DM
319 .section .note.GNU-stack,"",@progbits
320
321Naturally this has only a small effect on such a trivial function.
322
323
324Full example
325************
326
327Here's what the above looks like as a complete program:
328
329 .. literalinclude:: ../../examples/tut02-square.cc
330 :lines: 1-
331 :language: c++
332
333Building 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