]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/jit/docs/intro/tutorial05.rst
6ab608fa9bb3b1261d8515ac0f8324c399f9d12b
[thirdparty/gcc.git] / gcc / jit / docs / intro / tutorial05.rst
1 .. Copyright (C) 2015-2025 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 Tutorial part 5: Implementing an Ahead-of-Time compiler
19 -------------------------------------------------------
20
21 If you have a pre-existing language frontend that's compatible with
22 libgccjit's license, it's possible to hook it up to libgccjit as a
23 backend. In the previous example we showed
24 how to do that for in-memory JIT-compilation, but libgccjit can also
25 compile code directly to a file, allowing you to implement a more
26 traditional ahead-of-time compiler ("JIT" is something of a misnomer
27 for this use-case).
28
29 The essential difference is to compile the context using
30 :c:func:`gcc_jit_context_compile_to_file` rather than
31 :c:func:`gcc_jit_context_compile`.
32
33 The "brainf" language
34 *********************
35
36 In this example we use libgccjit to construct an ahead-of-time compiler
37 for an esoteric programming language that we shall refer to as "brainf".
38
39 brainf scripts operate on an array of bytes, with a notional data pointer
40 within the array.
41
42 brainf is hard for humans to read, but it's trivial to write a parser for
43 it, as there is no lexing; just a stream of bytes. The operations are:
44
45 .. list-table::
46 :header-rows: 1
47
48 * - Character
49 - Meaning
50
51 * - ``>``
52 - ``idx += 1``
53 * - ``<``
54 - ``idx -= 1``
55 * - ``+``
56 - ``data[idx] += 1``
57 * - ``-``
58 - ``data[idx] -= 1``
59 * - ``.``
60 - ``output (data[idx])``
61 * - ``,``
62 - ``data[idx] = input ()``
63 * - ``[``
64 - loop until ``data[idx] == 0``
65 * - ``]``
66 - end of loop
67 * - Anything else
68 - ignored
69
70 Unlike the previous example, we'll implement an ahead-of-time compiler,
71 which reads ``.bf`` scripts and outputs executables (though it would
72 be trivial to have it run them JIT-compiled in-process).
73
74 Here's what a simple ``.bf`` script looks like:
75
76 .. literalinclude:: ../examples/emit-alphabet.bf
77 :lines: 1-
78
79 .. note::
80
81 This example makes use of whitespace and comments for legibility, but
82 could have been written as::
83
84 ++++++++++++++++++++++++++
85 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
86 [>.+<-]
87
88 It's not a particularly useful language, except for providing
89 compiler-writers with a test case that's easy to parse. The point
90 is that you can use :c:func:`gcc_jit_context_compile_to_file`
91 to use libgccjit as a backend for a pre-existing language frontend
92 (provided that the pre-existing frontend is compatible with libgccjit's
93 license).
94
95 Converting a brainf script to libgccjit IR
96 ******************************************
97
98 As before we write simple code to populate a :c:expr:`gcc_jit_context *`.
99
100 .. literalinclude:: ../examples/tut05-bf.c
101 :start-after: #define MAX_OPEN_PARENS 16
102 :end-before: /* Entrypoint to the compiler. */
103 :language: c
104
105 Compiling a context to a file
106 *****************************
107
108 Unlike the previous tutorial, this time we'll compile the context
109 directly to an executable, using :c:func:`gcc_jit_context_compile_to_file`:
110
111 .. code-block:: c
112
113 gcc_jit_context_compile_to_file (ctxt,
114 GCC_JIT_OUTPUT_KIND_EXECUTABLE,
115 output_file);
116
117 Here's the top-level of the compiler, which is what actually calls into
118 :c:func:`gcc_jit_context_compile_to_file`:
119
120 .. literalinclude:: ../examples/tut05-bf.c
121 :start-after: /* Entrypoint to the compiler. */
122 :end-before: /* Use the built compiler to compile the example to an executable:
123 :language: c
124
125 Note how once the context is populated you could trivially instead compile
126 it to memory using :c:func:`gcc_jit_context_compile` and run it in-process
127 as in the previous tutorial.
128
129 To create an executable, we need to export a ``main`` function. Here's
130 how to create one from the JIT API:
131
132 .. literalinclude:: ../examples/tut05-bf.c
133 :start-after: #include "libgccjit.h"
134 :end-before: #define MAX_OPEN_PARENS 16
135 :language: c
136
137 .. note::
138
139 The above implementation ignores ``argc`` and ``argv``, but you could
140 make use of them by exposing ``param_argc`` and ``param_argv`` to the
141 caller.
142
143 Upon compiling this C code, we obtain a bf-to-machine-code compiler;
144 let's call it ``bfc``:
145
146 .. code-block:: console
147
148 $ gcc \
149 tut05-bf.c \
150 -o bfc \
151 -lgccjit
152
153 We can now use ``bfc`` to compile .bf files into machine code executables:
154
155 .. code-block:: console
156
157 $ ./bfc \
158 emit-alphabet.bf \
159 a.out
160
161 which we can run directly:
162
163 .. code-block:: console
164
165 $ ./a.out
166 ABCDEFGHIJKLMNOPQRSTUVWXYZ
167
168 Success!
169
170 We can also inspect the generated executable using standard tools:
171
172 .. code-block:: console
173
174 $ objdump -d a.out |less
175
176 which shows that libgccjit has managed to optimize the function
177 somewhat (for example, the runs of 26 and 65 increment operations
178 have become integer constants 0x1a and 0x41):
179
180 .. code-block:: console
181
182 0000000000400620 <main>:
183 400620: 80 3d 39 0a 20 00 00 cmpb $0x0,0x200a39(%rip) # 601060 <data
184 400627: 74 07 je 400630 <main
185 400629: eb fe jmp 400629 <main+0x9>
186 40062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
187 400630: 48 83 ec 08 sub $0x8,%rsp
188 400634: 0f b6 05 26 0a 20 00 movzbl 0x200a26(%rip),%eax # 601061 <data_cells+0x1>
189 40063b: c6 05 1e 0a 20 00 1a movb $0x1a,0x200a1e(%rip) # 601060 <data_cells>
190 400642: 8d 78 41 lea 0x41(%rax),%edi
191 400645: 40 88 3d 15 0a 20 00 mov %dil,0x200a15(%rip) # 601061 <data_cells+0x1>
192 40064c: 0f 1f 40 00 nopl 0x0(%rax)
193 400650: 40 0f b6 ff movzbl %dil,%edi
194 400654: e8 87 fe ff ff callq 4004e0 <putchar@plt>
195 400659: 0f b6 05 01 0a 20 00 movzbl 0x200a01(%rip),%eax # 601061 <data_cells+0x1>
196 400660: 80 2d f9 09 20 00 01 subb $0x1,0x2009f9(%rip) # 601060 <data_cells>
197 400667: 8d 78 01 lea 0x1(%rax),%edi
198 40066a: 40 88 3d f0 09 20 00 mov %dil,0x2009f0(%rip) # 601061 <data_cells+0x1>
199 400671: 75 dd jne 400650 <main+0x30>
200 400673: 31 c0 xor %eax,%eax
201 400675: 48 83 c4 08 add $0x8,%rsp
202 400679: c3 retq
203 40067a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
204
205 We also set up debugging information (via
206 :c:func:`gcc_jit_context_new_location` and
207 :c:macro:`GCC_JIT_BOOL_OPTION_DEBUGINFO`), so it's possible to use ``gdb``
208 to singlestep through the generated binary and inspect the internal
209 state ``idx`` and ``data_cells``:
210
211 .. code-block:: console
212
213 (gdb) break main
214 Breakpoint 1 at 0x400790
215 (gdb) run
216 Starting program: a.out
217
218 Breakpoint 1, 0x0000000000400790 in main (argc=1, argv=0x7fffffffe448)
219 (gdb) stepi
220 0x0000000000400797 in main (argc=1, argv=0x7fffffffe448)
221 (gdb) stepi
222 0x00000000004007a0 in main (argc=1, argv=0x7fffffffe448)
223 (gdb) stepi
224 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
225 (gdb) list
226 4
227 5 cell 0 = 26
228 6 ++++++++++++++++++++++++++
229 7
230 8 cell 1 = 65
231 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
232 10
233 11 while cell#0 != 0
234 12 [
235 13 >
236 (gdb) n
237 6 ++++++++++++++++++++++++++
238 (gdb) n
239 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
240 (gdb) p idx
241 $1 = 1
242 (gdb) p data_cells
243 $2 = "\032", '\000' <repeats 29998 times>
244 (gdb) p data_cells[0]
245 $3 = 26 '\032'
246 (gdb) p data_cells[1]
247 $4 = 0 '\000'
248 (gdb) list
249 4
250 5 cell 0 = 26
251 6 ++++++++++++++++++++++++++
252 7
253 8 cell 1 = 65
254 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
255 10
256 11 while cell#0 != 0
257 12 [
258 13 >
259
260
261 Other forms of ahead-of-time-compilation
262 ****************************************
263
264 The above demonstrates compiling a :c:expr:`gcc_jit_context *` directly
265 to an executable. It's also possible to compile it to an object file,
266 and to a dynamic library. See the documentation of
267 :c:func:`gcc_jit_context_compile_to_file` for more information.