Mercurial > hg > CbC > CbC_gcc
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 |