1.. Copyright (C) 2014-2022 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   <https://www.gnu.org/licenses/>.
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
42:c:type:`gcc_jit_context *`.
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
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
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
197and get a :c:type:`gcc_jit_result *`.
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
289        .ident  "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
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
318        .ident  "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
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
345