annotate gcc/jit/docs/cp/intro/tutorial04.rst @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 .. Copyright (C) 2014-2017 Free Software Foundation, Inc.
kono
parents:
diff changeset
2 Originally contributed by David Malcolm <dmalcolm@redhat.com>
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 This is free software: you can redistribute it and/or modify it
kono
parents:
diff changeset
5 under the terms of the GNU General Public License as published by
kono
parents:
diff changeset
6 the Free Software Foundation, either version 3 of the License, or
kono
parents:
diff changeset
7 (at your option) any later version.
kono
parents:
diff changeset
8
kono
parents:
diff changeset
9 This program is distributed in the hope that it will be useful, but
kono
parents:
diff changeset
10 WITHOUT ANY WARRANTY; without even the implied warranty of
kono
parents:
diff changeset
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
kono
parents:
diff changeset
12 General Public License for more details.
kono
parents:
diff changeset
13
kono
parents:
diff changeset
14 You should have received a copy of the GNU General Public License
kono
parents:
diff changeset
15 along with this program. If not, see
kono
parents:
diff changeset
16 <http://www.gnu.org/licenses/>.
kono
parents:
diff changeset
17
kono
parents:
diff changeset
18 .. default-domain:: cpp
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 Tutorial part 4: Adding JIT-compilation to a toy interpreter
kono
parents:
diff changeset
21 ------------------------------------------------------------
kono
parents:
diff changeset
22 In this example we construct a "toy" interpreter, and add JIT-compilation
kono
parents:
diff changeset
23 to it.
kono
parents:
diff changeset
24
kono
parents:
diff changeset
25 Our toy interpreter
kono
parents:
diff changeset
26 *******************
kono
parents:
diff changeset
27
kono
parents:
diff changeset
28 It's a stack-based interpreter, and is intended as a (very simple) example
kono
parents:
diff changeset
29 of the kind of bytecode interpreter seen in dynamic languages such as
kono
parents:
diff changeset
30 Python, Ruby etc.
kono
parents:
diff changeset
31
kono
parents:
diff changeset
32 For the sake of simplicity, our toy virtual machine is very limited:
kono
parents:
diff changeset
33
kono
parents:
diff changeset
34 * The only data type is `int`
kono
parents:
diff changeset
35
kono
parents:
diff changeset
36 * It can only work on one function at a time (so that the only
kono
parents:
diff changeset
37 function call that can be made is to recurse).
kono
parents:
diff changeset
38
kono
parents:
diff changeset
39 * Functions can only take one parameter.
kono
parents:
diff changeset
40
kono
parents:
diff changeset
41 * Functions have a stack of `int` values.
kono
parents:
diff changeset
42
kono
parents:
diff changeset
43 * We'll implement function call within the interpreter by calling a
kono
parents:
diff changeset
44 function in our implementation, rather than implementing our own
kono
parents:
diff changeset
45 frame stack.
kono
parents:
diff changeset
46
kono
parents:
diff changeset
47 * The parser is only good enough to get the examples to work.
kono
parents:
diff changeset
48
kono
parents:
diff changeset
49 Naturally, a real interpreter would be much more complicated that this.
kono
parents:
diff changeset
50
kono
parents:
diff changeset
51 The following operations are supported:
kono
parents:
diff changeset
52
kono
parents:
diff changeset
53 ====================== ======================== =============== ==============
kono
parents:
diff changeset
54 Operation Meaning Old Stack New Stack
kono
parents:
diff changeset
55 ====================== ======================== =============== ==============
kono
parents:
diff changeset
56 DUP Duplicate top of stack. ``[..., x]`` ``[..., x, x]``
kono
parents:
diff changeset
57 ROT Swap top two elements ``[..., x, y]`` ``[..., y, x]``
kono
parents:
diff changeset
58 of stack.
kono
parents:
diff changeset
59 BINARY_ADD Add the top two elements ``[..., x, y]`` ``[..., (x+y)]``
kono
parents:
diff changeset
60 on the stack.
kono
parents:
diff changeset
61 BINARY_SUBTRACT Likewise, but subtract. ``[..., x, y]`` ``[..., (x-y)]``
kono
parents:
diff changeset
62 BINARY_MULT Likewise, but multiply. ``[..., x, y]`` ``[..., (x*y)]``
kono
parents:
diff changeset
63 BINARY_COMPARE_LT Compare the top two ``[..., x, y]`` ``[..., (x<y)]``
kono
parents:
diff changeset
64 elements on the stack
kono
parents:
diff changeset
65 and push a nonzero/zero
kono
parents:
diff changeset
66 if (x<y).
kono
parents:
diff changeset
67 RECURSE Recurse, passing the top ``[..., x]`` ``[..., fn(x)]``
kono
parents:
diff changeset
68 of the stack, and
kono
parents:
diff changeset
69 popping the result.
kono
parents:
diff changeset
70 RETURN Return the top of the ``[x]`` ``[]``
kono
parents:
diff changeset
71 stack.
kono
parents:
diff changeset
72 PUSH_CONST `arg` Push an int const. ``[...]`` ``[..., arg]``
kono
parents:
diff changeset
73 JUMP_ABS_IF_TRUE `arg` Pop; if top of stack was ``[..., x]`` ``[...]``
kono
parents:
diff changeset
74 nonzero, jump to
kono
parents:
diff changeset
75 ``arg``.
kono
parents:
diff changeset
76 ====================== ======================== =============== ==============
kono
parents:
diff changeset
77
kono
parents:
diff changeset
78 Programs can be interpreted, disassembled, and compiled to machine code.
kono
parents:
diff changeset
79
kono
parents:
diff changeset
80 The interpreter reads ``.toy`` scripts. Here's what a simple recursive
kono
parents:
diff changeset
81 factorial program looks like, the script ``factorial.toy``.
kono
parents:
diff changeset
82 The parser ignores lines beginning with a `#`.
kono
parents:
diff changeset
83
kono
parents:
diff changeset
84 .. literalinclude:: ../../examples/tut04-toyvm/factorial.toy
kono
parents:
diff changeset
85 :lines: 1-
kono
parents:
diff changeset
86 :language: c
kono
parents:
diff changeset
87
kono
parents:
diff changeset
88 The interpreter is a simple infinite loop with a big ``switch`` statement
kono
parents:
diff changeset
89 based on what the next opcode is:
kono
parents:
diff changeset
90
kono
parents:
diff changeset
91 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
92 :start-after: /* Execute the given function. */
kono
parents:
diff changeset
93 :end-before: /* JIT compilation. */
kono
parents:
diff changeset
94 :language: c++
kono
parents:
diff changeset
95
kono
parents:
diff changeset
96 Compiling to machine code
kono
parents:
diff changeset
97 *************************
kono
parents:
diff changeset
98 We want to generate machine code that can be cast to this type and
kono
parents:
diff changeset
99 then directly executed in-process:
kono
parents:
diff changeset
100
kono
parents:
diff changeset
101 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
102 :start-after: /* Functions are compiled to this function ptr type. */
kono
parents:
diff changeset
103 :end-before: enum opcode
kono
parents:
diff changeset
104 :language: c++
kono
parents:
diff changeset
105
kono
parents:
diff changeset
106 Our compiler isn't very sophisticated; it takes the implementation of
kono
parents:
diff changeset
107 each opcode above, and maps it directly to the operations supported by
kono
parents:
diff changeset
108 the libgccjit API.
kono
parents:
diff changeset
109
kono
parents:
diff changeset
110 How should we handle the stack? In theory we could calculate what the
kono
parents:
diff changeset
111 stack depth will be at each opcode, and optimize away the stack
kono
parents:
diff changeset
112 manipulation "by hand". We'll see below that libgccjit is able to do
kono
parents:
diff changeset
113 this for us, so we'll implement stack manipulation
kono
parents:
diff changeset
114 in a direct way, by creating a ``stack`` array and ``stack_depth``
kono
parents:
diff changeset
115 variables, local within the generated function, equivalent to this C code:
kono
parents:
diff changeset
116
kono
parents:
diff changeset
117 .. code-block:: c
kono
parents:
diff changeset
118
kono
parents:
diff changeset
119 int stack_depth;
kono
parents:
diff changeset
120 int stack[MAX_STACK_DEPTH];
kono
parents:
diff changeset
121
kono
parents:
diff changeset
122 We'll also have local variables ``x`` and ``y`` for use when implementing
kono
parents:
diff changeset
123 the opcodes, equivalent to this:
kono
parents:
diff changeset
124
kono
parents:
diff changeset
125 .. code-block:: c
kono
parents:
diff changeset
126
kono
parents:
diff changeset
127 int x;
kono
parents:
diff changeset
128 int y;
kono
parents:
diff changeset
129
kono
parents:
diff changeset
130 This means our compiler has the following state:
kono
parents:
diff changeset
131
kono
parents:
diff changeset
132 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
133 :start-after: /* State. */
kono
parents:
diff changeset
134 :end-before: };
kono
parents:
diff changeset
135 :language: c++
kono
parents:
diff changeset
136
kono
parents:
diff changeset
137 Setting things up
kono
parents:
diff changeset
138 *****************
kono
parents:
diff changeset
139
kono
parents:
diff changeset
140 First we create our types:
kono
parents:
diff changeset
141
kono
parents:
diff changeset
142 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
143 :start-after: /* Create types. */
kono
parents:
diff changeset
144 :end-before: /* The constant value 1. */
kono
parents:
diff changeset
145 :language: c++
kono
parents:
diff changeset
146
kono
parents:
diff changeset
147 along with extracting a useful `int` constant:
kono
parents:
diff changeset
148
kono
parents:
diff changeset
149 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
150 :start-after: /* The constant value 1. */
kono
parents:
diff changeset
151 :end-before: /* Create locations. */
kono
parents:
diff changeset
152 :language: c++
kono
parents:
diff changeset
153
kono
parents:
diff changeset
154 We'll implement push and pop in terms of the ``stack`` array and
kono
parents:
diff changeset
155 ``stack_depth``. Here are helper functions for adding statements to
kono
parents:
diff changeset
156 a block, implementing pushing and popping values:
kono
parents:
diff changeset
157
kono
parents:
diff changeset
158 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
159 :start-after: /* Stack manipulation. */
kono
parents:
diff changeset
160 :end-before: /* Create the context. */
kono
parents:
diff changeset
161 :language: c++
kono
parents:
diff changeset
162
kono
parents:
diff changeset
163 We will support single-stepping through the generated code in the
kono
parents:
diff changeset
164 debugger, so we need to create :type:`gccjit::location` instances, one
kono
parents:
diff changeset
165 per operation in the source code. These will reference the lines of
kono
parents:
diff changeset
166 e.g. ``factorial.toy``.
kono
parents:
diff changeset
167
kono
parents:
diff changeset
168 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
169 :start-after: /* Create locations. */
kono
parents:
diff changeset
170 :end-before: /* Creating the function. */
kono
parents:
diff changeset
171 :language: c++
kono
parents:
diff changeset
172
kono
parents:
diff changeset
173 Let's create the function itself. As usual, we create its parameter
kono
parents:
diff changeset
174 first, then use the parameter to create the function:
kono
parents:
diff changeset
175
kono
parents:
diff changeset
176 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
177 :start-after: /* Creating the function. */
kono
parents:
diff changeset
178 :end-before: /* Create stack lvalues. */
kono
parents:
diff changeset
179 :language: c++
kono
parents:
diff changeset
180
kono
parents:
diff changeset
181 We create the locals within the function.
kono
parents:
diff changeset
182
kono
parents:
diff changeset
183 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
184 :start-after: /* Create stack lvalues. */
kono
parents:
diff changeset
185 :end-before: /* 1st pass: create blocks, one per opcode.
kono
parents:
diff changeset
186 :language: c++
kono
parents:
diff changeset
187
kono
parents:
diff changeset
188 Populating the function
kono
parents:
diff changeset
189 ***********************
kono
parents:
diff changeset
190
kono
parents:
diff changeset
191 There's some one-time initialization, and the API treats the first block
kono
parents:
diff changeset
192 you create as the entrypoint of the function, so we need to create that
kono
parents:
diff changeset
193 block first:
kono
parents:
diff changeset
194
kono
parents:
diff changeset
195 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
196 :start-after: first. */
kono
parents:
diff changeset
197 :end-before: /* Create a block per operation. */
kono
parents:
diff changeset
198 :language: c++
kono
parents:
diff changeset
199
kono
parents:
diff changeset
200 We can now create blocks for each of the operations. Most of these will
kono
parents:
diff changeset
201 be consolidated into larger blocks when the optimizer runs.
kono
parents:
diff changeset
202
kono
parents:
diff changeset
203 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
204 :start-after: /* Create a block per operation. */
kono
parents:
diff changeset
205 :end-before: /* Populate the initial block. */
kono
parents:
diff changeset
206 :language: c++
kono
parents:
diff changeset
207
kono
parents:
diff changeset
208 Now that we have a block it can jump to when it's done, we can populate
kono
parents:
diff changeset
209 the initial block:
kono
parents:
diff changeset
210
kono
parents:
diff changeset
211 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
212 :start-after: /* Populate the initial block. */
kono
parents:
diff changeset
213 :end-before: /* 2nd pass: fill in instructions. */
kono
parents:
diff changeset
214 :language: c++
kono
parents:
diff changeset
215
kono
parents:
diff changeset
216 We can now populate the blocks for the individual operations. We loop
kono
parents:
diff changeset
217 through them, adding instructions to their blocks:
kono
parents:
diff changeset
218
kono
parents:
diff changeset
219 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
220 :start-after: /* 2nd pass: fill in instructions. */
kono
parents:
diff changeset
221 :end-before: /* Helper macros. */
kono
parents:
diff changeset
222 :language: c++
kono
parents:
diff changeset
223
kono
parents:
diff changeset
224 We're going to have another big ``switch`` statement for implementing
kono
parents:
diff changeset
225 the opcodes, this time for compiling them, rather than interpreting
kono
parents:
diff changeset
226 them. It's helpful to have macros for implementing push and pop, so that
kono
parents:
diff changeset
227 we can make the ``switch`` statement that's coming up look as much as
kono
parents:
diff changeset
228 possible like the one above within the interpreter:
kono
parents:
diff changeset
229
kono
parents:
diff changeset
230 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
231 :start-after: /* Helper macros. */
kono
parents:
diff changeset
232 :end-before: block.add_comment
kono
parents:
diff changeset
233 :language: c++
kono
parents:
diff changeset
234
kono
parents:
diff changeset
235 .. note::
kono
parents:
diff changeset
236
kono
parents:
diff changeset
237 A particularly clever implementation would have an *identical*
kono
parents:
diff changeset
238 ``switch`` statement shared by the interpreter and the compiler, with
kono
parents:
diff changeset
239 some preprocessor "magic". We're not doing that here, for the sake
kono
parents:
diff changeset
240 of simplicity.
kono
parents:
diff changeset
241
kono
parents:
diff changeset
242 When I first implemented this compiler, I accidentally missed an edit
kono
parents:
diff changeset
243 when copying and pasting the ``Y_EQUALS_POP`` macro, so that popping the
kono
parents:
diff changeset
244 stack into ``y`` instead erroneously assigned it to ``x``, leaving ``y``
kono
parents:
diff changeset
245 uninitialized.
kono
parents:
diff changeset
246
kono
parents:
diff changeset
247 To track this kind of thing down, we can use
kono
parents:
diff changeset
248 :func:`gccjit::block::add_comment` to add descriptive comments
kono
parents:
diff changeset
249 to the internal representation. This is invaluable when looking through
kono
parents:
diff changeset
250 the generated IR for, say ``factorial``:
kono
parents:
diff changeset
251
kono
parents:
diff changeset
252 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
253 :start-after: PUSH_RVALUE (y)
kono
parents:
diff changeset
254 :end-before: /* Handle the individual opcodes. */
kono
parents:
diff changeset
255 :language: c++
kono
parents:
diff changeset
256
kono
parents:
diff changeset
257 We can now write the big ``switch`` statement that implements the
kono
parents:
diff changeset
258 individual opcodes, populating the relevant block with statements:
kono
parents:
diff changeset
259
kono
parents:
diff changeset
260 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
261 :start-after: /* Handle the individual opcodes. */
kono
parents:
diff changeset
262 :end-before: /* Go to the next block. */
kono
parents:
diff changeset
263 :language: c++
kono
parents:
diff changeset
264
kono
parents:
diff changeset
265 Every block must be terminated, via a call to one of the
kono
parents:
diff changeset
266 ``gccjit::block::end_with_`` entrypoints. This has been done for two
kono
parents:
diff changeset
267 of the opcodes, but we need to do it for the other ones, by jumping
kono
parents:
diff changeset
268 to the next block.
kono
parents:
diff changeset
269
kono
parents:
diff changeset
270 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
271 :start-after: /* Go to the next block. */
kono
parents:
diff changeset
272 :end-before: /* end of loop on PC locations. */
kono
parents:
diff changeset
273 :language: c++
kono
parents:
diff changeset
274
kono
parents:
diff changeset
275 This is analogous to simply incrementing the program counter.
kono
parents:
diff changeset
276
kono
parents:
diff changeset
277 Verifying the control flow graph
kono
parents:
diff changeset
278 ********************************
kono
parents:
diff changeset
279 Having finished looping over the blocks, the context is complete.
kono
parents:
diff changeset
280
kono
parents:
diff changeset
281 As before, we can verify that the control flow and statements are sane by
kono
parents:
diff changeset
282 using :func:`gccjit::function::dump_to_dot`:
kono
parents:
diff changeset
283
kono
parents:
diff changeset
284 .. code-block:: c++
kono
parents:
diff changeset
285
kono
parents:
diff changeset
286 fn.dump_to_dot ("/tmp/factorial.dot");
kono
parents:
diff changeset
287
kono
parents:
diff changeset
288 and viewing the result. Note how the label names, comments, and
kono
parents:
diff changeset
289 variable names show up in the dump, to make it easier to spot
kono
parents:
diff changeset
290 errors in our compiler.
kono
parents:
diff changeset
291
kono
parents:
diff changeset
292 .. figure:: ../../intro/factorial.png
kono
parents:
diff changeset
293 :alt: image of a control flow graph
kono
parents:
diff changeset
294
kono
parents:
diff changeset
295 Compiling the context
kono
parents:
diff changeset
296 *********************
kono
parents:
diff changeset
297 Having finished looping over the blocks and populating them with
kono
parents:
diff changeset
298 statements, the context is complete.
kono
parents:
diff changeset
299
kono
parents:
diff changeset
300 We can now compile it, extract machine code from the result, and
kono
parents:
diff changeset
301 run it:
kono
parents:
diff changeset
302
kono
parents:
diff changeset
303 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
304 :start-after: /* Wrapper around a gcc_jit_result *. */
kono
parents:
diff changeset
305 :end-before: /* Functions are compiled to this function ptr type. */
kono
parents:
diff changeset
306 :language: c++
kono
parents:
diff changeset
307
kono
parents:
diff changeset
308 .. literalinclude:: ../../examples/tut04-toyvm/toyvm.cc
kono
parents:
diff changeset
309 :start-after: /* JIT-compilation. */
kono
parents:
diff changeset
310 :end-before: return 0;
kono
parents:
diff changeset
311 :language: c++
kono
parents:
diff changeset
312
kono
parents:
diff changeset
313 Single-stepping through the generated code
kono
parents:
diff changeset
314 ******************************************
kono
parents:
diff changeset
315
kono
parents:
diff changeset
316 It's possible to debug the generated code. To do this we need to both:
kono
parents:
diff changeset
317
kono
parents:
diff changeset
318 * Set up source code locations for our statements, so that we can
kono
parents:
diff changeset
319 meaningfully step through the code. We did this above by
kono
parents:
diff changeset
320 calling :func:`gccjit::context::new_location` and using the
kono
parents:
diff changeset
321 results.
kono
parents:
diff changeset
322
kono
parents:
diff changeset
323 * Enable the generation of debugging information, by setting
kono
parents:
diff changeset
324 :c:macro:`GCC_JIT_BOOL_OPTION_DEBUGINFO` on the
kono
parents:
diff changeset
325 :type:`gccjit::context` via
kono
parents:
diff changeset
326 :func:`gccjit::context::set_bool_option`:
kono
parents:
diff changeset
327
kono
parents:
diff changeset
328 .. code-block:: c++
kono
parents:
diff changeset
329
kono
parents:
diff changeset
330 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO, 1);
kono
parents:
diff changeset
331
kono
parents:
diff changeset
332 Having done this, we can put a breakpoint on the generated function:
kono
parents:
diff changeset
333
kono
parents:
diff changeset
334 .. code-block:: console
kono
parents:
diff changeset
335
kono
parents:
diff changeset
336 $ gdb --args ./toyvm factorial.toy 10
kono
parents:
diff changeset
337 (gdb) break factorial
kono
parents:
diff changeset
338 Function "factorial" not defined.
kono
parents:
diff changeset
339 Make breakpoint pending on future shared library load? (y or [n]) y
kono
parents:
diff changeset
340 Breakpoint 1 (factorial) pending.
kono
parents:
diff changeset
341 (gdb) run
kono
parents:
diff changeset
342 Breakpoint 1, factorial (arg=10) at factorial.toy:14
kono
parents:
diff changeset
343 14 DUP
kono
parents:
diff changeset
344
kono
parents:
diff changeset
345 We've set up location information, which references ``factorial.toy``.
kono
parents:
diff changeset
346 This allows us to use e.g. ``list`` to see where we are in the script:
kono
parents:
diff changeset
347
kono
parents:
diff changeset
348 .. code-block:: console
kono
parents:
diff changeset
349
kono
parents:
diff changeset
350 (gdb) list
kono
parents:
diff changeset
351 9
kono
parents:
diff changeset
352 10 # Initial state:
kono
parents:
diff changeset
353 11 # stack: [arg]
kono
parents:
diff changeset
354 12
kono
parents:
diff changeset
355 13 # 0:
kono
parents:
diff changeset
356 14 DUP
kono
parents:
diff changeset
357 15 # stack: [arg, arg]
kono
parents:
diff changeset
358 16
kono
parents:
diff changeset
359 17 # 1:
kono
parents:
diff changeset
360 18 PUSH_CONST 2
kono
parents:
diff changeset
361
kono
parents:
diff changeset
362 and to step through the function, examining the data:
kono
parents:
diff changeset
363
kono
parents:
diff changeset
364 .. code-block:: console
kono
parents:
diff changeset
365
kono
parents:
diff changeset
366 (gdb) n
kono
parents:
diff changeset
367 18 PUSH_CONST 2
kono
parents:
diff changeset
368 (gdb) n
kono
parents:
diff changeset
369 22 BINARY_COMPARE_LT
kono
parents:
diff changeset
370 (gdb) print stack
kono
parents:
diff changeset
371 $5 = {10, 10, 2, 0, -7152, 32767, 0, 0}
kono
parents:
diff changeset
372 (gdb) print stack_depth
kono
parents:
diff changeset
373 $6 = 3
kono
parents:
diff changeset
374
kono
parents:
diff changeset
375 You'll see that the parts of the ``stack`` array that haven't been
kono
parents:
diff changeset
376 touched yet are uninitialized.
kono
parents:
diff changeset
377
kono
parents:
diff changeset
378 .. note::
kono
parents:
diff changeset
379
kono
parents:
diff changeset
380 Turning on optimizations may lead to unpredictable results when
kono
parents:
diff changeset
381 stepping through the generated code: the execution may appear to
kono
parents:
diff changeset
382 "jump around" the source code. This is analogous to turning up the
kono
parents:
diff changeset
383 optimization level in a regular compiler.
kono
parents:
diff changeset
384
kono
parents:
diff changeset
385 Examining the generated code
kono
parents:
diff changeset
386 ****************************
kono
parents:
diff changeset
387
kono
parents:
diff changeset
388 How good is the optimized code?
kono
parents:
diff changeset
389
kono
parents:
diff changeset
390 We can turn up optimizations, by calling
kono
parents:
diff changeset
391 :cpp:func:`gccjit::context::set_int_option` with
kono
parents:
diff changeset
392 :c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`:
kono
parents:
diff changeset
393
kono
parents:
diff changeset
394 .. code-block:: c++
kono
parents:
diff changeset
395
kono
parents:
diff changeset
396 ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
kono
parents:
diff changeset
397
kono
parents:
diff changeset
398 One of GCC's internal representations is called "gimple". A dump of the
kono
parents:
diff changeset
399 initial gimple representation of the code can be seen by setting:
kono
parents:
diff changeset
400
kono
parents:
diff changeset
401 .. code-block:: c++
kono
parents:
diff changeset
402
kono
parents:
diff changeset
403 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1);
kono
parents:
diff changeset
404
kono
parents:
diff changeset
405 With optimization on and source locations displayed, this gives:
kono
parents:
diff changeset
406
kono
parents:
diff changeset
407 .. We'll use "c" for gimple dumps
kono
parents:
diff changeset
408
kono
parents:
diff changeset
409 .. code-block:: c
kono
parents:
diff changeset
410
kono
parents:
diff changeset
411 factorial (signed int arg)
kono
parents:
diff changeset
412 {
kono
parents:
diff changeset
413 <unnamed type> D.80;
kono
parents:
diff changeset
414 signed int D.81;
kono
parents:
diff changeset
415 signed int D.82;
kono
parents:
diff changeset
416 signed int D.83;
kono
parents:
diff changeset
417 signed int D.84;
kono
parents:
diff changeset
418 signed int D.85;
kono
parents:
diff changeset
419 signed int y;
kono
parents:
diff changeset
420 signed int x;
kono
parents:
diff changeset
421 signed int stack_depth;
kono
parents:
diff changeset
422 signed int stack[8];
kono
parents:
diff changeset
423
kono
parents:
diff changeset
424 try
kono
parents:
diff changeset
425 {
kono
parents:
diff changeset
426 initial:
kono
parents:
diff changeset
427 stack_depth = 0;
kono
parents:
diff changeset
428 stack[stack_depth] = arg;
kono
parents:
diff changeset
429 stack_depth = stack_depth + 1;
kono
parents:
diff changeset
430 goto instr0;
kono
parents:
diff changeset
431 instr0:
kono
parents:
diff changeset
432 /* DUP */:
kono
parents:
diff changeset
433 stack_depth = stack_depth + -1;
kono
parents:
diff changeset
434 x = stack[stack_depth];
kono
parents:
diff changeset
435 stack[stack_depth] = x;
kono
parents:
diff changeset
436 stack_depth = stack_depth + 1;
kono
parents:
diff changeset
437 stack[stack_depth] = x;
kono
parents:
diff changeset
438 stack_depth = stack_depth + 1;
kono
parents:
diff changeset
439 goto instr1;
kono
parents:
diff changeset
440 instr1:
kono
parents:
diff changeset
441 /* PUSH_CONST */:
kono
parents:
diff changeset
442 stack[stack_depth] = 2;
kono
parents:
diff changeset
443 stack_depth = stack_depth + 1;
kono
parents:
diff changeset
444 goto instr2;
kono
parents:
diff changeset
445
kono
parents:
diff changeset
446 /* etc */
kono
parents:
diff changeset
447
kono
parents:
diff changeset
448 You can see the generated machine code in assembly form via:
kono
parents:
diff changeset
449
kono
parents:
diff changeset
450 .. code-block:: c++
kono
parents:
diff changeset
451
kono
parents:
diff changeset
452 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1);
kono
parents:
diff changeset
453 result = ctxt.compile ();
kono
parents:
diff changeset
454
kono
parents:
diff changeset
455 which shows that (on this x86_64 box) the compiler has unrolled the loop
kono
parents:
diff changeset
456 and is using MMX instructions to perform several multiplications
kono
parents:
diff changeset
457 simultaneously:
kono
parents:
diff changeset
458
kono
parents:
diff changeset
459 .. code-block:: gas
kono
parents:
diff changeset
460
kono
parents:
diff changeset
461 .file "fake.c"
kono
parents:
diff changeset
462 .text
kono
parents:
diff changeset
463 .Ltext0:
kono
parents:
diff changeset
464 .p2align 4,,15
kono
parents:
diff changeset
465 .globl factorial
kono
parents:
diff changeset
466 .type factorial, @function
kono
parents:
diff changeset
467 factorial:
kono
parents:
diff changeset
468 .LFB0:
kono
parents:
diff changeset
469 .file 1 "factorial.toy"
kono
parents:
diff changeset
470 .loc 1 14 0
kono
parents:
diff changeset
471 .cfi_startproc
kono
parents:
diff changeset
472 .LVL0:
kono
parents:
diff changeset
473 .L2:
kono
parents:
diff changeset
474 .loc 1 26 0
kono
parents:
diff changeset
475 cmpl $1, %edi
kono
parents:
diff changeset
476 jle .L13
kono
parents:
diff changeset
477 leal -1(%rdi), %edx
kono
parents:
diff changeset
478 movl %edx, %ecx
kono
parents:
diff changeset
479 shrl $2, %ecx
kono
parents:
diff changeset
480 leal 0(,%rcx,4), %esi
kono
parents:
diff changeset
481 testl %esi, %esi
kono
parents:
diff changeset
482 je .L14
kono
parents:
diff changeset
483 cmpl $9, %edx
kono
parents:
diff changeset
484 jbe .L14
kono
parents:
diff changeset
485 leal -2(%rdi), %eax
kono
parents:
diff changeset
486 movl %eax, -16(%rsp)
kono
parents:
diff changeset
487 leal -3(%rdi), %eax
kono
parents:
diff changeset
488 movd -16(%rsp), %xmm0
kono
parents:
diff changeset
489 movl %edi, -16(%rsp)
kono
parents:
diff changeset
490 movl %eax, -12(%rsp)
kono
parents:
diff changeset
491 movd -16(%rsp), %xmm1
kono
parents:
diff changeset
492 xorl %eax, %eax
kono
parents:
diff changeset
493 movl %edx, -16(%rsp)
kono
parents:
diff changeset
494 movd -12(%rsp), %xmm4
kono
parents:
diff changeset
495 movd -16(%rsp), %xmm6
kono
parents:
diff changeset
496 punpckldq %xmm4, %xmm0
kono
parents:
diff changeset
497 movdqa .LC1(%rip), %xmm4
kono
parents:
diff changeset
498 punpckldq %xmm6, %xmm1
kono
parents:
diff changeset
499 punpcklqdq %xmm0, %xmm1
kono
parents:
diff changeset
500 movdqa .LC0(%rip), %xmm0
kono
parents:
diff changeset
501 jmp .L5
kono
parents:
diff changeset
502 # etc - edited for brevity
kono
parents:
diff changeset
503
kono
parents:
diff changeset
504 This is clearly overkill for a function that will likely overflow the
kono
parents:
diff changeset
505 ``int`` type before the vectorization is worthwhile - but then again, this
kono
parents:
diff changeset
506 is a toy example.
kono
parents:
diff changeset
507
kono
parents:
diff changeset
508 Turning down the optimization level to 2:
kono
parents:
diff changeset
509
kono
parents:
diff changeset
510 .. code-block:: c++
kono
parents:
diff changeset
511
kono
parents:
diff changeset
512 ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
kono
parents:
diff changeset
513
kono
parents:
diff changeset
514 yields this code, which is simple enough to quote in its entirety:
kono
parents:
diff changeset
515
kono
parents:
diff changeset
516 .. code-block:: gas
kono
parents:
diff changeset
517
kono
parents:
diff changeset
518 .file "fake.c"
kono
parents:
diff changeset
519 .text
kono
parents:
diff changeset
520 .p2align 4,,15
kono
parents:
diff changeset
521 .globl factorial
kono
parents:
diff changeset
522 .type factorial, @function
kono
parents:
diff changeset
523 factorial:
kono
parents:
diff changeset
524 .LFB0:
kono
parents:
diff changeset
525 .cfi_startproc
kono
parents:
diff changeset
526 .L2:
kono
parents:
diff changeset
527 cmpl $1, %edi
kono
parents:
diff changeset
528 jle .L8
kono
parents:
diff changeset
529 movl $1, %edx
kono
parents:
diff changeset
530 jmp .L4
kono
parents:
diff changeset
531 .p2align 4,,10
kono
parents:
diff changeset
532 .p2align 3
kono
parents:
diff changeset
533 .L6:
kono
parents:
diff changeset
534 movl %eax, %edi
kono
parents:
diff changeset
535 .L4:
kono
parents:
diff changeset
536 .L5:
kono
parents:
diff changeset
537 leal -1(%rdi), %eax
kono
parents:
diff changeset
538 imull %edi, %edx
kono
parents:
diff changeset
539 cmpl $1, %eax
kono
parents:
diff changeset
540 jne .L6
kono
parents:
diff changeset
541 .L3:
kono
parents:
diff changeset
542 .L7:
kono
parents:
diff changeset
543 imull %edx, %eax
kono
parents:
diff changeset
544 ret
kono
parents:
diff changeset
545 .L8:
kono
parents:
diff changeset
546 movl %edi, %eax
kono
parents:
diff changeset
547 movl $1, %edx
kono
parents:
diff changeset
548 jmp .L7
kono
parents:
diff changeset
549 .cfi_endproc
kono
parents:
diff changeset
550 .LFE0:
kono
parents:
diff changeset
551 .size factorial, .-factorial
kono
parents:
diff changeset
552 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-%{gcc_release})"
kono
parents:
diff changeset
553 .section .note.GNU-stack,"",@progbits
kono
parents:
diff changeset
554
kono
parents:
diff changeset
555 Note that the stack pushing and popping have been eliminated, as has the
kono
parents:
diff changeset
556 recursive call (in favor of an iteration).
kono
parents:
diff changeset
557
kono
parents:
diff changeset
558 Putting it all together
kono
parents:
diff changeset
559 ***********************
kono
parents:
diff changeset
560
kono
parents:
diff changeset
561 The complete example can be seen in the source tree at
kono
parents:
diff changeset
562 ``gcc/jit/docs/examples/tut04-toyvm/toyvm.cc``
kono
parents:
diff changeset
563
kono
parents:
diff changeset
564 along with a Makefile and a couple of sample .toy scripts:
kono
parents:
diff changeset
565
kono
parents:
diff changeset
566 .. code-block:: console
kono
parents:
diff changeset
567
kono
parents:
diff changeset
568 $ ls -al
kono
parents:
diff changeset
569 drwxrwxr-x. 2 david david 4096 Sep 19 17:46 .
kono
parents:
diff changeset
570 drwxrwxr-x. 3 david david 4096 Sep 19 15:26 ..
kono
parents:
diff changeset
571 -rw-rw-r--. 1 david david 615 Sep 19 12:43 factorial.toy
kono
parents:
diff changeset
572 -rw-rw-r--. 1 david david 834 Sep 19 13:08 fibonacci.toy
kono
parents:
diff changeset
573 -rw-rw-r--. 1 david david 238 Sep 19 14:22 Makefile
kono
parents:
diff changeset
574 -rw-rw-r--. 1 david david 16457 Sep 19 17:07 toyvm.cc
kono
parents:
diff changeset
575
kono
parents:
diff changeset
576 $ make toyvm
kono
parents:
diff changeset
577 g++ -Wall -g -o toyvm toyvm.cc -lgccjit
kono
parents:
diff changeset
578
kono
parents:
diff changeset
579 $ ./toyvm factorial.toy 10
kono
parents:
diff changeset
580 interpreter result: 3628800
kono
parents:
diff changeset
581 compiler result: 3628800
kono
parents:
diff changeset
582
kono
parents:
diff changeset
583 $ ./toyvm fibonacci.toy 10
kono
parents:
diff changeset
584 interpreter result: 55
kono
parents:
diff changeset
585 compiler result: 55
kono
parents:
diff changeset
586
kono
parents:
diff changeset
587 Behind the curtain: How does our code get optimized?
kono
parents:
diff changeset
588 ****************************************************
kono
parents:
diff changeset
589
kono
parents:
diff changeset
590 Our example is done, but you may be wondering about exactly how the
kono
parents:
diff changeset
591 compiler turned what we gave it into the machine code seen above.
kono
parents:
diff changeset
592
kono
parents:
diff changeset
593 We can examine what the compiler is doing in detail by setting:
kono
parents:
diff changeset
594
kono
parents:
diff changeset
595 .. code-block:: c++
kono
parents:
diff changeset
596
kono
parents:
diff changeset
597 state.ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, 1);
kono
parents:
diff changeset
598 state.ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, 1);
kono
parents:
diff changeset
599
kono
parents:
diff changeset
600 This will dump detailed information about the compiler's state to a
kono
parents:
diff changeset
601 directory under ``/tmp``, and keep it from being cleaned up.
kono
parents:
diff changeset
602
kono
parents:
diff changeset
603 The precise names and their formats of these files is subject to change.
kono
parents:
diff changeset
604 Higher optimization levels lead to more files.
kono
parents:
diff changeset
605 Here's what I saw (edited for brevity; there were almost 200 files):
kono
parents:
diff changeset
606
kono
parents:
diff changeset
607 .. code-block:: console
kono
parents:
diff changeset
608
kono
parents:
diff changeset
609 intermediate files written to /tmp/libgccjit-KPQbGw
kono
parents:
diff changeset
610 $ ls /tmp/libgccjit-KPQbGw/
kono
parents:
diff changeset
611 fake.c.000i.cgraph
kono
parents:
diff changeset
612 fake.c.000i.type-inheritance
kono
parents:
diff changeset
613 fake.c.004t.gimple
kono
parents:
diff changeset
614 fake.c.007t.omplower
kono
parents:
diff changeset
615 fake.c.008t.lower
kono
parents:
diff changeset
616 fake.c.011t.eh
kono
parents:
diff changeset
617 fake.c.012t.cfg
kono
parents:
diff changeset
618 fake.c.014i.visibility
kono
parents:
diff changeset
619 fake.c.015i.early_local_cleanups
kono
parents:
diff changeset
620 fake.c.016t.ssa
kono
parents:
diff changeset
621 # etc
kono
parents:
diff changeset
622
kono
parents:
diff changeset
623 The gimple code is converted into Static Single Assignment form,
kono
parents:
diff changeset
624 with annotations for use when generating the debuginfo:
kono
parents:
diff changeset
625
kono
parents:
diff changeset
626 .. code-block:: console
kono
parents:
diff changeset
627
kono
parents:
diff changeset
628 $ less /tmp/libgccjit-KPQbGw/fake.c.016t.ssa
kono
parents:
diff changeset
629
kono
parents:
diff changeset
630 .. code-block:: c
kono
parents:
diff changeset
631
kono
parents:
diff changeset
632 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
633
kono
parents:
diff changeset
634 factorial (signed int arg)
kono
parents:
diff changeset
635 {
kono
parents:
diff changeset
636 signed int stack[8];
kono
parents:
diff changeset
637 signed int stack_depth;
kono
parents:
diff changeset
638 signed int x;
kono
parents:
diff changeset
639 signed int y;
kono
parents:
diff changeset
640 <unnamed type> _20;
kono
parents:
diff changeset
641 signed int _21;
kono
parents:
diff changeset
642 signed int _38;
kono
parents:
diff changeset
643 signed int _44;
kono
parents:
diff changeset
644 signed int _51;
kono
parents:
diff changeset
645 signed int _56;
kono
parents:
diff changeset
646
kono
parents:
diff changeset
647 initial:
kono
parents:
diff changeset
648 stack_depth_3 = 0;
kono
parents:
diff changeset
649 # DEBUG stack_depth => stack_depth_3
kono
parents:
diff changeset
650 stack[stack_depth_3] = arg_5(D);
kono
parents:
diff changeset
651 stack_depth_7 = stack_depth_3 + 1;
kono
parents:
diff changeset
652 # DEBUG stack_depth => stack_depth_7
kono
parents:
diff changeset
653 # DEBUG instr0 => NULL
kono
parents:
diff changeset
654 # DEBUG /* DUP */ => NULL
kono
parents:
diff changeset
655 stack_depth_8 = stack_depth_7 + -1;
kono
parents:
diff changeset
656 # DEBUG stack_depth => stack_depth_8
kono
parents:
diff changeset
657 x_9 = stack[stack_depth_8];
kono
parents:
diff changeset
658 # DEBUG x => x_9
kono
parents:
diff changeset
659 stack[stack_depth_8] = x_9;
kono
parents:
diff changeset
660 stack_depth_11 = stack_depth_8 + 1;
kono
parents:
diff changeset
661 # DEBUG stack_depth => stack_depth_11
kono
parents:
diff changeset
662 stack[stack_depth_11] = x_9;
kono
parents:
diff changeset
663 stack_depth_13 = stack_depth_11 + 1;
kono
parents:
diff changeset
664 # DEBUG stack_depth => stack_depth_13
kono
parents:
diff changeset
665 # DEBUG instr1 => NULL
kono
parents:
diff changeset
666 # DEBUG /* PUSH_CONST */ => NULL
kono
parents:
diff changeset
667 stack[stack_depth_13] = 2;
kono
parents:
diff changeset
668
kono
parents:
diff changeset
669 /* etc; edited for brevity */
kono
parents:
diff changeset
670
kono
parents:
diff changeset
671 We can perhaps better see the code by turning off
kono
parents:
diff changeset
672 :c:macro:`GCC_JIT_BOOL_OPTION_DEBUGINFO` to suppress all those ``DEBUG``
kono
parents:
diff changeset
673 statements, giving:
kono
parents:
diff changeset
674
kono
parents:
diff changeset
675 .. code-block:: console
kono
parents:
diff changeset
676
kono
parents:
diff changeset
677 $ less /tmp/libgccjit-1Hywc0/fake.c.016t.ssa
kono
parents:
diff changeset
678
kono
parents:
diff changeset
679 .. code-block:: c
kono
parents:
diff changeset
680
kono
parents:
diff changeset
681 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
682
kono
parents:
diff changeset
683 factorial (signed int arg)
kono
parents:
diff changeset
684 {
kono
parents:
diff changeset
685 signed int stack[8];
kono
parents:
diff changeset
686 signed int stack_depth;
kono
parents:
diff changeset
687 signed int x;
kono
parents:
diff changeset
688 signed int y;
kono
parents:
diff changeset
689 <unnamed type> _20;
kono
parents:
diff changeset
690 signed int _21;
kono
parents:
diff changeset
691 signed int _38;
kono
parents:
diff changeset
692 signed int _44;
kono
parents:
diff changeset
693 signed int _51;
kono
parents:
diff changeset
694 signed int _56;
kono
parents:
diff changeset
695
kono
parents:
diff changeset
696 initial:
kono
parents:
diff changeset
697 stack_depth_3 = 0;
kono
parents:
diff changeset
698 stack[stack_depth_3] = arg_5(D);
kono
parents:
diff changeset
699 stack_depth_7 = stack_depth_3 + 1;
kono
parents:
diff changeset
700 stack_depth_8 = stack_depth_7 + -1;
kono
parents:
diff changeset
701 x_9 = stack[stack_depth_8];
kono
parents:
diff changeset
702 stack[stack_depth_8] = x_9;
kono
parents:
diff changeset
703 stack_depth_11 = stack_depth_8 + 1;
kono
parents:
diff changeset
704 stack[stack_depth_11] = x_9;
kono
parents:
diff changeset
705 stack_depth_13 = stack_depth_11 + 1;
kono
parents:
diff changeset
706 stack[stack_depth_13] = 2;
kono
parents:
diff changeset
707 stack_depth_15 = stack_depth_13 + 1;
kono
parents:
diff changeset
708 stack_depth_16 = stack_depth_15 + -1;
kono
parents:
diff changeset
709 y_17 = stack[stack_depth_16];
kono
parents:
diff changeset
710 stack_depth_18 = stack_depth_16 + -1;
kono
parents:
diff changeset
711 x_19 = stack[stack_depth_18];
kono
parents:
diff changeset
712 _20 = x_19 < y_17;
kono
parents:
diff changeset
713 _21 = (signed int) _20;
kono
parents:
diff changeset
714 stack[stack_depth_18] = _21;
kono
parents:
diff changeset
715 stack_depth_23 = stack_depth_18 + 1;
kono
parents:
diff changeset
716 stack_depth_24 = stack_depth_23 + -1;
kono
parents:
diff changeset
717 x_25 = stack[stack_depth_24];
kono
parents:
diff changeset
718 if (x_25 != 0)
kono
parents:
diff changeset
719 goto <bb 4> (instr9);
kono
parents:
diff changeset
720 else
kono
parents:
diff changeset
721 goto <bb 3> (instr4);
kono
parents:
diff changeset
722
kono
parents:
diff changeset
723 instr4:
kono
parents:
diff changeset
724 /* DUP */:
kono
parents:
diff changeset
725 stack_depth_26 = stack_depth_24 + -1;
kono
parents:
diff changeset
726 x_27 = stack[stack_depth_26];
kono
parents:
diff changeset
727 stack[stack_depth_26] = x_27;
kono
parents:
diff changeset
728 stack_depth_29 = stack_depth_26 + 1;
kono
parents:
diff changeset
729 stack[stack_depth_29] = x_27;
kono
parents:
diff changeset
730 stack_depth_31 = stack_depth_29 + 1;
kono
parents:
diff changeset
731 stack[stack_depth_31] = 1;
kono
parents:
diff changeset
732 stack_depth_33 = stack_depth_31 + 1;
kono
parents:
diff changeset
733 stack_depth_34 = stack_depth_33 + -1;
kono
parents:
diff changeset
734 y_35 = stack[stack_depth_34];
kono
parents:
diff changeset
735 stack_depth_36 = stack_depth_34 + -1;
kono
parents:
diff changeset
736 x_37 = stack[stack_depth_36];
kono
parents:
diff changeset
737 _38 = x_37 - y_35;
kono
parents:
diff changeset
738 stack[stack_depth_36] = _38;
kono
parents:
diff changeset
739 stack_depth_40 = stack_depth_36 + 1;
kono
parents:
diff changeset
740 stack_depth_41 = stack_depth_40 + -1;
kono
parents:
diff changeset
741 x_42 = stack[stack_depth_41];
kono
parents:
diff changeset
742 _44 = factorial (x_42);
kono
parents:
diff changeset
743 stack[stack_depth_41] = _44;
kono
parents:
diff changeset
744 stack_depth_46 = stack_depth_41 + 1;
kono
parents:
diff changeset
745 stack_depth_47 = stack_depth_46 + -1;
kono
parents:
diff changeset
746 y_48 = stack[stack_depth_47];
kono
parents:
diff changeset
747 stack_depth_49 = stack_depth_47 + -1;
kono
parents:
diff changeset
748 x_50 = stack[stack_depth_49];
kono
parents:
diff changeset
749 _51 = x_50 * y_48;
kono
parents:
diff changeset
750 stack[stack_depth_49] = _51;
kono
parents:
diff changeset
751 stack_depth_53 = stack_depth_49 + 1;
kono
parents:
diff changeset
752
kono
parents:
diff changeset
753 # stack_depth_1 = PHI <stack_depth_24(2), stack_depth_53(3)>
kono
parents:
diff changeset
754 instr9:
kono
parents:
diff changeset
755 /* RETURN */:
kono
parents:
diff changeset
756 stack_depth_54 = stack_depth_1 + -1;
kono
parents:
diff changeset
757 x_55 = stack[stack_depth_54];
kono
parents:
diff changeset
758 _56 = x_55;
kono
parents:
diff changeset
759 stack ={v} {CLOBBER};
kono
parents:
diff changeset
760 return _56;
kono
parents:
diff changeset
761
kono
parents:
diff changeset
762 }
kono
parents:
diff changeset
763
kono
parents:
diff changeset
764 Note in the above how all the :type:`gccjit::block` instances we
kono
parents:
diff changeset
765 created have been consolidated into just 3 blocks in GCC's internal
kono
parents:
diff changeset
766 representation: ``initial``, ``instr4`` and ``instr9``.
kono
parents:
diff changeset
767
kono
parents:
diff changeset
768 Optimizing away stack manipulation
kono
parents:
diff changeset
769 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
kono
parents:
diff changeset
770 Recall our simple implementation of stack operations. Let's examine
kono
parents:
diff changeset
771 how the stack operations are optimized away.
kono
parents:
diff changeset
772
kono
parents:
diff changeset
773 After a pass of constant-propagation, the depth of the stack at each
kono
parents:
diff changeset
774 opcode can be determined at compile-time:
kono
parents:
diff changeset
775
kono
parents:
diff changeset
776 .. code-block:: console
kono
parents:
diff changeset
777
kono
parents:
diff changeset
778 $ less /tmp/libgccjit-1Hywc0/fake.c.021t.ccp1
kono
parents:
diff changeset
779
kono
parents:
diff changeset
780 .. code-block:: c
kono
parents:
diff changeset
781
kono
parents:
diff changeset
782 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
783
kono
parents:
diff changeset
784 factorial (signed int arg)
kono
parents:
diff changeset
785 {
kono
parents:
diff changeset
786 signed int stack[8];
kono
parents:
diff changeset
787 signed int stack_depth;
kono
parents:
diff changeset
788 signed int x;
kono
parents:
diff changeset
789 signed int y;
kono
parents:
diff changeset
790 <unnamed type> _20;
kono
parents:
diff changeset
791 signed int _21;
kono
parents:
diff changeset
792 signed int _38;
kono
parents:
diff changeset
793 signed int _44;
kono
parents:
diff changeset
794 signed int _51;
kono
parents:
diff changeset
795
kono
parents:
diff changeset
796 initial:
kono
parents:
diff changeset
797 stack[0] = arg_5(D);
kono
parents:
diff changeset
798 x_9 = stack[0];
kono
parents:
diff changeset
799 stack[0] = x_9;
kono
parents:
diff changeset
800 stack[1] = x_9;
kono
parents:
diff changeset
801 stack[2] = 2;
kono
parents:
diff changeset
802 y_17 = stack[2];
kono
parents:
diff changeset
803 x_19 = stack[1];
kono
parents:
diff changeset
804 _20 = x_19 < y_17;
kono
parents:
diff changeset
805 _21 = (signed int) _20;
kono
parents:
diff changeset
806 stack[1] = _21;
kono
parents:
diff changeset
807 x_25 = stack[1];
kono
parents:
diff changeset
808 if (x_25 != 0)
kono
parents:
diff changeset
809 goto <bb 4> (instr9);
kono
parents:
diff changeset
810 else
kono
parents:
diff changeset
811 goto <bb 3> (instr4);
kono
parents:
diff changeset
812
kono
parents:
diff changeset
813 instr4:
kono
parents:
diff changeset
814 /* DUP */:
kono
parents:
diff changeset
815 x_27 = stack[0];
kono
parents:
diff changeset
816 stack[0] = x_27;
kono
parents:
diff changeset
817 stack[1] = x_27;
kono
parents:
diff changeset
818 stack[2] = 1;
kono
parents:
diff changeset
819 y_35 = stack[2];
kono
parents:
diff changeset
820 x_37 = stack[1];
kono
parents:
diff changeset
821 _38 = x_37 - y_35;
kono
parents:
diff changeset
822 stack[1] = _38;
kono
parents:
diff changeset
823 x_42 = stack[1];
kono
parents:
diff changeset
824 _44 = factorial (x_42);
kono
parents:
diff changeset
825 stack[1] = _44;
kono
parents:
diff changeset
826 y_48 = stack[1];
kono
parents:
diff changeset
827 x_50 = stack[0];
kono
parents:
diff changeset
828 _51 = x_50 * y_48;
kono
parents:
diff changeset
829 stack[0] = _51;
kono
parents:
diff changeset
830
kono
parents:
diff changeset
831 instr9:
kono
parents:
diff changeset
832 /* RETURN */:
kono
parents:
diff changeset
833 x_55 = stack[0];
kono
parents:
diff changeset
834 x_56 = x_55;
kono
parents:
diff changeset
835 stack ={v} {CLOBBER};
kono
parents:
diff changeset
836 return x_56;
kono
parents:
diff changeset
837
kono
parents:
diff changeset
838 }
kono
parents:
diff changeset
839
kono
parents:
diff changeset
840 Note how, in the above, all those ``stack_depth`` values are now just
kono
parents:
diff changeset
841 constants: we're accessing specific stack locations at each opcode.
kono
parents:
diff changeset
842
kono
parents:
diff changeset
843 The "esra" pass ("Early Scalar Replacement of Aggregates") breaks
kono
parents:
diff changeset
844 out our "stack" array into individual elements:
kono
parents:
diff changeset
845
kono
parents:
diff changeset
846 .. code-block:: console
kono
parents:
diff changeset
847
kono
parents:
diff changeset
848 $ less /tmp/libgccjit-1Hywc0/fake.c.024t.esra
kono
parents:
diff changeset
849
kono
parents:
diff changeset
850 .. code-block:: c
kono
parents:
diff changeset
851
kono
parents:
diff changeset
852 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
853
kono
parents:
diff changeset
854 Created a replacement for stack offset: 0, size: 32: stack$0
kono
parents:
diff changeset
855 Created a replacement for stack offset: 32, size: 32: stack$1
kono
parents:
diff changeset
856 Created a replacement for stack offset: 64, size: 32: stack$2
kono
parents:
diff changeset
857
kono
parents:
diff changeset
858 Symbols to be put in SSA form
kono
parents:
diff changeset
859 { D.89 D.90 D.91 }
kono
parents:
diff changeset
860 Incremental SSA update started at block: 0
kono
parents:
diff changeset
861 Number of blocks in CFG: 5
kono
parents:
diff changeset
862 Number of blocks to update: 4 ( 80%)
kono
parents:
diff changeset
863
kono
parents:
diff changeset
864
kono
parents:
diff changeset
865 factorial (signed int arg)
kono
parents:
diff changeset
866 {
kono
parents:
diff changeset
867 signed int stack$2;
kono
parents:
diff changeset
868 signed int stack$1;
kono
parents:
diff changeset
869 signed int stack$0;
kono
parents:
diff changeset
870 signed int stack[8];
kono
parents:
diff changeset
871 signed int stack_depth;
kono
parents:
diff changeset
872 signed int x;
kono
parents:
diff changeset
873 signed int y;
kono
parents:
diff changeset
874 <unnamed type> _20;
kono
parents:
diff changeset
875 signed int _21;
kono
parents:
diff changeset
876 signed int _38;
kono
parents:
diff changeset
877 signed int _44;
kono
parents:
diff changeset
878 signed int _51;
kono
parents:
diff changeset
879
kono
parents:
diff changeset
880 initial:
kono
parents:
diff changeset
881 stack$0_45 = arg_5(D);
kono
parents:
diff changeset
882 x_9 = stack$0_45;
kono
parents:
diff changeset
883 stack$0_39 = x_9;
kono
parents:
diff changeset
884 stack$1_32 = x_9;
kono
parents:
diff changeset
885 stack$2_30 = 2;
kono
parents:
diff changeset
886 y_17 = stack$2_30;
kono
parents:
diff changeset
887 x_19 = stack$1_32;
kono
parents:
diff changeset
888 _20 = x_19 < y_17;
kono
parents:
diff changeset
889 _21 = (signed int) _20;
kono
parents:
diff changeset
890 stack$1_28 = _21;
kono
parents:
diff changeset
891 x_25 = stack$1_28;
kono
parents:
diff changeset
892 if (x_25 != 0)
kono
parents:
diff changeset
893 goto <bb 4> (instr9);
kono
parents:
diff changeset
894 else
kono
parents:
diff changeset
895 goto <bb 3> (instr4);
kono
parents:
diff changeset
896
kono
parents:
diff changeset
897 instr4:
kono
parents:
diff changeset
898 /* DUP */:
kono
parents:
diff changeset
899 x_27 = stack$0_39;
kono
parents:
diff changeset
900 stack$0_22 = x_27;
kono
parents:
diff changeset
901 stack$1_14 = x_27;
kono
parents:
diff changeset
902 stack$2_12 = 1;
kono
parents:
diff changeset
903 y_35 = stack$2_12;
kono
parents:
diff changeset
904 x_37 = stack$1_14;
kono
parents:
diff changeset
905 _38 = x_37 - y_35;
kono
parents:
diff changeset
906 stack$1_10 = _38;
kono
parents:
diff changeset
907 x_42 = stack$1_10;
kono
parents:
diff changeset
908 _44 = factorial (x_42);
kono
parents:
diff changeset
909 stack$1_6 = _44;
kono
parents:
diff changeset
910 y_48 = stack$1_6;
kono
parents:
diff changeset
911 x_50 = stack$0_22;
kono
parents:
diff changeset
912 _51 = x_50 * y_48;
kono
parents:
diff changeset
913 stack$0_1 = _51;
kono
parents:
diff changeset
914
kono
parents:
diff changeset
915 # stack$0_52 = PHI <stack$0_39(2), stack$0_1(3)>
kono
parents:
diff changeset
916 instr9:
kono
parents:
diff changeset
917 /* RETURN */:
kono
parents:
diff changeset
918 x_55 = stack$0_52;
kono
parents:
diff changeset
919 x_56 = x_55;
kono
parents:
diff changeset
920 stack ={v} {CLOBBER};
kono
parents:
diff changeset
921 return x_56;
kono
parents:
diff changeset
922
kono
parents:
diff changeset
923 }
kono
parents:
diff changeset
924
kono
parents:
diff changeset
925 Hence at this point, all those pushes and pops of the stack are now
kono
parents:
diff changeset
926 simply assignments to specific temporary variables.
kono
parents:
diff changeset
927
kono
parents:
diff changeset
928 After some copy propagation, the stack manipulation has been completely
kono
parents:
diff changeset
929 optimized away:
kono
parents:
diff changeset
930
kono
parents:
diff changeset
931 .. code-block:: console
kono
parents:
diff changeset
932
kono
parents:
diff changeset
933 $ less /tmp/libgccjit-1Hywc0/fake.c.026t.copyprop1
kono
parents:
diff changeset
934
kono
parents:
diff changeset
935 .. code-block:: c
kono
parents:
diff changeset
936
kono
parents:
diff changeset
937 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
938
kono
parents:
diff changeset
939 factorial (signed int arg)
kono
parents:
diff changeset
940 {
kono
parents:
diff changeset
941 signed int stack$2;
kono
parents:
diff changeset
942 signed int stack$1;
kono
parents:
diff changeset
943 signed int stack$0;
kono
parents:
diff changeset
944 signed int stack[8];
kono
parents:
diff changeset
945 signed int stack_depth;
kono
parents:
diff changeset
946 signed int x;
kono
parents:
diff changeset
947 signed int y;
kono
parents:
diff changeset
948 <unnamed type> _20;
kono
parents:
diff changeset
949 signed int _21;
kono
parents:
diff changeset
950 signed int _38;
kono
parents:
diff changeset
951 signed int _44;
kono
parents:
diff changeset
952 signed int _51;
kono
parents:
diff changeset
953
kono
parents:
diff changeset
954 initial:
kono
parents:
diff changeset
955 stack$0_39 = arg_5(D);
kono
parents:
diff changeset
956 _20 = arg_5(D) <= 1;
kono
parents:
diff changeset
957 _21 = (signed int) _20;
kono
parents:
diff changeset
958 if (_21 != 0)
kono
parents:
diff changeset
959 goto <bb 4> (instr9);
kono
parents:
diff changeset
960 else
kono
parents:
diff changeset
961 goto <bb 3> (instr4);
kono
parents:
diff changeset
962
kono
parents:
diff changeset
963 instr4:
kono
parents:
diff changeset
964 /* DUP */:
kono
parents:
diff changeset
965 _38 = arg_5(D) + -1;
kono
parents:
diff changeset
966 _44 = factorial (_38);
kono
parents:
diff changeset
967 _51 = arg_5(D) * _44;
kono
parents:
diff changeset
968 stack$0_1 = _51;
kono
parents:
diff changeset
969
kono
parents:
diff changeset
970 # stack$0_52 = PHI <arg_5(D)(2), _51(3)>
kono
parents:
diff changeset
971 instr9:
kono
parents:
diff changeset
972 /* RETURN */:
kono
parents:
diff changeset
973 stack ={v} {CLOBBER};
kono
parents:
diff changeset
974 return stack$0_52;
kono
parents:
diff changeset
975
kono
parents:
diff changeset
976 }
kono
parents:
diff changeset
977
kono
parents:
diff changeset
978 Later on, another pass finally eliminated ``stack_depth`` local and the
kono
parents:
diff changeset
979 unused parts of the `stack`` array altogether:
kono
parents:
diff changeset
980
kono
parents:
diff changeset
981 .. code-block:: console
kono
parents:
diff changeset
982
kono
parents:
diff changeset
983 $ less /tmp/libgccjit-1Hywc0/fake.c.036t.release_ssa
kono
parents:
diff changeset
984
kono
parents:
diff changeset
985 .. code-block:: c
kono
parents:
diff changeset
986
kono
parents:
diff changeset
987 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
988
kono
parents:
diff changeset
989 Released 44 names, 314.29%, removed 44 holes
kono
parents:
diff changeset
990 factorial (signed int arg)
kono
parents:
diff changeset
991 {
kono
parents:
diff changeset
992 signed int stack$0;
kono
parents:
diff changeset
993 signed int mult_acc_1;
kono
parents:
diff changeset
994 <unnamed type> _5;
kono
parents:
diff changeset
995 signed int _6;
kono
parents:
diff changeset
996 signed int _7;
kono
parents:
diff changeset
997 signed int mul_tmp_10;
kono
parents:
diff changeset
998 signed int mult_acc_11;
kono
parents:
diff changeset
999 signed int mult_acc_13;
kono
parents:
diff changeset
1000
kono
parents:
diff changeset
1001 # arg_9 = PHI <arg_8(D)(0)>
kono
parents:
diff changeset
1002 # mult_acc_13 = PHI <1(0)>
kono
parents:
diff changeset
1003 initial:
kono
parents:
diff changeset
1004
kono
parents:
diff changeset
1005 <bb 5>:
kono
parents:
diff changeset
1006 # arg_4 = PHI <arg_9(2), _7(3)>
kono
parents:
diff changeset
1007 # mult_acc_1 = PHI <mult_acc_13(2), mult_acc_11(3)>
kono
parents:
diff changeset
1008 _5 = arg_4 <= 1;
kono
parents:
diff changeset
1009 _6 = (signed int) _5;
kono
parents:
diff changeset
1010 if (_6 != 0)
kono
parents:
diff changeset
1011 goto <bb 4> (instr9);
kono
parents:
diff changeset
1012 else
kono
parents:
diff changeset
1013 goto <bb 3> (instr4);
kono
parents:
diff changeset
1014
kono
parents:
diff changeset
1015 instr4:
kono
parents:
diff changeset
1016 /* DUP */:
kono
parents:
diff changeset
1017 _7 = arg_4 + -1;
kono
parents:
diff changeset
1018 mult_acc_11 = mult_acc_1 * arg_4;
kono
parents:
diff changeset
1019 goto <bb 5>;
kono
parents:
diff changeset
1020
kono
parents:
diff changeset
1021 # stack$0_12 = PHI <arg_4(5)>
kono
parents:
diff changeset
1022 instr9:
kono
parents:
diff changeset
1023 /* RETURN */:
kono
parents:
diff changeset
1024 mul_tmp_10 = mult_acc_1 * stack$0_12;
kono
parents:
diff changeset
1025 return mul_tmp_10;
kono
parents:
diff changeset
1026
kono
parents:
diff changeset
1027 }
kono
parents:
diff changeset
1028
kono
parents:
diff changeset
1029
kono
parents:
diff changeset
1030 Elimination of tail recursion
kono
parents:
diff changeset
1031 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
kono
parents:
diff changeset
1032 Another significant optimization is the detection that the call to
kono
parents:
diff changeset
1033 ``factorial`` is tail recursion, which can be eliminated in favor of
kono
parents:
diff changeset
1034 an iteration:
kono
parents:
diff changeset
1035
kono
parents:
diff changeset
1036 .. code-block:: console
kono
parents:
diff changeset
1037
kono
parents:
diff changeset
1038 $ less /tmp/libgccjit-1Hywc0/fake.c.030t.tailr1
kono
parents:
diff changeset
1039
kono
parents:
diff changeset
1040 .. code-block:: c
kono
parents:
diff changeset
1041
kono
parents:
diff changeset
1042 ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0)
kono
parents:
diff changeset
1043
kono
parents:
diff changeset
1044
kono
parents:
diff changeset
1045 Symbols to be put in SSA form
kono
parents:
diff changeset
1046 { D.88 }
kono
parents:
diff changeset
1047 Incremental SSA update started at block: 0
kono
parents:
diff changeset
1048 Number of blocks in CFG: 5
kono
parents:
diff changeset
1049 Number of blocks to update: 4 ( 80%)
kono
parents:
diff changeset
1050
kono
parents:
diff changeset
1051
kono
parents:
diff changeset
1052 factorial (signed int arg)
kono
parents:
diff changeset
1053 {
kono
parents:
diff changeset
1054 signed int stack$2;
kono
parents:
diff changeset
1055 signed int stack$1;
kono
parents:
diff changeset
1056 signed int stack$0;
kono
parents:
diff changeset
1057 signed int stack[8];
kono
parents:
diff changeset
1058 signed int stack_depth;
kono
parents:
diff changeset
1059 signed int x;
kono
parents:
diff changeset
1060 signed int y;
kono
parents:
diff changeset
1061 signed int mult_acc_1;
kono
parents:
diff changeset
1062 <unnamed type> _20;
kono
parents:
diff changeset
1063 signed int _21;
kono
parents:
diff changeset
1064 signed int _38;
kono
parents:
diff changeset
1065 signed int mul_tmp_44;
kono
parents:
diff changeset
1066 signed int mult_acc_51;
kono
parents:
diff changeset
1067
kono
parents:
diff changeset
1068 # arg_5 = PHI <arg_39(D)(0), _38(3)>
kono
parents:
diff changeset
1069 # mult_acc_1 = PHI <1(0), mult_acc_51(3)>
kono
parents:
diff changeset
1070 initial:
kono
parents:
diff changeset
1071 _20 = arg_5 <= 1;
kono
parents:
diff changeset
1072 _21 = (signed int) _20;
kono
parents:
diff changeset
1073 if (_21 != 0)
kono
parents:
diff changeset
1074 goto <bb 4> (instr9);
kono
parents:
diff changeset
1075 else
kono
parents:
diff changeset
1076 goto <bb 3> (instr4);
kono
parents:
diff changeset
1077
kono
parents:
diff changeset
1078 instr4:
kono
parents:
diff changeset
1079 /* DUP */:
kono
parents:
diff changeset
1080 _38 = arg_5 + -1;
kono
parents:
diff changeset
1081 mult_acc_51 = mult_acc_1 * arg_5;
kono
parents:
diff changeset
1082 goto <bb 2> (initial);
kono
parents:
diff changeset
1083
kono
parents:
diff changeset
1084 # stack$0_52 = PHI <arg_5(2)>
kono
parents:
diff changeset
1085 instr9:
kono
parents:
diff changeset
1086 /* RETURN */:
kono
parents:
diff changeset
1087 stack ={v} {CLOBBER};
kono
parents:
diff changeset
1088 mul_tmp_44 = mult_acc_1 * stack$0_52;
kono
parents:
diff changeset
1089 return mul_tmp_44;
kono
parents:
diff changeset
1090
kono
parents:
diff changeset
1091 }