111
|
1 /* Internals of libgccjit: classes for playing back recorded API calls.
|
145
|
2 Copyright (C) 2013-2020 Free Software Foundation, Inc.
|
111
|
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it
|
|
8 under the terms of the GNU General Public License as published by
|
|
9 the Free Software Foundation; either version 3, or (at your option)
|
|
10 any later version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful, but
|
|
13 WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
15 General Public License for more details.
|
|
16
|
|
17 You should have received a copy of the GNU General Public License
|
|
18 along with GCC; see the file COPYING3. If not see
|
|
19 <http://www.gnu.org/licenses/>. */
|
|
20
|
|
21 #include "config.h"
|
|
22 #include "system.h"
|
|
23 #include "coretypes.h"
|
|
24 #include "target.h"
|
|
25 #include "tree.h"
|
|
26 #include "stringpool.h"
|
|
27 #include "cgraph.h"
|
|
28 #include "dumpfile.h"
|
|
29 #include "toplev.h"
|
|
30 #include "tree-cfg.h"
|
|
31 #include "convert.h"
|
|
32 #include "stor-layout.h"
|
|
33 #include "print-tree.h"
|
|
34 #include "gimplify.h"
|
|
35 #include "gcc-driver-name.h"
|
|
36 #include "attribs.h"
|
|
37 #include "context.h"
|
|
38 #include "fold-const.h"
|
131
|
39 #include "opt-suggestions.h"
|
111
|
40 #include "gcc.h"
|
|
41 #include "diagnostic.h"
|
|
42
|
|
43 #include <pthread.h>
|
|
44
|
|
45 #include "jit-playback.h"
|
|
46 #include "jit-result.h"
|
|
47 #include "jit-builtins.h"
|
|
48 #include "jit-tempdir.h"
|
|
49
|
145
|
50 /* Compare with gcc/c-family/c-common.h: DECL_C_BIT_FIELD,
|
|
51 SET_DECL_C_BIT_FIELD.
|
|
52 These are redefined here to avoid depending from the C frontend. */
|
|
53 #define DECL_JIT_BIT_FIELD(NODE) \
|
|
54 (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
|
|
55 #define SET_DECL_JIT_BIT_FIELD(NODE) \
|
|
56 (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1)
|
111
|
57
|
|
58 /* gcc::jit::playback::context::build_cast uses the convert.h API,
|
|
59 which in turn requires the frontend to provide a "convert"
|
|
60 function, apparently as a fallback.
|
|
61
|
|
62 Hence we provide this dummy one, with the requirement that any casts
|
|
63 are handled before reaching this. */
|
|
64 extern tree convert (tree type, tree expr);
|
|
65
|
|
66 tree
|
|
67 convert (tree dst_type, tree expr)
|
|
68 {
|
|
69 gcc_assert (gcc::jit::active_playback_ctxt);
|
|
70 gcc::jit::active_playback_ctxt->add_error (NULL, "unhandled conversion");
|
|
71 fprintf (stderr, "input expression:\n");
|
|
72 debug_tree (expr);
|
|
73 fprintf (stderr, "requested type:\n");
|
|
74 debug_tree (dst_type);
|
|
75 return error_mark_node;
|
|
76 }
|
|
77
|
|
78 namespace gcc {
|
|
79 namespace jit {
|
|
80
|
|
81 /**********************************************************************
|
|
82 Playback.
|
|
83 **********************************************************************/
|
|
84
|
|
85 /* The constructor for gcc::jit::playback::context. */
|
|
86
|
|
87 playback::context::context (recording::context *ctxt)
|
|
88 : log_user (ctxt->get_logger ()),
|
|
89 m_recording_ctxt (ctxt),
|
|
90 m_tempdir (NULL),
|
|
91 m_char_array_type_node (NULL),
|
|
92 m_const_char_ptr (NULL)
|
|
93 {
|
|
94 JIT_LOG_SCOPE (get_logger ());
|
|
95 m_functions.create (0);
|
|
96 m_globals.create (0);
|
|
97 m_source_files.create (0);
|
|
98 m_cached_locations.create (0);
|
|
99 }
|
|
100
|
|
101 /* The destructor for gcc::jit::playback::context. */
|
|
102
|
|
103 playback::context::~context ()
|
|
104 {
|
|
105 JIT_LOG_SCOPE (get_logger ());
|
|
106
|
|
107 /* Normally the playback::context is responsible for cleaning up the
|
|
108 tempdir (including "fake.so" within the filesystem).
|
|
109
|
|
110 In the normal case, clean it up now.
|
|
111
|
|
112 However m_tempdir can be NULL if the context has handed over
|
|
113 responsibility for the tempdir cleanup to the jit::result object, so
|
|
114 that the cleanup can be delayed (see PR jit/64206). If that's the
|
|
115 case this "delete NULL;" is a no-op. */
|
|
116 delete m_tempdir;
|
|
117
|
|
118 m_functions.release ();
|
|
119 }
|
|
120
|
|
121 /* A playback::context can reference GC-managed pointers. Mark them
|
|
122 ("by hand", rather than by gengtype).
|
|
123
|
|
124 This is called on the active playback context (if any) by the
|
|
125 my_ggc_walker hook in the jit_root_table in dummy-frontend.c. */
|
|
126
|
|
127 void
|
|
128 playback::context::
|
|
129 gt_ggc_mx ()
|
|
130 {
|
|
131 int i;
|
|
132 function *func;
|
|
133 FOR_EACH_VEC_ELT (m_functions, i, func)
|
|
134 {
|
|
135 if (ggc_test_and_set_mark (func))
|
|
136 func->gt_ggc_mx ();
|
|
137 }
|
|
138 }
|
|
139
|
|
140 /* Given an enum gcc_jit_types value, get a "tree" type. */
|
|
141
|
|
142 static tree
|
|
143 get_tree_node_for_type (enum gcc_jit_types type_)
|
|
144 {
|
|
145 switch (type_)
|
|
146 {
|
|
147 case GCC_JIT_TYPE_VOID:
|
|
148 return void_type_node;
|
|
149
|
|
150 case GCC_JIT_TYPE_VOID_PTR:
|
|
151 return ptr_type_node;
|
|
152
|
|
153 case GCC_JIT_TYPE_BOOL:
|
|
154 return boolean_type_node;
|
|
155
|
|
156 case GCC_JIT_TYPE_CHAR:
|
|
157 return char_type_node;
|
|
158 case GCC_JIT_TYPE_SIGNED_CHAR:
|
|
159 return signed_char_type_node;
|
|
160 case GCC_JIT_TYPE_UNSIGNED_CHAR:
|
|
161 return unsigned_char_type_node;
|
|
162
|
|
163 case GCC_JIT_TYPE_SHORT:
|
|
164 return short_integer_type_node;
|
|
165 case GCC_JIT_TYPE_UNSIGNED_SHORT:
|
|
166 return short_unsigned_type_node;
|
|
167
|
|
168 case GCC_JIT_TYPE_CONST_CHAR_PTR:
|
|
169 {
|
|
170 tree const_char = build_qualified_type (char_type_node,
|
|
171 TYPE_QUAL_CONST);
|
|
172 return build_pointer_type (const_char);
|
|
173 }
|
|
174
|
|
175 case GCC_JIT_TYPE_INT:
|
|
176 return integer_type_node;
|
|
177 case GCC_JIT_TYPE_UNSIGNED_INT:
|
|
178 return unsigned_type_node;
|
|
179
|
|
180 case GCC_JIT_TYPE_LONG:
|
|
181 return long_integer_type_node;
|
|
182 case GCC_JIT_TYPE_UNSIGNED_LONG:
|
|
183 return long_unsigned_type_node;
|
|
184
|
|
185 case GCC_JIT_TYPE_LONG_LONG:
|
|
186 return long_long_integer_type_node;
|
|
187 case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
|
|
188 return long_long_unsigned_type_node;
|
|
189
|
|
190 case GCC_JIT_TYPE_FLOAT:
|
|
191 return float_type_node;
|
|
192 case GCC_JIT_TYPE_DOUBLE:
|
|
193 return double_type_node;
|
|
194 case GCC_JIT_TYPE_LONG_DOUBLE:
|
|
195 return long_double_type_node;
|
|
196
|
|
197 case GCC_JIT_TYPE_SIZE_T:
|
|
198 return size_type_node;
|
|
199
|
|
200 case GCC_JIT_TYPE_FILE_PTR:
|
|
201 return fileptr_type_node;
|
|
202
|
|
203 case GCC_JIT_TYPE_COMPLEX_FLOAT:
|
|
204 return complex_float_type_node;
|
|
205 case GCC_JIT_TYPE_COMPLEX_DOUBLE:
|
|
206 return complex_double_type_node;
|
|
207 case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
|
|
208 return complex_long_double_type_node;
|
|
209 }
|
|
210
|
|
211 return NULL;
|
|
212 }
|
|
213
|
|
214 /* Construct a playback::type instance (wrapping a tree) for the given
|
|
215 enum value. */
|
|
216
|
|
217 playback::type *
|
|
218 playback::context::
|
|
219 get_type (enum gcc_jit_types type_)
|
|
220 {
|
|
221 tree type_node = get_tree_node_for_type (type_);
|
131
|
222 if (type_node == NULL)
|
111
|
223 {
|
131
|
224 add_error (NULL, "unrecognized (enum gcc_jit_types) value: %i", type_);
|
111
|
225 return NULL;
|
|
226 }
|
|
227
|
|
228 return new type (type_node);
|
|
229 }
|
|
230
|
|
231 /* Construct a playback::type instance (wrapping a tree) for the given
|
|
232 array type. */
|
|
233
|
|
234 playback::type *
|
|
235 playback::context::
|
|
236 new_array_type (playback::location *loc,
|
|
237 playback::type *element_type,
|
|
238 int num_elements)
|
|
239 {
|
|
240 gcc_assert (element_type);
|
|
241
|
|
242 tree t = build_array_type_nelts (element_type->as_tree (),
|
|
243 num_elements);
|
|
244 layout_type (t);
|
|
245
|
|
246 if (loc)
|
|
247 set_tree_location (t, loc);
|
|
248
|
|
249 return new type (t);
|
|
250 }
|
|
251
|
|
252 /* Construct a playback::field instance (wrapping a tree). */
|
|
253
|
|
254 playback::field *
|
|
255 playback::context::
|
|
256 new_field (location *loc,
|
|
257 type *type,
|
|
258 const char *name)
|
|
259 {
|
|
260 gcc_assert (type);
|
|
261 gcc_assert (name);
|
|
262
|
|
263 /* compare with c/c-decl.c:grokfield and grokdeclarator. */
|
|
264 tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
|
|
265 get_identifier (name), type->as_tree ());
|
|
266
|
|
267 if (loc)
|
|
268 set_tree_location (decl, loc);
|
|
269
|
|
270 return new field (decl);
|
|
271 }
|
|
272
|
145
|
273 /* Construct a playback::bitfield instance (wrapping a tree). */
|
|
274
|
|
275 playback::field *
|
|
276 playback::context::
|
|
277 new_bitfield (location *loc,
|
|
278 type *type,
|
|
279 int width,
|
|
280 const char *name)
|
|
281 {
|
|
282 gcc_assert (type);
|
|
283 gcc_assert (name);
|
|
284 gcc_assert (width);
|
|
285
|
|
286 /* compare with c/c-decl.c:grokfield, grokdeclarator and
|
|
287 check_bitfield_type_and_width. */
|
|
288
|
|
289 tree tree_type = type->as_tree ();
|
|
290 gcc_assert (INTEGRAL_TYPE_P (tree_type));
|
|
291 tree tree_width = build_int_cst (integer_type_node, width);
|
|
292 if (compare_tree_int (tree_width, TYPE_PRECISION (tree_type)) > 0)
|
|
293 {
|
|
294 add_error (
|
|
295 loc,
|
|
296 "width of bit-field %s (width: %i) is wider than its type (width: %i)",
|
|
297 name, width, TYPE_PRECISION (tree_type));
|
|
298 return NULL;
|
|
299 }
|
|
300
|
|
301 tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
|
|
302 get_identifier (name), type->as_tree ());
|
|
303 DECL_NONADDRESSABLE_P (decl) = true;
|
|
304 DECL_INITIAL (decl) = tree_width;
|
|
305 SET_DECL_JIT_BIT_FIELD (decl);
|
|
306
|
|
307 if (loc)
|
|
308 set_tree_location (decl, loc);
|
|
309
|
|
310 return new field (decl);
|
|
311 }
|
|
312
|
111
|
313 /* Construct a playback::compound_type instance (wrapping a tree). */
|
|
314
|
|
315 playback::compound_type *
|
|
316 playback::context::
|
|
317 new_compound_type (location *loc,
|
|
318 const char *name,
|
|
319 bool is_struct) /* else is union */
|
|
320 {
|
|
321 gcc_assert (name);
|
|
322
|
|
323 /* Compare with c/c-decl.c: start_struct. */
|
|
324
|
|
325 tree t = make_node (is_struct ? RECORD_TYPE : UNION_TYPE);
|
|
326 TYPE_NAME (t) = get_identifier (name);
|
|
327 TYPE_SIZE (t) = 0;
|
|
328
|
|
329 if (loc)
|
|
330 set_tree_location (t, loc);
|
|
331
|
|
332 return new compound_type (t);
|
|
333 }
|
|
334
|
|
335 void
|
|
336 playback::compound_type::set_fields (const auto_vec<playback::field *> *fields)
|
|
337 {
|
|
338 /* Compare with c/c-decl.c: finish_struct. */
|
|
339 tree t = as_tree ();
|
|
340
|
|
341 tree fieldlist = NULL;
|
|
342 for (unsigned i = 0; i < fields->length (); i++)
|
|
343 {
|
|
344 field *f = (*fields)[i];
|
145
|
345 tree x = f->as_tree ();
|
|
346 DECL_CONTEXT (x) = t;
|
|
347 if (DECL_JIT_BIT_FIELD (x))
|
|
348 {
|
|
349 unsigned HOST_WIDE_INT width = tree_to_uhwi (DECL_INITIAL (x));
|
|
350 DECL_SIZE (x) = bitsize_int (width);
|
|
351 DECL_BIT_FIELD (x) = 1;
|
|
352 }
|
|
353 fieldlist = chainon (x, fieldlist);
|
111
|
354 }
|
|
355 fieldlist = nreverse (fieldlist);
|
|
356 TYPE_FIELDS (t) = fieldlist;
|
|
357
|
|
358 layout_type (t);
|
|
359 }
|
|
360
|
|
361 /* Construct a playback::type instance (wrapping a tree) for a function
|
|
362 type. */
|
|
363
|
|
364 playback::type *
|
|
365 playback::context::
|
|
366 new_function_type (type *return_type,
|
|
367 const auto_vec<type *> *param_types,
|
|
368 int is_variadic)
|
|
369 {
|
|
370 int i;
|
|
371 type *param_type;
|
|
372
|
|
373 tree *arg_types = (tree *)xcalloc(param_types->length (), sizeof(tree*));
|
|
374
|
|
375 FOR_EACH_VEC_ELT (*param_types, i, param_type)
|
|
376 arg_types[i] = param_type->as_tree ();
|
|
377
|
|
378 tree fn_type;
|
|
379 if (is_variadic)
|
|
380 fn_type =
|
|
381 build_varargs_function_type_array (return_type->as_tree (),
|
|
382 param_types->length (),
|
|
383 arg_types);
|
|
384 else
|
|
385 fn_type = build_function_type_array (return_type->as_tree (),
|
|
386 param_types->length (),
|
|
387 arg_types);
|
|
388 free (arg_types);
|
|
389
|
|
390 return new type (fn_type);
|
|
391 }
|
|
392
|
|
393 /* Construct a playback::param instance (wrapping a tree). */
|
|
394
|
|
395 playback::param *
|
|
396 playback::context::
|
|
397 new_param (location *loc,
|
|
398 type *type,
|
|
399 const char *name)
|
|
400 {
|
|
401 gcc_assert (type);
|
|
402 gcc_assert (name);
|
|
403 tree inner = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
404 get_identifier (name), type->as_tree ());
|
|
405 if (loc)
|
|
406 set_tree_location (inner, loc);
|
|
407
|
|
408 return new param (this, inner);
|
|
409 }
|
|
410
|
|
411 /* Construct a playback::function instance. */
|
|
412
|
|
413 playback::function *
|
|
414 playback::context::
|
|
415 new_function (location *loc,
|
|
416 enum gcc_jit_function_kind kind,
|
|
417 type *return_type,
|
|
418 const char *name,
|
|
419 const auto_vec<param *> *params,
|
|
420 int is_variadic,
|
|
421 enum built_in_function builtin_id)
|
|
422 {
|
|
423 int i;
|
|
424 param *param;
|
|
425
|
|
426 //can return_type be NULL?
|
|
427 gcc_assert (name);
|
|
428
|
|
429 tree *arg_types = (tree *)xcalloc(params->length (), sizeof(tree*));
|
|
430 FOR_EACH_VEC_ELT (*params, i, param)
|
|
431 arg_types[i] = TREE_TYPE (param->as_tree ());
|
|
432
|
|
433 tree fn_type;
|
|
434 if (is_variadic)
|
|
435 fn_type = build_varargs_function_type_array (return_type->as_tree (),
|
|
436 params->length (), arg_types);
|
|
437 else
|
|
438 fn_type = build_function_type_array (return_type->as_tree (),
|
|
439 params->length (), arg_types);
|
|
440 free (arg_types);
|
|
441
|
|
442 /* FIXME: this uses input_location: */
|
|
443 tree fndecl = build_fn_decl (name, fn_type);
|
|
444
|
|
445 if (loc)
|
|
446 set_tree_location (fndecl, loc);
|
|
447
|
|
448 tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL,
|
|
449 NULL_TREE, return_type->as_tree ());
|
|
450 DECL_ARTIFICIAL (resdecl) = 1;
|
|
451 DECL_IGNORED_P (resdecl) = 1;
|
|
452 DECL_RESULT (fndecl) = resdecl;
|
|
453
|
|
454 if (builtin_id)
|
|
455 {
|
|
456 gcc_assert (loc == NULL);
|
|
457 DECL_SOURCE_LOCATION (fndecl) = BUILTINS_LOCATION;
|
|
458
|
145
|
459 built_in_class fclass = builtins_manager::get_class (builtin_id);
|
|
460 set_decl_built_in_function (fndecl, fclass, builtin_id);
|
111
|
461 set_builtin_decl (builtin_id, fndecl,
|
|
462 builtins_manager::implicit_p (builtin_id));
|
|
463
|
|
464 builtins_manager *bm = get_builtins_manager ();
|
|
465 tree attrs = bm->get_attrs_tree (builtin_id);
|
|
466 if (attrs)
|
|
467 decl_attributes (&fndecl, attrs, ATTR_FLAG_BUILT_IN);
|
|
468 else
|
|
469 decl_attributes (&fndecl, NULL_TREE, 0);
|
|
470 }
|
|
471
|
|
472 if (kind != GCC_JIT_FUNCTION_IMPORTED)
|
|
473 {
|
|
474 tree param_decl_list = NULL;
|
|
475 FOR_EACH_VEC_ELT (*params, i, param)
|
|
476 {
|
|
477 param_decl_list = chainon (param->as_tree (), param_decl_list);
|
|
478 }
|
|
479
|
|
480 /* The param list was created in reverse order; fix it: */
|
|
481 param_decl_list = nreverse (param_decl_list);
|
|
482
|
|
483 tree t;
|
|
484 for (t = param_decl_list; t; t = DECL_CHAIN (t))
|
|
485 {
|
|
486 DECL_CONTEXT (t) = fndecl;
|
|
487 DECL_ARG_TYPE (t) = TREE_TYPE (t);
|
|
488 }
|
|
489
|
|
490 /* Set it up on DECL_ARGUMENTS */
|
|
491 DECL_ARGUMENTS(fndecl) = param_decl_list;
|
|
492 }
|
|
493
|
|
494 if (kind == GCC_JIT_FUNCTION_ALWAYS_INLINE)
|
|
495 {
|
|
496 DECL_DECLARED_INLINE_P (fndecl) = 1;
|
|
497
|
|
498 /* Add attribute "always_inline": */
|
|
499 DECL_ATTRIBUTES (fndecl) =
|
|
500 tree_cons (get_identifier ("always_inline"),
|
|
501 NULL,
|
|
502 DECL_ATTRIBUTES (fndecl));
|
|
503 }
|
|
504
|
|
505 function *func = new function (this, fndecl, kind);
|
|
506 m_functions.safe_push (func);
|
|
507 return func;
|
|
508 }
|
|
509
|
|
510 /* Construct a playback::lvalue instance (wrapping a tree). */
|
|
511
|
|
512 playback::lvalue *
|
|
513 playback::context::
|
|
514 new_global (location *loc,
|
|
515 enum gcc_jit_global_kind kind,
|
|
516 type *type,
|
|
517 const char *name)
|
|
518 {
|
|
519 gcc_assert (type);
|
|
520 gcc_assert (name);
|
|
521 tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
|
522 get_identifier (name),
|
|
523 type->as_tree ());
|
|
524 TREE_PUBLIC (inner) = (kind != GCC_JIT_GLOBAL_INTERNAL);
|
|
525 DECL_COMMON (inner) = 1;
|
|
526 switch (kind)
|
|
527 {
|
|
528 default:
|
|
529 gcc_unreachable ();
|
|
530
|
|
531 case GCC_JIT_GLOBAL_EXPORTED:
|
|
532 TREE_STATIC (inner) = 1;
|
|
533 break;
|
|
534
|
|
535 case GCC_JIT_GLOBAL_INTERNAL:
|
|
536 TREE_STATIC (inner) = 1;
|
|
537 break;
|
|
538
|
|
539 case GCC_JIT_GLOBAL_IMPORTED:
|
|
540 DECL_EXTERNAL (inner) = 1;
|
|
541 break;
|
|
542 }
|
|
543
|
|
544 if (loc)
|
|
545 set_tree_location (inner, loc);
|
|
546
|
|
547 varpool_node::get_create (inner);
|
|
548
|
|
549 varpool_node::finalize_decl (inner);
|
|
550
|
|
551 m_globals.safe_push (inner);
|
|
552
|
|
553 return new lvalue (this, inner);
|
|
554 }
|
|
555
|
|
556 /* Implementation of the various
|
|
557 gcc::jit::playback::context::new_rvalue_from_const <HOST_TYPE>
|
|
558 methods.
|
|
559 Each of these constructs a playback::rvalue instance (wrapping a tree).
|
|
560
|
|
561 These specializations are required to be in the same namespace
|
|
562 as the template, hence we now have to enter the gcc::jit::playback
|
|
563 namespace. */
|
|
564
|
|
565 namespace playback
|
|
566 {
|
|
567
|
|
568 /* Specialization of making an rvalue from a const, for host <int>. */
|
|
569
|
|
570 template <>
|
|
571 rvalue *
|
|
572 context::
|
|
573 new_rvalue_from_const <int> (type *type,
|
|
574 int value)
|
|
575 {
|
|
576 // FIXME: type-checking, or coercion?
|
|
577 tree inner_type = type->as_tree ();
|
|
578 if (INTEGRAL_TYPE_P (inner_type))
|
|
579 {
|
|
580 tree inner = build_int_cst (inner_type, value);
|
|
581 return new rvalue (this, inner);
|
|
582 }
|
|
583 else
|
|
584 {
|
|
585 REAL_VALUE_TYPE real_value;
|
|
586 real_from_integer (&real_value, VOIDmode, value, SIGNED);
|
|
587 tree inner = build_real (inner_type, real_value);
|
|
588 return new rvalue (this, inner);
|
|
589 }
|
|
590 }
|
|
591
|
|
592 /* Specialization of making an rvalue from a const, for host <long>. */
|
|
593
|
|
594 template <>
|
|
595 rvalue *
|
|
596 context::
|
|
597 new_rvalue_from_const <long> (type *type,
|
|
598 long value)
|
|
599 {
|
|
600 // FIXME: type-checking, or coercion?
|
|
601 tree inner_type = type->as_tree ();
|
|
602 if (INTEGRAL_TYPE_P (inner_type))
|
|
603 {
|
|
604 tree inner = build_int_cst (inner_type, value);
|
|
605 return new rvalue (this, inner);
|
|
606 }
|
|
607 else
|
|
608 {
|
|
609 REAL_VALUE_TYPE real_value;
|
|
610 real_from_integer (&real_value, VOIDmode, value, SIGNED);
|
|
611 tree inner = build_real (inner_type, real_value);
|
|
612 return new rvalue (this, inner);
|
|
613 }
|
|
614 }
|
|
615
|
|
616 /* Specialization of making an rvalue from a const, for host <double>. */
|
|
617
|
|
618 template <>
|
|
619 rvalue *
|
|
620 context::
|
|
621 new_rvalue_from_const <double> (type *type,
|
|
622 double value)
|
|
623 {
|
|
624 // FIXME: type-checking, or coercion?
|
|
625 tree inner_type = type->as_tree ();
|
|
626
|
|
627 /* We have a "double", we want a REAL_VALUE_TYPE.
|
|
628
|
|
629 real.c:real_from_target appears to require the representation to be
|
|
630 split into 32-bit values, and then sent as an pair of host long
|
|
631 ints. */
|
|
632 REAL_VALUE_TYPE real_value;
|
|
633 union
|
|
634 {
|
|
635 double as_double;
|
|
636 uint32_t as_uint32s[2];
|
|
637 } u;
|
|
638 u.as_double = value;
|
|
639 long int as_long_ints[2];
|
|
640 as_long_ints[0] = u.as_uint32s[0];
|
|
641 as_long_ints[1] = u.as_uint32s[1];
|
|
642 real_from_target (&real_value, as_long_ints, DFmode);
|
|
643 tree inner = build_real (inner_type, real_value);
|
|
644 return new rvalue (this, inner);
|
|
645 }
|
|
646
|
|
647 /* Specialization of making an rvalue from a const, for host <void *>. */
|
|
648
|
|
649 template <>
|
|
650 rvalue *
|
|
651 context::
|
|
652 new_rvalue_from_const <void *> (type *type,
|
|
653 void *value)
|
|
654 {
|
|
655 tree inner_type = type->as_tree ();
|
|
656 /* FIXME: how to ensure we have a wide enough type? */
|
|
657 tree inner = build_int_cstu (inner_type, (unsigned HOST_WIDE_INT)value);
|
|
658 return new rvalue (this, inner);
|
|
659 }
|
|
660
|
|
661 /* We're done implementing the specializations of
|
|
662 gcc::jit::playback::context::new_rvalue_from_const <T>
|
|
663 so we can exit the gcc::jit::playback namespace. */
|
|
664
|
|
665 } // namespace playback
|
|
666
|
|
667 /* Construct a playback::rvalue instance (wrapping a tree). */
|
|
668
|
|
669 playback::rvalue *
|
|
670 playback::context::
|
|
671 new_string_literal (const char *value)
|
|
672 {
|
|
673 tree t_str = build_string (strlen (value), value);
|
|
674 gcc_assert (m_char_array_type_node);
|
|
675 TREE_TYPE (t_str) = m_char_array_type_node;
|
|
676
|
|
677 /* Convert to (const char*), loosely based on
|
|
678 c/c-typeck.c: array_to_pointer_conversion,
|
|
679 by taking address of start of string. */
|
|
680 tree t_addr = build1 (ADDR_EXPR, m_const_char_ptr, t_str);
|
|
681
|
|
682 return new rvalue (this, t_addr);
|
|
683 }
|
|
684
|
|
685 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
686 vector. */
|
|
687
|
|
688 playback::rvalue *
|
|
689 playback::context::new_rvalue_from_vector (location *,
|
|
690 type *type,
|
|
691 const auto_vec<rvalue *> &elements)
|
|
692 {
|
|
693 vec<constructor_elt, va_gc> *v;
|
|
694 vec_alloc (v, elements.length ());
|
|
695 for (unsigned i = 0; i < elements.length (); ++i)
|
|
696 CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, elements[i]->as_tree ());
|
|
697 tree t_ctor = build_constructor (type->as_tree (), v);
|
|
698 return new rvalue (this, t_ctor);
|
|
699 }
|
|
700
|
|
701 /* Coerce a tree expression into a boolean tree expression. */
|
|
702
|
|
703 tree
|
|
704 playback::context::
|
|
705 as_truth_value (tree expr, location *loc)
|
|
706 {
|
|
707 /* Compare to c-typeck.c:c_objc_common_truthvalue_conversion */
|
|
708 tree typed_zero = fold_build1 (CONVERT_EXPR,
|
|
709 TREE_TYPE (expr),
|
|
710 integer_zero_node);
|
|
711 if (loc)
|
|
712 set_tree_location (typed_zero, loc);
|
|
713
|
|
714 expr = build2 (NE_EXPR, integer_type_node, expr, typed_zero);
|
|
715 if (loc)
|
|
716 set_tree_location (expr, loc);
|
|
717
|
|
718 return expr;
|
|
719 }
|
|
720
|
|
721 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
722 unary op. */
|
|
723
|
|
724 playback::rvalue *
|
|
725 playback::context::
|
|
726 new_unary_op (location *loc,
|
|
727 enum gcc_jit_unary_op op,
|
|
728 type *result_type,
|
|
729 rvalue *a)
|
|
730 {
|
|
731 // FIXME: type-checking, or coercion?
|
|
732 enum tree_code inner_op;
|
|
733
|
|
734 gcc_assert (result_type);
|
|
735 gcc_assert (a);
|
|
736
|
|
737 tree node = a->as_tree ();
|
|
738 tree inner_result = NULL;
|
|
739
|
|
740 switch (op)
|
|
741 {
|
|
742 default:
|
|
743 add_error (loc, "unrecognized (enum gcc_jit_unary_op) value: %i", op);
|
|
744 return NULL;
|
|
745
|
|
746 case GCC_JIT_UNARY_OP_MINUS:
|
|
747 inner_op = NEGATE_EXPR;
|
|
748 break;
|
|
749
|
|
750 case GCC_JIT_UNARY_OP_BITWISE_NEGATE:
|
|
751 inner_op = BIT_NOT_EXPR;
|
|
752 break;
|
|
753
|
|
754 case GCC_JIT_UNARY_OP_LOGICAL_NEGATE:
|
|
755 node = as_truth_value (node, loc);
|
|
756 inner_result = invert_truthvalue (node);
|
|
757 if (loc)
|
|
758 set_tree_location (inner_result, loc);
|
|
759 return new rvalue (this, inner_result);
|
|
760
|
|
761 case GCC_JIT_UNARY_OP_ABS:
|
|
762 inner_op = ABS_EXPR;
|
|
763 break;
|
|
764 }
|
|
765
|
|
766 inner_result = build1 (inner_op,
|
|
767 result_type->as_tree (),
|
|
768 node);
|
|
769 if (loc)
|
|
770 set_tree_location (inner_result, loc);
|
|
771
|
|
772 return new rvalue (this, inner_result);
|
|
773 }
|
|
774
|
|
775 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
776 binary op. */
|
|
777
|
|
778 playback::rvalue *
|
|
779 playback::context::
|
|
780 new_binary_op (location *loc,
|
|
781 enum gcc_jit_binary_op op,
|
|
782 type *result_type,
|
|
783 rvalue *a, rvalue *b)
|
|
784 {
|
|
785 // FIXME: type-checking, or coercion?
|
|
786 enum tree_code inner_op;
|
|
787
|
|
788 gcc_assert (result_type);
|
|
789 gcc_assert (a);
|
|
790 gcc_assert (b);
|
|
791
|
|
792 tree node_a = a->as_tree ();
|
|
793 tree node_b = b->as_tree ();
|
|
794
|
|
795 switch (op)
|
|
796 {
|
|
797 default:
|
|
798 add_error (loc, "unrecognized (enum gcc_jit_binary_op) value: %i", op);
|
|
799 return NULL;
|
|
800
|
|
801 case GCC_JIT_BINARY_OP_PLUS:
|
|
802 inner_op = PLUS_EXPR;
|
|
803 break;
|
|
804
|
|
805 case GCC_JIT_BINARY_OP_MINUS:
|
|
806 inner_op = MINUS_EXPR;
|
|
807 break;
|
|
808
|
|
809 case GCC_JIT_BINARY_OP_MULT:
|
|
810 inner_op = MULT_EXPR;
|
|
811 break;
|
|
812
|
|
813 case GCC_JIT_BINARY_OP_DIVIDE:
|
|
814 if (FLOAT_TYPE_P (result_type->as_tree ()))
|
|
815 /* Floating-point division: */
|
|
816 inner_op = RDIV_EXPR;
|
|
817 else
|
|
818 /* Truncating to zero: */
|
|
819 inner_op = TRUNC_DIV_EXPR;
|
|
820 break;
|
|
821
|
|
822 case GCC_JIT_BINARY_OP_MODULO:
|
|
823 inner_op = TRUNC_MOD_EXPR;
|
|
824 break;
|
|
825
|
|
826 case GCC_JIT_BINARY_OP_BITWISE_AND:
|
|
827 inner_op = BIT_AND_EXPR;
|
|
828 break;
|
|
829
|
|
830 case GCC_JIT_BINARY_OP_BITWISE_XOR:
|
|
831 inner_op = BIT_XOR_EXPR;
|
|
832 break;
|
|
833
|
|
834 case GCC_JIT_BINARY_OP_BITWISE_OR:
|
|
835 inner_op = BIT_IOR_EXPR;
|
|
836 break;
|
|
837
|
|
838 case GCC_JIT_BINARY_OP_LOGICAL_AND:
|
|
839 node_a = as_truth_value (node_a, loc);
|
|
840 node_b = as_truth_value (node_b, loc);
|
|
841 inner_op = TRUTH_ANDIF_EXPR;
|
|
842 break;
|
|
843
|
|
844 case GCC_JIT_BINARY_OP_LOGICAL_OR:
|
|
845 node_a = as_truth_value (node_a, loc);
|
|
846 node_b = as_truth_value (node_b, loc);
|
|
847 inner_op = TRUTH_ORIF_EXPR;
|
|
848 break;
|
|
849
|
|
850 case GCC_JIT_BINARY_OP_LSHIFT:
|
|
851 inner_op = LSHIFT_EXPR;
|
|
852 break;
|
|
853
|
|
854 case GCC_JIT_BINARY_OP_RSHIFT:
|
|
855 inner_op = RSHIFT_EXPR;
|
|
856 break;
|
|
857 }
|
|
858
|
|
859 tree inner_expr = build2 (inner_op,
|
|
860 result_type->as_tree (),
|
|
861 node_a,
|
|
862 node_b);
|
|
863 if (loc)
|
|
864 set_tree_location (inner_expr, loc);
|
|
865
|
|
866 return new rvalue (this, inner_expr);
|
|
867 }
|
|
868
|
|
869 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
870 comparison. */
|
|
871
|
|
872 playback::rvalue *
|
|
873 playback::context::
|
|
874 new_comparison (location *loc,
|
|
875 enum gcc_jit_comparison op,
|
|
876 rvalue *a, rvalue *b)
|
|
877 {
|
|
878 // FIXME: type-checking, or coercion?
|
|
879 enum tree_code inner_op;
|
|
880
|
|
881 gcc_assert (a);
|
|
882 gcc_assert (b);
|
|
883
|
|
884 switch (op)
|
|
885 {
|
|
886 default:
|
|
887 add_error (loc, "unrecognized (enum gcc_jit_comparison) value: %i", op);
|
|
888 return NULL;
|
|
889
|
|
890 case GCC_JIT_COMPARISON_EQ:
|
|
891 inner_op = EQ_EXPR;
|
|
892 break;
|
|
893 case GCC_JIT_COMPARISON_NE:
|
|
894 inner_op = NE_EXPR;
|
|
895 break;
|
|
896 case GCC_JIT_COMPARISON_LT:
|
|
897 inner_op = LT_EXPR;
|
|
898 break;
|
|
899 case GCC_JIT_COMPARISON_LE:
|
|
900 inner_op = LE_EXPR;
|
|
901 break;
|
|
902 case GCC_JIT_COMPARISON_GT:
|
|
903 inner_op = GT_EXPR;
|
|
904 break;
|
|
905 case GCC_JIT_COMPARISON_GE:
|
|
906 inner_op = GE_EXPR;
|
|
907 break;
|
|
908 }
|
|
909
|
|
910 tree inner_expr = build2 (inner_op,
|
|
911 boolean_type_node,
|
|
912 a->as_tree (),
|
|
913 b->as_tree ());
|
|
914 if (loc)
|
|
915 set_tree_location (inner_expr, loc);
|
|
916 return new rvalue (this, inner_expr);
|
|
917 }
|
|
918
|
|
919 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
920 function call. */
|
|
921
|
|
922 playback::rvalue *
|
|
923 playback::context::
|
|
924 build_call (location *loc,
|
|
925 tree fn_ptr,
|
|
926 const auto_vec<rvalue *> *args,
|
|
927 bool require_tail_call)
|
|
928 {
|
|
929 vec<tree, va_gc> *tree_args;
|
|
930 vec_alloc (tree_args, args->length ());
|
|
931 for (unsigned i = 0; i < args->length (); i++)
|
|
932 tree_args->quick_push ((*args)[i]->as_tree ());
|
|
933
|
|
934 if (loc)
|
|
935 set_tree_location (fn_ptr, loc);
|
|
936
|
|
937 tree fn = TREE_TYPE (fn_ptr);
|
|
938 tree fn_type = TREE_TYPE (fn);
|
|
939 tree return_type = TREE_TYPE (fn_type);
|
|
940
|
|
941 tree call = build_call_vec (return_type,
|
|
942 fn_ptr, tree_args);
|
|
943
|
|
944 if (require_tail_call)
|
|
945 CALL_EXPR_MUST_TAIL_CALL (call) = 1;
|
|
946
|
|
947 return new rvalue (this, call);
|
|
948
|
|
949 /* see c-typeck.c: build_function_call
|
|
950 which calls build_function_call_vec
|
|
951
|
|
952 which does lots of checking, then:
|
|
953 result = build_call_array_loc (loc, TREE_TYPE (fntype),
|
|
954 function, nargs, argarray);
|
|
955 which is in tree.c
|
|
956 (see also build_call_vec)
|
|
957 */
|
|
958 }
|
|
959
|
|
960 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
961 call to a specific function. */
|
|
962
|
|
963 playback::rvalue *
|
|
964 playback::context::
|
|
965 new_call (location *loc,
|
|
966 function *func,
|
|
967 const auto_vec<rvalue *> *args,
|
|
968 bool require_tail_call)
|
|
969 {
|
|
970 tree fndecl;
|
|
971
|
|
972 gcc_assert (func);
|
|
973
|
|
974 fndecl = func->as_fndecl ();
|
|
975
|
|
976 tree fntype = TREE_TYPE (fndecl);
|
|
977
|
|
978 tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
|
|
979
|
|
980 return build_call (loc, fn, args, require_tail_call);
|
|
981 }
|
|
982
|
|
983 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
984 call through a function pointer. */
|
|
985
|
|
986 playback::rvalue *
|
|
987 playback::context::
|
|
988 new_call_through_ptr (location *loc,
|
|
989 rvalue *fn_ptr,
|
|
990 const auto_vec<rvalue *> *args,
|
|
991 bool require_tail_call)
|
|
992 {
|
|
993 gcc_assert (fn_ptr);
|
|
994 tree t_fn_ptr = fn_ptr->as_tree ();
|
|
995
|
|
996 return build_call (loc, t_fn_ptr, args, require_tail_call);
|
|
997 }
|
|
998
|
|
999 /* Construct a tree for a cast. */
|
|
1000
|
|
1001 tree
|
|
1002 playback::context::build_cast (playback::location *loc,
|
|
1003 playback::rvalue *expr,
|
|
1004 playback::type *type_)
|
|
1005 {
|
|
1006 /* For comparison, see:
|
|
1007 - c/c-typeck.c:build_c_cast
|
|
1008 - c/c-convert.c: convert
|
|
1009 - convert.h
|
|
1010
|
|
1011 Only some kinds of cast are currently supported here. */
|
|
1012 tree t_expr = expr->as_tree ();
|
|
1013 tree t_dst_type = type_->as_tree ();
|
|
1014 tree t_ret = NULL;
|
|
1015 t_ret = targetm.convert_to_type (t_dst_type, t_expr);
|
|
1016 if (t_ret)
|
|
1017 return t_ret;
|
|
1018 enum tree_code dst_code = TREE_CODE (t_dst_type);
|
|
1019 switch (dst_code)
|
|
1020 {
|
|
1021 case INTEGER_TYPE:
|
|
1022 case ENUMERAL_TYPE:
|
|
1023 t_ret = convert_to_integer (t_dst_type, t_expr);
|
|
1024 goto maybe_fold;
|
|
1025
|
|
1026 case BOOLEAN_TYPE:
|
|
1027 /* Compare with c_objc_common_truthvalue_conversion and
|
|
1028 c_common_truthvalue_conversion. */
|
|
1029 /* For now, convert to: (t_expr != 0) */
|
|
1030 t_ret = build2 (NE_EXPR, t_dst_type,
|
|
1031 t_expr,
|
|
1032 build_int_cst (TREE_TYPE (t_expr), 0));
|
|
1033 goto maybe_fold;
|
|
1034
|
|
1035 case REAL_TYPE:
|
|
1036 t_ret = convert_to_real (t_dst_type, t_expr);
|
|
1037 goto maybe_fold;
|
|
1038
|
|
1039 case POINTER_TYPE:
|
|
1040 t_ret = build1 (NOP_EXPR, t_dst_type, t_expr);
|
|
1041 goto maybe_fold;
|
|
1042
|
|
1043 default:
|
|
1044 add_error (loc, "couldn't handle cast during playback");
|
|
1045 fprintf (stderr, "input expression:\n");
|
|
1046 debug_tree (t_expr);
|
|
1047 fprintf (stderr, "requested type:\n");
|
|
1048 debug_tree (t_dst_type);
|
|
1049 return error_mark_node;
|
|
1050
|
|
1051 maybe_fold:
|
|
1052 if (TREE_CODE (t_ret) != C_MAYBE_CONST_EXPR)
|
|
1053 t_ret = fold (t_ret);
|
|
1054 return t_ret;
|
|
1055 }
|
|
1056 }
|
|
1057
|
|
1058 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
1059 cast. */
|
|
1060
|
|
1061 playback::rvalue *
|
|
1062 playback::context::
|
|
1063 new_cast (playback::location *loc,
|
|
1064 playback::rvalue *expr,
|
|
1065 playback::type *type_)
|
|
1066 {
|
|
1067
|
|
1068 tree t_cast = build_cast (loc, expr, type_);
|
|
1069 if (loc)
|
|
1070 set_tree_location (t_cast, loc);
|
|
1071 return new rvalue (this, t_cast);
|
|
1072 }
|
|
1073
|
|
1074 /* Construct a playback::lvalue instance (wrapping a tree) for an
|
|
1075 array access. */
|
|
1076
|
|
1077 playback::lvalue *
|
|
1078 playback::context::
|
|
1079 new_array_access (location *loc,
|
|
1080 rvalue *ptr,
|
|
1081 rvalue *index)
|
|
1082 {
|
|
1083 gcc_assert (ptr);
|
|
1084 gcc_assert (index);
|
|
1085
|
|
1086 /* For comparison, see:
|
|
1087 c/c-typeck.c: build_array_ref
|
|
1088 c-family/c-common.c: pointer_int_sum
|
|
1089 */
|
|
1090 tree t_ptr = ptr->as_tree ();
|
|
1091 tree t_index = index->as_tree ();
|
|
1092 tree t_type_ptr = TREE_TYPE (t_ptr);
|
|
1093 tree t_type_star_ptr = TREE_TYPE (t_type_ptr);
|
|
1094
|
|
1095 if (TREE_CODE (t_type_ptr) == ARRAY_TYPE)
|
|
1096 {
|
|
1097 tree t_result = build4 (ARRAY_REF, t_type_star_ptr, t_ptr, t_index,
|
|
1098 NULL_TREE, NULL_TREE);
|
|
1099 if (loc)
|
|
1100 set_tree_location (t_result, loc);
|
|
1101 return new lvalue (this, t_result);
|
|
1102 }
|
|
1103 else
|
|
1104 {
|
|
1105 /* Convert index to an offset in bytes. */
|
|
1106 tree t_sizeof = size_in_bytes (t_type_star_ptr);
|
|
1107 t_index = fold_build1 (CONVERT_EXPR, sizetype, t_index);
|
|
1108 tree t_offset = build2 (MULT_EXPR, sizetype, t_index, t_sizeof);
|
|
1109
|
|
1110 /* Locate (ptr + offset). */
|
|
1111 tree t_address = build2 (POINTER_PLUS_EXPR, t_type_ptr, t_ptr, t_offset);
|
|
1112
|
|
1113 tree t_indirection = build1 (INDIRECT_REF, t_type_star_ptr, t_address);
|
|
1114 if (loc)
|
|
1115 {
|
|
1116 set_tree_location (t_sizeof, loc);
|
|
1117 set_tree_location (t_offset, loc);
|
|
1118 set_tree_location (t_address, loc);
|
|
1119 set_tree_location (t_indirection, loc);
|
|
1120 }
|
|
1121
|
|
1122 return new lvalue (this, t_indirection);
|
|
1123 }
|
|
1124 }
|
|
1125
|
|
1126 /* Construct a tree for a field access. */
|
|
1127
|
|
1128 tree
|
|
1129 playback::context::
|
|
1130 new_field_access (location *loc,
|
|
1131 tree datum,
|
|
1132 field *field)
|
|
1133 {
|
|
1134 gcc_assert (datum);
|
|
1135 gcc_assert (field);
|
|
1136
|
|
1137 /* Compare with c/c-typeck.c:lookup_field, build_indirect_ref, and
|
|
1138 build_component_ref. */
|
|
1139 tree type = TREE_TYPE (datum);
|
|
1140 gcc_assert (type);
|
|
1141 gcc_assert (TREE_CODE (type) != POINTER_TYPE);
|
|
1142
|
|
1143 tree t_field = field->as_tree ();
|
|
1144 tree ref = build3 (COMPONENT_REF, TREE_TYPE (t_field), datum,
|
|
1145 t_field, NULL_TREE);
|
|
1146 if (loc)
|
|
1147 set_tree_location (ref, loc);
|
|
1148 return ref;
|
|
1149 }
|
|
1150
|
|
1151 /* Construct a tree for a dereference. */
|
|
1152
|
|
1153 tree
|
|
1154 playback::context::
|
|
1155 new_dereference (tree ptr,
|
|
1156 location *loc)
|
|
1157 {
|
|
1158 gcc_assert (ptr);
|
|
1159
|
|
1160 tree type = TREE_TYPE (TREE_TYPE(ptr));
|
|
1161 tree datum = build1 (INDIRECT_REF, type, ptr);
|
|
1162 if (loc)
|
|
1163 set_tree_location (datum, loc);
|
|
1164 return datum;
|
|
1165 }
|
|
1166
|
|
1167 /* Construct a playback::type instance (wrapping a tree)
|
|
1168 with the given alignment. */
|
|
1169
|
|
1170 playback::type *
|
|
1171 playback::type::
|
|
1172 get_aligned (size_t alignment_in_bytes) const
|
|
1173 {
|
|
1174 tree t_new_type = build_variant_type_copy (m_inner);
|
|
1175
|
|
1176 SET_TYPE_ALIGN (t_new_type, alignment_in_bytes * BITS_PER_UNIT);
|
|
1177 TYPE_USER_ALIGN (t_new_type) = 1;
|
|
1178
|
|
1179 return new type (t_new_type);
|
|
1180 }
|
|
1181
|
|
1182 /* Construct a playback::type instance (wrapping a tree)
|
|
1183 for the given vector type. */
|
|
1184
|
|
1185 playback::type *
|
|
1186 playback::type::
|
|
1187 get_vector (size_t num_units) const
|
|
1188 {
|
|
1189 tree t_new_type = build_vector_type (m_inner, num_units);
|
|
1190 return new type (t_new_type);
|
|
1191 }
|
|
1192
|
|
1193 /* Construct a playback::lvalue instance (wrapping a tree) for a
|
|
1194 field access. */
|
|
1195
|
|
1196 playback::lvalue *
|
|
1197 playback::lvalue::
|
|
1198 access_field (location *loc,
|
|
1199 field *field)
|
|
1200 {
|
|
1201 tree datum = as_tree ();
|
|
1202 tree ref = get_context ()->new_field_access (loc, datum, field);
|
|
1203 if (!ref)
|
|
1204 return NULL;
|
|
1205 return new lvalue (get_context (), ref);
|
|
1206 }
|
|
1207
|
|
1208 /* Construct a playback::rvalue instance (wrapping a tree) for a
|
|
1209 field access. */
|
|
1210
|
|
1211 playback::rvalue *
|
|
1212 playback::rvalue::
|
|
1213 access_field (location *loc,
|
|
1214 field *field)
|
|
1215 {
|
|
1216 tree datum = as_tree ();
|
|
1217 tree ref = get_context ()->new_field_access (loc, datum, field);
|
|
1218 if (!ref)
|
|
1219 return NULL;
|
|
1220 return new rvalue (get_context (), ref);
|
|
1221 }
|
|
1222
|
|
1223 /* Construct a playback::lvalue instance (wrapping a tree) for a
|
|
1224 dereferenced field access. */
|
|
1225
|
|
1226 playback::lvalue *
|
|
1227 playback::rvalue::
|
|
1228 dereference_field (location *loc,
|
|
1229 field *field)
|
|
1230 {
|
|
1231 tree ptr = as_tree ();
|
|
1232 tree datum = get_context ()->new_dereference (ptr, loc);
|
|
1233 if (!datum)
|
|
1234 return NULL;
|
|
1235 tree ref = get_context ()->new_field_access (loc, datum, field);
|
|
1236 if (!ref)
|
|
1237 return NULL;
|
|
1238 return new lvalue (get_context (), ref);
|
|
1239 }
|
|
1240
|
|
1241 /* Construct a playback::lvalue instance (wrapping a tree) for a
|
|
1242 dereference. */
|
|
1243
|
|
1244 playback::lvalue *
|
|
1245 playback::rvalue::
|
|
1246 dereference (location *loc)
|
|
1247 {
|
|
1248 tree ptr = as_tree ();
|
|
1249 tree datum = get_context ()->new_dereference (ptr, loc);
|
|
1250 return new lvalue (get_context (), datum);
|
|
1251 }
|
|
1252
|
145
|
1253 /* Mark the lvalue saying that we need to be able to take the
|
111
|
1254 address of it; it should not be allocated in a register.
|
145
|
1255 Compare with e.g. c/c-typeck.c: c_mark_addressable really_atomic_lvalue.
|
|
1256 Returns false if a failure occurred (an error will already have been
|
|
1257 added to the active context for this case). */
|
|
1258
|
|
1259 bool
|
|
1260 playback::lvalue::
|
|
1261 mark_addressable (location *loc)
|
111
|
1262 {
|
145
|
1263 tree x = as_tree ();;
|
111
|
1264
|
|
1265 while (1)
|
|
1266 switch (TREE_CODE (x))
|
|
1267 {
|
|
1268 case COMPONENT_REF:
|
145
|
1269 if (DECL_JIT_BIT_FIELD (TREE_OPERAND (x, 1)))
|
|
1270 {
|
|
1271 gcc_assert (gcc::jit::active_playback_ctxt);
|
|
1272 gcc::jit::
|
|
1273 active_playback_ctxt->add_error (loc,
|
|
1274 "cannot take address of "
|
|
1275 "bit-field");
|
|
1276 return false;
|
|
1277 }
|
111
|
1278 /* fallthrough */
|
|
1279 case ADDR_EXPR:
|
|
1280 case ARRAY_REF:
|
|
1281 case REALPART_EXPR:
|
|
1282 case IMAGPART_EXPR:
|
|
1283 x = TREE_OPERAND (x, 0);
|
|
1284 break;
|
|
1285
|
|
1286 case COMPOUND_LITERAL_EXPR:
|
|
1287 case CONSTRUCTOR:
|
|
1288 TREE_ADDRESSABLE (x) = 1;
|
145
|
1289 return true;
|
111
|
1290
|
|
1291 case VAR_DECL:
|
|
1292 case CONST_DECL:
|
|
1293 case PARM_DECL:
|
|
1294 case RESULT_DECL:
|
|
1295 /* (we don't have a concept of a "register" declaration) */
|
|
1296 /* fallthrough */
|
|
1297 case FUNCTION_DECL:
|
|
1298 TREE_ADDRESSABLE (x) = 1;
|
|
1299 /* fallthrough */
|
|
1300 default:
|
145
|
1301 return true;
|
111
|
1302 }
|
|
1303 }
|
|
1304
|
|
1305 /* Construct a playback::rvalue instance (wrapping a tree) for an
|
|
1306 address-lookup. */
|
|
1307
|
|
1308 playback::rvalue *
|
|
1309 playback::lvalue::
|
|
1310 get_address (location *loc)
|
|
1311 {
|
|
1312 tree t_lvalue = as_tree ();
|
|
1313 tree t_thistype = TREE_TYPE (t_lvalue);
|
|
1314 tree t_ptrtype = build_pointer_type (t_thistype);
|
|
1315 tree ptr = build1 (ADDR_EXPR, t_ptrtype, t_lvalue);
|
|
1316 if (loc)
|
|
1317 get_context ()->set_tree_location (ptr, loc);
|
145
|
1318 if (mark_addressable (loc))
|
|
1319 return new rvalue (get_context (), ptr);
|
|
1320 else
|
|
1321 return NULL;
|
111
|
1322 }
|
|
1323
|
|
1324 /* The wrapper subclasses are GC-managed, but can own non-GC memory.
|
|
1325 Provide this finalization hook for calling then they are collected,
|
|
1326 which calls the finalizer vfunc. This allows them to call "release"
|
|
1327 on any vec<> within them. */
|
|
1328
|
|
1329 static void
|
|
1330 wrapper_finalizer (void *ptr)
|
|
1331 {
|
|
1332 playback::wrapper *wrapper = reinterpret_cast <playback::wrapper *> (ptr);
|
|
1333 wrapper->finalizer ();
|
|
1334 }
|
|
1335
|
|
1336 /* gcc::jit::playback::wrapper subclasses are GC-managed:
|
|
1337 allocate them using ggc_internal_cleared_alloc. */
|
|
1338
|
|
1339 void *
|
|
1340 playback::wrapper::
|
|
1341 operator new (size_t sz)
|
|
1342 {
|
|
1343 return ggc_internal_cleared_alloc (sz, wrapper_finalizer, 0, 1);
|
|
1344
|
|
1345 }
|
|
1346
|
|
1347 /* Constructor for gcc:jit::playback::function. */
|
|
1348
|
|
1349 playback::function::
|
|
1350 function (context *ctxt,
|
|
1351 tree fndecl,
|
|
1352 enum gcc_jit_function_kind kind)
|
|
1353 : m_ctxt(ctxt),
|
|
1354 m_inner_fndecl (fndecl),
|
|
1355 m_inner_bind_expr (NULL),
|
|
1356 m_kind (kind)
|
|
1357 {
|
|
1358 if (m_kind != GCC_JIT_FUNCTION_IMPORTED)
|
|
1359 {
|
|
1360 /* Create a BIND_EXPR, and within it, a statement list. */
|
|
1361 m_stmt_list = alloc_stmt_list ();
|
|
1362 m_stmt_iter = tsi_start (m_stmt_list);
|
|
1363 m_inner_block = make_node (BLOCK);
|
|
1364 m_inner_bind_expr =
|
|
1365 build3 (BIND_EXPR, void_type_node, NULL, m_stmt_list, m_inner_block);
|
|
1366 }
|
|
1367 else
|
|
1368 {
|
|
1369 m_inner_block = NULL;
|
|
1370 m_stmt_list = NULL;
|
|
1371 }
|
|
1372 }
|
|
1373
|
|
1374 /* Hand-written GC-marking hook for playback functions. */
|
|
1375
|
|
1376 void
|
|
1377 playback::function::
|
|
1378 gt_ggc_mx ()
|
|
1379 {
|
|
1380 gt_ggc_m_9tree_node (m_inner_fndecl);
|
|
1381 gt_ggc_m_9tree_node (m_inner_bind_expr);
|
|
1382 gt_ggc_m_9tree_node (m_stmt_list);
|
|
1383 gt_ggc_m_9tree_node (m_inner_block);
|
|
1384 }
|
|
1385
|
|
1386 /* Don't leak vec's internal buffer (in non-GC heap) when we are
|
|
1387 GC-ed. */
|
|
1388
|
|
1389 void
|
|
1390 playback::function::finalizer ()
|
|
1391 {
|
|
1392 m_blocks.release ();
|
|
1393 }
|
|
1394
|
|
1395 /* Get the return type of a playback function, in tree form. */
|
|
1396
|
|
1397 tree
|
|
1398 playback::function::
|
|
1399 get_return_type_as_tree () const
|
|
1400 {
|
|
1401 return TREE_TYPE (TREE_TYPE(m_inner_fndecl));
|
|
1402 }
|
|
1403
|
|
1404 /* Construct a new local within this playback::function. */
|
|
1405
|
|
1406 playback::lvalue *
|
|
1407 playback::function::
|
|
1408 new_local (location *loc,
|
|
1409 type *type,
|
|
1410 const char *name)
|
|
1411 {
|
|
1412 gcc_assert (type);
|
|
1413 gcc_assert (name);
|
|
1414 tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
|
|
1415 get_identifier (name),
|
|
1416 type->as_tree ());
|
|
1417 DECL_CONTEXT (inner) = this->m_inner_fndecl;
|
|
1418
|
|
1419 /* Prepend to BIND_EXPR_VARS: */
|
|
1420 DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
|
|
1421 BIND_EXPR_VARS (m_inner_bind_expr) = inner;
|
|
1422
|
|
1423 if (loc)
|
|
1424 set_tree_location (inner, loc);
|
|
1425 return new lvalue (m_ctxt, inner);
|
|
1426 }
|
|
1427
|
|
1428 /* Construct a new block within this playback::function. */
|
|
1429
|
|
1430 playback::block *
|
|
1431 playback::function::
|
|
1432 new_block (const char *name)
|
|
1433 {
|
|
1434 gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
|
|
1435
|
|
1436 block *result = new playback::block (this, name);
|
|
1437 m_blocks.safe_push (result);
|
|
1438 return result;
|
|
1439 }
|
|
1440
|
|
1441 /* Construct a playback::rvalue instance wrapping an ADDR_EXPR for
|
|
1442 this playback::function. */
|
|
1443
|
|
1444 playback::rvalue *
|
|
1445 playback::function::get_address (location *loc)
|
|
1446 {
|
|
1447 tree t_fndecl = as_fndecl ();
|
|
1448 tree t_fntype = TREE_TYPE (t_fndecl);
|
|
1449 tree t_fnptr = build1 (ADDR_EXPR, build_pointer_type (t_fntype), t_fndecl);
|
|
1450 if (loc)
|
|
1451 m_ctxt->set_tree_location (t_fnptr, loc);
|
|
1452 return new rvalue (m_ctxt, t_fnptr);
|
|
1453 }
|
|
1454
|
|
1455 /* Build a statement list for the function as a whole out of the
|
|
1456 lists of statements for the individual blocks, building labels
|
|
1457 for each block. */
|
|
1458
|
|
1459 void
|
|
1460 playback::function::
|
|
1461 build_stmt_list ()
|
|
1462 {
|
|
1463 int i;
|
|
1464 block *b;
|
|
1465
|
|
1466 JIT_LOG_SCOPE (m_ctxt->get_logger ());
|
|
1467
|
|
1468 FOR_EACH_VEC_ELT (m_blocks, i, b)
|
|
1469 {
|
|
1470 int j;
|
|
1471 tree stmt;
|
|
1472
|
|
1473 b->m_label_expr = build1 (LABEL_EXPR,
|
|
1474 void_type_node,
|
|
1475 b->as_label_decl ());
|
|
1476 tsi_link_after (&m_stmt_iter, b->m_label_expr, TSI_CONTINUE_LINKING);
|
|
1477
|
|
1478 FOR_EACH_VEC_ELT (b->m_stmts, j, stmt)
|
|
1479 tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
|
|
1480 }
|
|
1481 }
|
|
1482
|
|
1483 /* Finish compiling the given function, potentially running the
|
|
1484 garbage-collector.
|
|
1485 The function will have a statement list by now.
|
|
1486 Amongst other things, this gimplifies the statement list,
|
|
1487 and calls cgraph_node::finalize_function on the function. */
|
|
1488
|
|
1489 void
|
|
1490 playback::function::
|
|
1491 postprocess ()
|
|
1492 {
|
|
1493 JIT_LOG_SCOPE (m_ctxt->get_logger ());
|
|
1494
|
|
1495 if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE))
|
|
1496 debug_tree (m_stmt_list);
|
|
1497
|
|
1498 /* Do we need this to force cgraphunit.c to output the function? */
|
|
1499 if (m_kind == GCC_JIT_FUNCTION_EXPORTED)
|
|
1500 {
|
|
1501 DECL_EXTERNAL (m_inner_fndecl) = 0;
|
|
1502 DECL_PRESERVE_P (m_inner_fndecl) = 1;
|
|
1503 }
|
|
1504
|
|
1505 if (m_kind == GCC_JIT_FUNCTION_INTERNAL
|
|
1506 ||m_kind == GCC_JIT_FUNCTION_ALWAYS_INLINE)
|
|
1507 {
|
|
1508 DECL_EXTERNAL (m_inner_fndecl) = 0;
|
|
1509 TREE_PUBLIC (m_inner_fndecl) = 0;
|
|
1510 }
|
|
1511
|
|
1512 if (m_kind != GCC_JIT_FUNCTION_IMPORTED)
|
|
1513 {
|
|
1514 /* Seem to need this in gimple-low.c: */
|
|
1515 gcc_assert (m_inner_block);
|
|
1516 DECL_INITIAL (m_inner_fndecl) = m_inner_block;
|
|
1517
|
|
1518 /* how to add to function? the following appears to be how to
|
|
1519 set the body of a m_inner_fndecl: */
|
|
1520 DECL_SAVED_TREE(m_inner_fndecl) = m_inner_bind_expr;
|
|
1521
|
|
1522 /* Ensure that locals appear in the debuginfo. */
|
|
1523 BLOCK_VARS (m_inner_block) = BIND_EXPR_VARS (m_inner_bind_expr);
|
|
1524
|
|
1525 //debug_tree (m_inner_fndecl);
|
|
1526
|
|
1527 /* Convert to gimple: */
|
|
1528 //printf("about to gimplify_function_tree\n");
|
|
1529 gimplify_function_tree (m_inner_fndecl);
|
|
1530 //printf("finished gimplify_function_tree\n");
|
|
1531
|
|
1532 current_function_decl = m_inner_fndecl;
|
|
1533 if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE))
|
|
1534 dump_function_to_file (m_inner_fndecl, stderr, TDF_VOPS|TDF_MEMSYMS|TDF_LINENO);
|
|
1535 //debug_tree (m_inner_fndecl);
|
|
1536
|
|
1537 //printf("about to add to cgraph\n");
|
|
1538 /* Add to cgraph: */
|
|
1539 cgraph_node::finalize_function (m_inner_fndecl, false);
|
|
1540 /* This can trigger a collection, so we need to have all of
|
|
1541 the funcs as roots. */
|
|
1542
|
|
1543 current_function_decl = NULL;
|
|
1544 }
|
|
1545 }
|
|
1546
|
|
1547 /* Don't leak vec's internal buffer (in non-GC heap) when we are
|
|
1548 GC-ed. */
|
|
1549
|
|
1550 void
|
|
1551 playback::block::finalizer ()
|
|
1552 {
|
|
1553 m_stmts.release ();
|
|
1554 }
|
|
1555
|
|
1556 /* Add an eval of the rvalue to the function's statement list. */
|
|
1557
|
|
1558 void
|
|
1559 playback::block::
|
|
1560 add_eval (location *loc,
|
|
1561 rvalue *rvalue)
|
|
1562 {
|
|
1563 gcc_assert (rvalue);
|
|
1564
|
|
1565 if (loc)
|
|
1566 set_tree_location (rvalue->as_tree (), loc);
|
|
1567
|
|
1568 add_stmt (rvalue->as_tree ());
|
|
1569 }
|
|
1570
|
|
1571 /* Add an assignment to the function's statement list. */
|
|
1572
|
|
1573 void
|
|
1574 playback::block::
|
|
1575 add_assignment (location *loc,
|
|
1576 lvalue *lvalue,
|
|
1577 rvalue *rvalue)
|
|
1578 {
|
|
1579 gcc_assert (lvalue);
|
|
1580 gcc_assert (rvalue);
|
|
1581
|
|
1582 tree t_lvalue = lvalue->as_tree ();
|
|
1583 tree t_rvalue = rvalue->as_tree ();
|
|
1584 if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
|
|
1585 {
|
|
1586 t_rvalue = build1 (CONVERT_EXPR,
|
|
1587 TREE_TYPE (t_lvalue),
|
|
1588 t_rvalue);
|
|
1589 if (loc)
|
|
1590 set_tree_location (t_rvalue, loc);
|
|
1591 }
|
|
1592
|
|
1593 tree stmt =
|
|
1594 build2 (MODIFY_EXPR, TREE_TYPE (t_lvalue),
|
|
1595 t_lvalue, t_rvalue);
|
|
1596 if (loc)
|
|
1597 set_tree_location (stmt, loc);
|
|
1598 add_stmt (stmt);
|
|
1599 }
|
|
1600
|
|
1601 /* Add a comment to the function's statement list.
|
|
1602 For now this is done by adding a dummy label. */
|
|
1603
|
|
1604 void
|
|
1605 playback::block::
|
|
1606 add_comment (location *loc,
|
|
1607 const char *text)
|
|
1608 {
|
|
1609 /* Wrap the text in C-style comment delimiters. */
|
|
1610 size_t sz =
|
|
1611 (3 /* opening delim */
|
|
1612 + strlen (text)
|
|
1613 + 3 /* closing delim */
|
|
1614 + 1 /* terminator */);
|
|
1615 char *wrapped = (char *)ggc_internal_alloc (sz);
|
|
1616 snprintf (wrapped, sz, "/* %s */", text);
|
|
1617
|
|
1618 /* For now we simply implement this by adding a dummy label with a name
|
|
1619 containing the given text. */
|
|
1620 tree identifier = get_identifier (wrapped);
|
|
1621 tree label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
|
|
1622 identifier, void_type_node);
|
|
1623 DECL_CONTEXT (label_decl) = m_func->as_fndecl ();
|
|
1624
|
|
1625 tree label_expr = build1 (LABEL_EXPR, void_type_node, label_decl);
|
|
1626 if (loc)
|
|
1627 set_tree_location (label_expr, loc);
|
|
1628 add_stmt (label_expr);
|
|
1629 }
|
|
1630
|
|
1631 /* Add a conditional jump statement to the function's statement list. */
|
|
1632
|
|
1633 void
|
|
1634 playback::block::
|
|
1635 add_conditional (location *loc,
|
|
1636 rvalue *boolval,
|
|
1637 block *on_true,
|
|
1638 block *on_false)
|
|
1639 {
|
|
1640 gcc_assert (boolval);
|
|
1641 gcc_assert (on_true);
|
|
1642 gcc_assert (on_false);
|
|
1643
|
|
1644 /* COND_EXPR wants statement lists for the true/false operands, but we
|
|
1645 want labels.
|
|
1646 Shim it by creating jumps to the labels */
|
|
1647 tree true_jump = build1 (GOTO_EXPR, void_type_node,
|
|
1648 on_true->as_label_decl ());
|
|
1649 if (loc)
|
|
1650 set_tree_location (true_jump, loc);
|
|
1651
|
|
1652 tree false_jump = build1 (GOTO_EXPR, void_type_node,
|
|
1653 on_false->as_label_decl ());
|
|
1654 if (loc)
|
|
1655 set_tree_location (false_jump, loc);
|
|
1656
|
|
1657 tree stmt =
|
|
1658 build3 (COND_EXPR, void_type_node, boolval->as_tree (),
|
|
1659 true_jump, false_jump);
|
|
1660 if (loc)
|
|
1661 set_tree_location (stmt, loc);
|
|
1662 add_stmt (stmt);
|
|
1663 }
|
|
1664
|
|
1665 /* Add an unconditional jump statement to the function's statement list. */
|
|
1666
|
|
1667 void
|
|
1668 playback::block::
|
|
1669 add_jump (location *loc,
|
|
1670 block *target)
|
|
1671 {
|
|
1672 gcc_assert (target);
|
|
1673
|
|
1674 // see c_finish_loop
|
|
1675 //tree top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
|
|
1676 //add_stmt (top);
|
|
1677
|
|
1678 //tree stmt = build_and_jump (&LABEL_EXPR_LABEL (target->label_));
|
|
1679 TREE_USED (target->as_label_decl ()) = 1;
|
|
1680 tree stmt = build1 (GOTO_EXPR, void_type_node, target->as_label_decl ());
|
|
1681 if (loc)
|
|
1682 set_tree_location (stmt, loc);
|
|
1683 add_stmt (stmt);
|
|
1684
|
|
1685 /*
|
|
1686 from c-typeck.c:
|
|
1687 tree
|
|
1688 c_finish_goto_label (location_t loc, tree label)
|
|
1689 {
|
|
1690 tree decl = lookup_label_for_goto (loc, label);
|
|
1691 if (!decl)
|
|
1692 return NULL_TREE;
|
|
1693 TREE_USED (decl) = 1;
|
|
1694 {
|
|
1695 tree t = build1 (GOTO_EXPR, void_type_node, decl);
|
|
1696 SET_EXPR_LOCATION (t, loc);
|
|
1697 return add_stmt (t);
|
|
1698 }
|
|
1699 }
|
|
1700 */
|
|
1701
|
|
1702 }
|
|
1703
|
|
1704 /* Add a return statement to the function's statement list. */
|
|
1705
|
|
1706 void
|
|
1707 playback::block::
|
|
1708 add_return (location *loc,
|
|
1709 rvalue *rvalue)
|
|
1710 {
|
|
1711 tree modify_retval = NULL;
|
|
1712 tree return_type = m_func->get_return_type_as_tree ();
|
|
1713 if (rvalue)
|
|
1714 {
|
|
1715 tree t_lvalue = DECL_RESULT (m_func->as_fndecl ());
|
|
1716 tree t_rvalue = rvalue->as_tree ();
|
|
1717 if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
|
|
1718 t_rvalue = build1 (CONVERT_EXPR,
|
|
1719 TREE_TYPE (t_lvalue),
|
|
1720 t_rvalue);
|
|
1721 modify_retval = build2 (MODIFY_EXPR, return_type,
|
|
1722 t_lvalue, t_rvalue);
|
|
1723 if (loc)
|
|
1724 set_tree_location (modify_retval, loc);
|
|
1725 }
|
|
1726 tree return_stmt = build1 (RETURN_EXPR, return_type,
|
|
1727 modify_retval);
|
|
1728 if (loc)
|
|
1729 set_tree_location (return_stmt, loc);
|
|
1730
|
|
1731 add_stmt (return_stmt);
|
|
1732 }
|
|
1733
|
|
1734 /* Helper function for playback::block::add_switch.
|
|
1735 Construct a case label for the given range, followed by a goto stmt
|
|
1736 to the given block, appending them to stmt list *ptr_t_switch_body. */
|
|
1737
|
|
1738 static void
|
|
1739 add_case (tree *ptr_t_switch_body,
|
|
1740 tree t_low_value,
|
|
1741 tree t_high_value,
|
|
1742 playback::block *dest_block)
|
|
1743 {
|
|
1744 tree t_label = create_artificial_label (UNKNOWN_LOCATION);
|
|
1745 DECL_CONTEXT (t_label) = dest_block->get_function ()->as_fndecl ();
|
|
1746
|
|
1747 tree t_case_label =
|
|
1748 build_case_label (t_low_value, t_high_value, t_label);
|
|
1749 append_to_statement_list (t_case_label, ptr_t_switch_body);
|
|
1750
|
|
1751 tree t_goto_stmt =
|
|
1752 build1 (GOTO_EXPR, void_type_node, dest_block->as_label_decl ());
|
|
1753 append_to_statement_list (t_goto_stmt, ptr_t_switch_body);
|
|
1754 }
|
|
1755
|
|
1756 /* Add a switch statement to the function's statement list.
|
|
1757
|
131
|
1758 We create a switch body, and populate it with case labels, each
|
111
|
1759 followed by a goto to the desired block. */
|
|
1760
|
|
1761 void
|
|
1762 playback::block::
|
|
1763 add_switch (location *loc,
|
|
1764 rvalue *expr,
|
|
1765 block *default_block,
|
|
1766 const auto_vec <case_> *cases)
|
|
1767 {
|
|
1768 /* Compare with:
|
|
1769 - c/c-typeck.c: c_start_case
|
|
1770 - c-family/c-common.c:c_add_case_label
|
|
1771 - java/expr.c:expand_java_switch and expand_java_add_case
|
|
1772 We've already rejected overlaps and duplicates in
|
|
1773 libgccjit.c:case_range_validator::validate. */
|
|
1774
|
|
1775 tree t_expr = expr->as_tree ();
|
|
1776 tree t_type = TREE_TYPE (t_expr);
|
|
1777
|
|
1778 tree t_switch_body = alloc_stmt_list ();
|
|
1779
|
|
1780 int i;
|
|
1781 case_ *c;
|
|
1782 FOR_EACH_VEC_ELT (*cases, i, c)
|
|
1783 {
|
|
1784 tree t_low_value = c->m_min_value->as_tree ();
|
|
1785 tree t_high_value = c->m_max_value->as_tree ();
|
131
|
1786 add_case (&t_switch_body, t_low_value, t_high_value, c->m_dest_block);
|
111
|
1787 }
|
|
1788 /* Default label. */
|
131
|
1789 add_case (&t_switch_body, NULL_TREE, NULL_TREE, default_block);
|
|
1790
|
|
1791 tree switch_stmt = build2 (SWITCH_EXPR, t_type, t_expr, t_switch_body);
|
111
|
1792 if (loc)
|
|
1793 set_tree_location (switch_stmt, loc);
|
|
1794 add_stmt (switch_stmt);
|
|
1795 }
|
|
1796
|
|
1797 /* Constructor for gcc::jit::playback::block. */
|
|
1798
|
|
1799 playback::block::
|
|
1800 block (function *func,
|
|
1801 const char *name)
|
|
1802 : m_func (func),
|
|
1803 m_stmts ()
|
|
1804 {
|
|
1805 tree identifier;
|
|
1806
|
|
1807 gcc_assert (func);
|
|
1808 // name can be NULL
|
|
1809 if (name)
|
|
1810 identifier = get_identifier (name);
|
|
1811 else
|
|
1812 identifier = NULL;
|
|
1813 m_label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
|
|
1814 identifier, void_type_node);
|
|
1815 DECL_CONTEXT (m_label_decl) = func->as_fndecl ();
|
|
1816 m_label_expr = NULL;
|
|
1817 }
|
|
1818
|
|
1819 /* Compile a playback::context:
|
|
1820
|
|
1821 - Use the context's options to cconstruct command-line options, and
|
|
1822 call into the rest of GCC (toplev::main).
|
|
1823 - Assuming it succeeds, we have a .s file.
|
|
1824 - We then run the "postprocess" vfunc:
|
|
1825
|
|
1826 (A) In-memory compile ("gcc_jit_context_compile")
|
|
1827
|
|
1828 For an in-memory compile we have the playback::compile_to_memory
|
|
1829 subclass; "postprocess" will convert the .s file to a .so DSO,
|
|
1830 and load it in memory (via dlopen), wrapping the result up as
|
|
1831 a jit::result and returning it.
|
|
1832
|
|
1833 (B) Compile to file ("gcc_jit_context_compile_to_file")
|
|
1834
|
|
1835 When compiling to a file, we have the playback::compile_to_file
|
|
1836 subclass; "postprocess" will either copy the .s file to the
|
|
1837 destination (for GCC_JIT_OUTPUT_KIND_ASSEMBLER), or invoke
|
|
1838 the driver to convert it as necessary, copying the result. */
|
|
1839
|
|
1840 void
|
|
1841 playback::context::
|
|
1842 compile ()
|
|
1843 {
|
|
1844 JIT_LOG_SCOPE (get_logger ());
|
|
1845
|
|
1846 const char *ctxt_progname;
|
|
1847
|
|
1848 int keep_intermediates =
|
|
1849 get_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES);
|
|
1850
|
|
1851 m_tempdir = new tempdir (get_logger (), keep_intermediates);
|
|
1852 if (!m_tempdir->create ())
|
|
1853 return;
|
|
1854
|
|
1855 /* Call into the rest of gcc.
|
|
1856 For now, we have to assemble command-line options to pass into
|
|
1857 toplev::main, so that they can be parsed. */
|
|
1858
|
|
1859 /* Pass in user-provided program name as argv0, if any, so that it
|
|
1860 makes it into GCC's "progname" global, used in various diagnostics. */
|
|
1861 ctxt_progname = get_str_option (GCC_JIT_STR_OPTION_PROGNAME);
|
|
1862
|
|
1863 if (!ctxt_progname)
|
|
1864 ctxt_progname = "libgccjit.so";
|
|
1865
|
|
1866 auto_vec <recording::requested_dump> requested_dumps;
|
|
1867 m_recording_ctxt->get_all_requested_dumps (&requested_dumps);
|
|
1868
|
|
1869 /* Acquire the JIT mutex and set "this" as the active playback ctxt. */
|
|
1870 acquire_mutex ();
|
|
1871
|
131
|
1872 auto_string_vec fake_args;
|
111
|
1873 make_fake_args (&fake_args, ctxt_progname, &requested_dumps);
|
|
1874 if (errors_occurred ())
|
|
1875 {
|
|
1876 release_mutex ();
|
|
1877 return;
|
|
1878 }
|
|
1879
|
|
1880 /* This runs the compiler. */
|
|
1881 toplev toplev (get_timer (), /* external_timer */
|
|
1882 false); /* init_signals */
|
|
1883 enter_scope ("toplev::main");
|
|
1884 if (get_logger ())
|
|
1885 for (unsigned i = 0; i < fake_args.length (); i++)
|
|
1886 get_logger ()->log ("argv[%i]: %s", i, fake_args[i]);
|
|
1887 toplev.main (fake_args.length (),
|
|
1888 const_cast <char **> (fake_args.address ()));
|
|
1889 exit_scope ("toplev::main");
|
|
1890
|
|
1891 /* Extracting dumps makes use of the gcc::dump_manager, hence we
|
|
1892 need to do it between toplev::main (which creates the dump manager)
|
|
1893 and toplev::finalize (which deletes it). */
|
|
1894 extract_any_requested_dumps (&requested_dumps);
|
|
1895
|
|
1896 /* Clean up the compiler. */
|
|
1897 enter_scope ("toplev::finalize");
|
|
1898 toplev.finalize ();
|
|
1899 exit_scope ("toplev::finalize");
|
|
1900
|
|
1901 /* Ideally we would release the jit mutex here, but we can't yet since
|
|
1902 followup activities use timevars, which are global state. */
|
|
1903
|
|
1904 if (errors_occurred ())
|
|
1905 {
|
|
1906 release_mutex ();
|
|
1907 return;
|
|
1908 }
|
|
1909
|
|
1910 if (get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE))
|
|
1911 dump_generated_code ();
|
|
1912
|
|
1913 /* We now have a .s file.
|
|
1914
|
|
1915 Run any postprocessing steps. This will either convert the .s file to
|
|
1916 a .so DSO, and load it in memory (playback::compile_to_memory), or
|
|
1917 convert the .s file to the requested output format, and copy it to a
|
|
1918 given file (playback::compile_to_file). */
|
|
1919 postprocess (ctxt_progname);
|
|
1920
|
|
1921 release_mutex ();
|
|
1922 }
|
|
1923
|
|
1924 /* Implementation of class gcc::jit::playback::compile_to_memory,
|
|
1925 a subclass of gcc::jit::playback::context. */
|
|
1926
|
|
1927 /* playback::compile_to_memory's trivial constructor. */
|
|
1928
|
|
1929 playback::compile_to_memory::compile_to_memory (recording::context *ctxt) :
|
|
1930 playback::context (ctxt),
|
|
1931 m_result (NULL)
|
|
1932 {
|
|
1933 JIT_LOG_SCOPE (get_logger ());
|
|
1934 }
|
|
1935
|
|
1936 /* Implementation of the playback::context::process vfunc for compiling
|
|
1937 to memory.
|
|
1938
|
|
1939 Convert the .s file to a .so DSO, and load it in memory (via dlopen),
|
|
1940 wrapping the result up as a jit::result and returning it. */
|
|
1941
|
|
1942 void
|
|
1943 playback::compile_to_memory::postprocess (const char *ctxt_progname)
|
|
1944 {
|
|
1945 JIT_LOG_SCOPE (get_logger ());
|
|
1946 convert_to_dso (ctxt_progname);
|
|
1947 if (errors_occurred ())
|
|
1948 return;
|
|
1949 m_result = dlopen_built_dso ();
|
|
1950 }
|
|
1951
|
|
1952 /* Implementation of class gcc::jit::playback::compile_to_file,
|
|
1953 a subclass of gcc::jit::playback::context. */
|
|
1954
|
|
1955 /* playback::compile_to_file's trivial constructor. */
|
|
1956
|
|
1957 playback::compile_to_file::compile_to_file (recording::context *ctxt,
|
|
1958 enum gcc_jit_output_kind output_kind,
|
|
1959 const char *output_path) :
|
|
1960 playback::context (ctxt),
|
|
1961 m_output_kind (output_kind),
|
|
1962 m_output_path (output_path)
|
|
1963 {
|
|
1964 JIT_LOG_SCOPE (get_logger ());
|
|
1965 }
|
|
1966
|
|
1967 /* Implementation of the playback::context::process vfunc for compiling
|
|
1968 to a file.
|
|
1969
|
|
1970 Either copy the .s file to the given destination (for
|
|
1971 GCC_JIT_OUTPUT_KIND_ASSEMBLER), or invoke the driver to convert it
|
|
1972 as necessary, copying the result. */
|
|
1973
|
|
1974 void
|
|
1975 playback::compile_to_file::postprocess (const char *ctxt_progname)
|
|
1976 {
|
|
1977 JIT_LOG_SCOPE (get_logger ());
|
|
1978
|
|
1979 /* The driver takes different actions based on the filename, so
|
|
1980 we provide a filename with an appropriate suffix for the
|
|
1981 output kind, and then copy it up to the user-provided path,
|
|
1982 rather than directly compiling it to the requested output path. */
|
|
1983
|
|
1984 switch (m_output_kind)
|
|
1985 {
|
|
1986 default:
|
|
1987 gcc_unreachable ();
|
|
1988
|
|
1989 case GCC_JIT_OUTPUT_KIND_ASSEMBLER:
|
|
1990 copy_file (get_tempdir ()->get_path_s_file (),
|
|
1991 m_output_path);
|
|
1992 /* The .s file is automatically unlinked by tempdir::~tempdir. */
|
|
1993 break;
|
|
1994
|
|
1995 case GCC_JIT_OUTPUT_KIND_OBJECT_FILE:
|
|
1996 {
|
|
1997 char *tmp_o_path = ::concat (get_tempdir ()->get_path (),
|
|
1998 "/fake.o",
|
|
1999 NULL);
|
|
2000 invoke_driver (ctxt_progname,
|
|
2001 get_tempdir ()->get_path_s_file (),
|
|
2002 tmp_o_path,
|
|
2003 TV_ASSEMBLE,
|
|
2004 false, /* bool shared, */
|
|
2005 false);/* bool run_linker */
|
|
2006 if (!errors_occurred ())
|
|
2007 {
|
|
2008 copy_file (tmp_o_path,
|
|
2009 m_output_path);
|
|
2010 get_tempdir ()->add_temp_file (tmp_o_path);
|
|
2011 }
|
|
2012 else
|
|
2013 free (tmp_o_path);
|
|
2014 }
|
|
2015 break;
|
|
2016
|
|
2017 case GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY:
|
|
2018 invoke_driver (ctxt_progname,
|
|
2019 get_tempdir ()->get_path_s_file (),
|
|
2020 get_tempdir ()->get_path_so_file (),
|
|
2021 TV_ASSEMBLE,
|
|
2022 true, /* bool shared, */
|
|
2023 true);/* bool run_linker */
|
|
2024 if (!errors_occurred ())
|
|
2025 copy_file (get_tempdir ()->get_path_so_file (),
|
|
2026 m_output_path);
|
|
2027 /* The .so file is automatically unlinked by tempdir::~tempdir. */
|
|
2028 break;
|
|
2029
|
|
2030 case GCC_JIT_OUTPUT_KIND_EXECUTABLE:
|
|
2031 {
|
|
2032 char *tmp_exe_path = ::concat (get_tempdir ()->get_path (),
|
|
2033 "/fake.exe",
|
|
2034 NULL);
|
|
2035 invoke_driver (ctxt_progname,
|
|
2036 get_tempdir ()->get_path_s_file (),
|
|
2037 tmp_exe_path,
|
|
2038 TV_ASSEMBLE,
|
|
2039 false, /* bool shared, */
|
|
2040 true);/* bool run_linker */
|
|
2041 if (!errors_occurred ())
|
|
2042 {
|
|
2043 copy_file (tmp_exe_path,
|
|
2044 m_output_path);
|
|
2045 get_tempdir ()->add_temp_file (tmp_exe_path);
|
|
2046 }
|
|
2047 else
|
|
2048 free (tmp_exe_path);
|
|
2049 }
|
|
2050 break;
|
|
2051
|
|
2052 }
|
|
2053
|
|
2054 }
|
|
2055
|
|
2056 /* Copy SRC_PATH to DST_PATH, preserving permission bits (in particular,
|
|
2057 the "executable" bits).
|
|
2058
|
|
2059 Any errors that occur are reported on the context and hence count as
|
|
2060 a failure of the compile.
|
|
2061
|
|
2062 We can't in general hardlink or use "rename" from the tempdir since
|
|
2063 it might be on a different filesystem to the destination. For example,
|
|
2064 I get EXDEV: "Invalid cross-device link". */
|
|
2065
|
|
2066 void
|
|
2067 playback::compile_to_file::copy_file (const char *src_path,
|
|
2068 const char *dst_path)
|
|
2069 {
|
|
2070 JIT_LOG_SCOPE (get_logger ());
|
|
2071 if (get_logger ())
|
|
2072 {
|
|
2073 get_logger ()->log ("src_path: %s", src_path);
|
|
2074 get_logger ()->log ("dst_path: %s", dst_path);
|
|
2075 }
|
|
2076
|
|
2077 FILE *f_in = NULL;
|
|
2078 FILE *f_out = NULL;
|
|
2079 size_t total_sz_in = 0;
|
|
2080 size_t total_sz_out = 0;
|
|
2081 char buf[4096];
|
|
2082 size_t sz_in;
|
|
2083 struct stat stat_buf;
|
|
2084
|
|
2085 f_in = fopen (src_path, "rb");
|
|
2086 if (!f_in)
|
|
2087 {
|
|
2088 add_error (NULL,
|
|
2089 "unable to open %s for reading: %s",
|
|
2090 src_path,
|
|
2091 xstrerror (errno));
|
|
2092 return;
|
|
2093 }
|
|
2094
|
|
2095 /* Use stat on the filedescriptor to get the mode,
|
|
2096 so that we can copy it over (in particular, the
|
|
2097 "executable" bits). */
|
131
|
2098 if (fstat (fileno (f_in), &stat_buf) == -1)
|
111
|
2099 {
|
|
2100 add_error (NULL,
|
|
2101 "unable to fstat %s: %s",
|
|
2102 src_path,
|
|
2103 xstrerror (errno));
|
|
2104 fclose (f_in);
|
|
2105 return;
|
|
2106 }
|
|
2107
|
|
2108 f_out = fopen (dst_path, "wb");
|
|
2109 if (!f_out)
|
|
2110 {
|
|
2111 add_error (NULL,
|
|
2112 "unable to open %s for writing: %s",
|
|
2113 dst_path,
|
|
2114 xstrerror (errno));
|
|
2115 fclose (f_in);
|
|
2116 return;
|
|
2117 }
|
|
2118
|
|
2119 while ( (sz_in = fread (buf, 1, sizeof (buf), f_in)) )
|
|
2120 {
|
|
2121 total_sz_in += sz_in;
|
|
2122 size_t sz_out_remaining = sz_in;
|
|
2123 size_t sz_out_so_far = 0;
|
|
2124 while (sz_out_remaining)
|
|
2125 {
|
|
2126 size_t sz_out = fwrite (buf + sz_out_so_far,
|
|
2127 1,
|
|
2128 sz_out_remaining,
|
|
2129 f_out);
|
|
2130 gcc_assert (sz_out <= sz_out_remaining);
|
|
2131 if (!sz_out)
|
|
2132 {
|
|
2133 add_error (NULL,
|
|
2134 "error writing to %s: %s",
|
|
2135 dst_path,
|
|
2136 xstrerror (errno));
|
|
2137 fclose (f_in);
|
|
2138 fclose (f_out);
|
|
2139 return;
|
|
2140 }
|
|
2141 total_sz_out += sz_out;
|
|
2142 sz_out_so_far += sz_out;
|
|
2143 sz_out_remaining -= sz_out;
|
|
2144 }
|
|
2145 gcc_assert (sz_out_so_far == sz_in);
|
|
2146 }
|
|
2147
|
|
2148 if (!feof (f_in))
|
|
2149 add_error (NULL,
|
|
2150 "error reading from %s: %s",
|
|
2151 src_path,
|
|
2152 xstrerror (errno));
|
|
2153
|
|
2154 fclose (f_in);
|
|
2155
|
|
2156 gcc_assert (total_sz_in == total_sz_out);
|
|
2157 if (get_logger ())
|
|
2158 get_logger ()->log ("total bytes copied: %ld", total_sz_out);
|
|
2159
|
|
2160 /* Set the permissions of the copy to those of the original file,
|
|
2161 in particular the "executable" bits. */
|
131
|
2162 if (fchmod (fileno (f_out), stat_buf.st_mode) == -1)
|
111
|
2163 add_error (NULL,
|
|
2164 "error setting mode of %s: %s",
|
|
2165 dst_path,
|
|
2166 xstrerror (errno));
|
|
2167
|
|
2168 fclose (f_out);
|
|
2169 }
|
|
2170
|
|
2171 /* Helper functions for gcc::jit::playback::context::compile. */
|
|
2172
|
|
2173 /* This mutex guards gcc::jit::recording::context::compile, so that only
|
|
2174 one thread can be accessing the bulk of GCC's state at once. */
|
|
2175
|
|
2176 static pthread_mutex_t jit_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
2177
|
|
2178 /* Acquire jit_mutex and set "this" as the active playback ctxt. */
|
|
2179
|
|
2180 void
|
|
2181 playback::context::acquire_mutex ()
|
|
2182 {
|
|
2183 auto_timevar tv (get_timer (), TV_JIT_ACQUIRING_MUTEX);
|
|
2184
|
|
2185 /* Acquire the big GCC mutex. */
|
|
2186 JIT_LOG_SCOPE (get_logger ());
|
|
2187 pthread_mutex_lock (&jit_mutex);
|
131
|
2188 gcc_assert (active_playback_ctxt == NULL);
|
111
|
2189 active_playback_ctxt = this;
|
|
2190 }
|
|
2191
|
|
2192 /* Release jit_mutex and clear the active playback ctxt. */
|
|
2193
|
|
2194 void
|
|
2195 playback::context::release_mutex ()
|
|
2196 {
|
|
2197 /* Release the big GCC mutex. */
|
|
2198 JIT_LOG_SCOPE (get_logger ());
|
|
2199 gcc_assert (active_playback_ctxt == this);
|
|
2200 active_playback_ctxt = NULL;
|
|
2201 pthread_mutex_unlock (&jit_mutex);
|
|
2202 }
|
|
2203
|
|
2204 /* Callback used by gcc::jit::playback::context::make_fake_args when
|
|
2205 invoking driver_get_configure_time_options.
|
|
2206 Populate a vec <char * > with the configure-time options. */
|
|
2207
|
|
2208 static void
|
|
2209 append_arg_from_driver (const char *option, void *user_data)
|
|
2210 {
|
|
2211 gcc_assert (option);
|
|
2212 gcc_assert (user_data);
|
|
2213 vec <char *> *argvec = static_cast <vec <char *> *> (user_data);
|
|
2214 argvec->safe_push (concat ("-", option, NULL));
|
|
2215 }
|
|
2216
|
|
2217 /* Build a fake argv for toplev::main from the options set
|
|
2218 by the user on the context . */
|
|
2219
|
|
2220 void
|
|
2221 playback::context::
|
|
2222 make_fake_args (vec <char *> *argvec,
|
|
2223 const char *ctxt_progname,
|
|
2224 vec <recording::requested_dump> *requested_dumps)
|
|
2225 {
|
|
2226 JIT_LOG_SCOPE (get_logger ());
|
|
2227
|
|
2228 #define ADD_ARG(arg) argvec->safe_push (xstrdup (arg))
|
|
2229 #define ADD_ARG_TAKE_OWNERSHIP(arg) argvec->safe_push (arg)
|
|
2230
|
|
2231 ADD_ARG (ctxt_progname);
|
|
2232 ADD_ARG (get_path_c_file ());
|
|
2233 ADD_ARG ("-fPIC");
|
|
2234
|
|
2235 /* Handle int options: */
|
|
2236 switch (get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL))
|
|
2237 {
|
|
2238 default:
|
|
2239 add_error (NULL,
|
|
2240 "unrecognized optimization level: %i",
|
|
2241 get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL));
|
|
2242 return;
|
|
2243
|
|
2244 case 0:
|
|
2245 ADD_ARG ("-O0");
|
|
2246 break;
|
|
2247
|
|
2248 case 1:
|
|
2249 ADD_ARG ("-O1");
|
|
2250 break;
|
|
2251
|
|
2252 case 2:
|
|
2253 ADD_ARG ("-O2");
|
|
2254 break;
|
|
2255
|
|
2256 case 3:
|
|
2257 ADD_ARG ("-O3");
|
|
2258 break;
|
|
2259 }
|
|
2260 /* What about -Os? */
|
|
2261
|
|
2262 /* Handle bool options: */
|
|
2263 if (get_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO))
|
|
2264 ADD_ARG ("-g");
|
|
2265
|
|
2266 /* Suppress timing (and other) info. */
|
|
2267 if (!get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_SUMMARY))
|
|
2268 {
|
|
2269 ADD_ARG ("-quiet");
|
|
2270 quiet_flag = 1;
|
|
2271 }
|
|
2272
|
|
2273 /* Aggressively garbage-collect, to shake out bugs: */
|
|
2274 if (get_bool_option (GCC_JIT_BOOL_OPTION_SELFCHECK_GC))
|
|
2275 {
|
145
|
2276 ADD_ARG ("--param=ggc-min-expand=0");
|
|
2277 ADD_ARG ("--param=ggc-min-heapsize=0");
|
111
|
2278 }
|
|
2279
|
|
2280 if (get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING))
|
|
2281 {
|
|
2282 ADD_ARG ("-fdump-tree-all");
|
|
2283 ADD_ARG ("-fdump-rtl-all");
|
|
2284 ADD_ARG ("-fdump-ipa-all");
|
|
2285 }
|
|
2286
|
|
2287 /* Add "-fdump-" options for any calls to
|
|
2288 gcc_jit_context_enable_dump. */
|
|
2289 {
|
|
2290 int i;
|
|
2291 recording::requested_dump *d;
|
|
2292 FOR_EACH_VEC_ELT (*requested_dumps, i, d)
|
|
2293 {
|
|
2294 char *arg = concat ("-fdump-", d->m_dumpname, NULL);
|
|
2295 ADD_ARG_TAKE_OWNERSHIP (arg);
|
|
2296 }
|
|
2297 }
|
|
2298
|
|
2299 /* PR jit/64810: Add any target-specific default options
|
|
2300 from OPTION_DEFAULT_SPECS, normally provided by the driver
|
|
2301 in the non-jit case.
|
|
2302
|
|
2303 The target-specific code can define OPTION_DEFAULT_SPECS:
|
|
2304 default command options in the form of spec macros for the
|
|
2305 driver to expand ().
|
|
2306
|
|
2307 For cc1 etc, the driver processes OPTION_DEFAULT_SPECS and,
|
|
2308 if not overriden, injects the defaults as extra arguments to
|
|
2309 cc1 etc.
|
|
2310 For the jit case, we need to add these arguments here. The
|
|
2311 input format (using the specs language) means that we have to run
|
|
2312 part of the driver code here (driver_get_configure_time_options).
|
|
2313
|
|
2314 To avoid running the spec-expansion code every time, we just do
|
|
2315 it the first time (via a function-static flag), saving the result
|
|
2316 into a function-static vec.
|
|
2317 This flag and vec are global state (i.e. per-process).
|
|
2318 They are guarded by the jit mutex. */
|
|
2319 {
|
|
2320 static bool have_configure_time_options = false;
|
|
2321 static vec <char *> configure_time_options;
|
|
2322
|
|
2323 if (have_configure_time_options)
|
|
2324 log ("reusing cached configure-time options");
|
|
2325 else
|
|
2326 {
|
|
2327 have_configure_time_options = true;
|
|
2328 log ("getting configure-time options from driver");
|
|
2329 driver_get_configure_time_options (append_arg_from_driver,
|
|
2330 &configure_time_options);
|
|
2331 }
|
|
2332
|
|
2333 int i;
|
|
2334 char *opt;
|
|
2335
|
|
2336 if (get_logger ())
|
|
2337 FOR_EACH_VEC_ELT (configure_time_options, i, opt)
|
|
2338 log ("configure_time_options[%i]: %s", i, opt);
|
|
2339
|
|
2340 /* configure_time_options should now contain the expanded options
|
|
2341 from OPTION_DEFAULT_SPECS (if any). */
|
|
2342 FOR_EACH_VEC_ELT (configure_time_options, i, opt)
|
|
2343 {
|
|
2344 gcc_assert (opt);
|
|
2345 gcc_assert (opt[0] == '-');
|
|
2346 ADD_ARG (opt);
|
|
2347 }
|
|
2348 }
|
|
2349
|
|
2350 if (get_timer ())
|
|
2351 ADD_ARG ("-ftime-report");
|
|
2352
|
|
2353 /* Add any user-provided extra options, starting with any from
|
|
2354 parent contexts. */
|
|
2355 m_recording_ctxt->append_command_line_options (argvec);
|
|
2356
|
|
2357 #undef ADD_ARG
|
|
2358 #undef ADD_ARG_TAKE_OWNERSHIP
|
|
2359 }
|
|
2360
|
|
2361 /* The second half of the implementation of gcc_jit_context_enable_dump.
|
|
2362 Iterate through the requested dumps, reading the underlying files
|
|
2363 into heap-allocated buffers, writing pointers to the buffers into
|
|
2364 the char ** pointers provided by client code.
|
|
2365 Client code is responsible for calling free on the results. */
|
|
2366
|
|
2367 void
|
|
2368 playback::context::
|
|
2369 extract_any_requested_dumps (vec <recording::requested_dump> *requested_dumps)
|
|
2370 {
|
|
2371 JIT_LOG_SCOPE (get_logger ());
|
|
2372
|
|
2373 int i;
|
|
2374 recording::requested_dump *d;
|
|
2375 FOR_EACH_VEC_ELT (*requested_dumps, i, d)
|
|
2376 {
|
|
2377 dump_file_info *dfi;
|
|
2378 char *filename;
|
|
2379 char *content;
|
|
2380
|
|
2381 dfi = g->get_dumps ()->get_dump_file_info_by_switch (d->m_dumpname);
|
|
2382 if (!dfi)
|
|
2383 {
|
|
2384 add_error (NULL, "unrecognized dump: %s", d->m_dumpname);
|
|
2385 continue;
|
|
2386 }
|
|
2387
|
|
2388 filename = g->get_dumps ()->get_dump_file_name (dfi);
|
|
2389 content = read_dump_file (filename);
|
|
2390 *(d->m_out_ptr) = content;
|
|
2391 m_tempdir->add_temp_file (filename);
|
|
2392 }
|
|
2393 }
|
|
2394
|
|
2395 /* Helper function for playback::context::extract_any_requested_dumps
|
|
2396 (itself for use in implementation of gcc_jit_context_enable_dump).
|
|
2397
|
|
2398 Attempt to read the complete file at the given path, returning the
|
|
2399 bytes found there as a buffer.
|
|
2400 The caller is responsible for calling free on the result.
|
|
2401 Errors will be reported on the context, and lead to NULL being
|
|
2402 returned; an out-of-memory error will terminate the process. */
|
|
2403
|
|
2404 char *
|
|
2405 playback::context::read_dump_file (const char *path)
|
|
2406 {
|
|
2407 char *result = NULL;
|
|
2408 size_t total_sz = 0;
|
|
2409 char buf[4096];
|
|
2410 size_t sz;
|
|
2411 FILE *f_in;
|
|
2412
|
|
2413 f_in = fopen (path, "r");
|
|
2414 if (!f_in)
|
|
2415 {
|
|
2416 add_error (NULL, "unable to open %s for reading", path);
|
|
2417 return NULL;
|
|
2418 }
|
|
2419
|
|
2420 while ( (sz = fread (buf, 1, sizeof (buf), f_in)) )
|
|
2421 {
|
|
2422 size_t old_total_sz = total_sz;
|
|
2423 total_sz += sz;
|
|
2424 result = reinterpret_cast <char *> (xrealloc (result, total_sz + 1));
|
|
2425 memcpy (result + old_total_sz, buf, sz);
|
|
2426 }
|
|
2427
|
|
2428 if (!feof (f_in))
|
|
2429 {
|
|
2430 add_error (NULL, "error reading from %s", path);
|
|
2431 free (result);
|
|
2432 fclose (f_in);
|
|
2433 return NULL;
|
|
2434 }
|
|
2435
|
|
2436 fclose (f_in);
|
|
2437
|
|
2438 if (result)
|
|
2439 {
|
|
2440 result[total_sz] = '\0';
|
|
2441 return result;
|
|
2442 }
|
|
2443 else
|
|
2444 return xstrdup ("");
|
|
2445 }
|
|
2446
|
|
2447 /* Part of playback::context::compile ().
|
|
2448
|
|
2449 We have a .s file; we want a .so file.
|
|
2450 We could reuse parts of gcc/gcc.c to do this.
|
|
2451 For now, just use the driver binary from the install, as
|
|
2452 named in gcc-driver-name.h
|
|
2453 e.g. "x86_64-unknown-linux-gnu-gcc-5.0.0". */
|
|
2454
|
|
2455 void
|
|
2456 playback::context::
|
|
2457 convert_to_dso (const char *ctxt_progname)
|
|
2458 {
|
|
2459 JIT_LOG_SCOPE (get_logger ());
|
|
2460
|
|
2461 invoke_driver (ctxt_progname,
|
|
2462 m_tempdir->get_path_s_file (),
|
|
2463 m_tempdir->get_path_so_file (),
|
|
2464 TV_ASSEMBLE,
|
|
2465 true, /* bool shared, */
|
|
2466 true);/* bool run_linker */
|
|
2467 }
|
|
2468
|
|
2469 static const char * const gcc_driver_name = GCC_DRIVER_NAME;
|
|
2470
|
|
2471 void
|
|
2472 playback::context::
|
|
2473 invoke_driver (const char *ctxt_progname,
|
|
2474 const char *input_file,
|
|
2475 const char *output_file,
|
|
2476 timevar_id_t tv_id,
|
|
2477 bool shared,
|
|
2478 bool run_linker)
|
|
2479 {
|
|
2480 JIT_LOG_SCOPE (get_logger ());
|
|
2481
|
|
2482 bool embedded_driver
|
|
2483 = !get_inner_bool_option (INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER);
|
|
2484
|
|
2485 /* Currently this lumps together both assembling and linking into
|
|
2486 TV_ASSEMBLE. */
|
|
2487 auto_timevar assemble_timevar (get_timer (), tv_id);
|
131
|
2488 auto_string_vec argvec;
|
111
|
2489 #define ADD_ARG(arg) argvec.safe_push (xstrdup (arg))
|
|
2490
|
|
2491 ADD_ARG (gcc_driver_name);
|
|
2492
|
|
2493 add_multilib_driver_arguments (&argvec);
|
|
2494
|
|
2495 if (shared)
|
|
2496 ADD_ARG ("-shared");
|
|
2497
|
|
2498 if (!run_linker)
|
|
2499 ADD_ARG ("-c");
|
|
2500
|
|
2501 ADD_ARG (input_file);
|
|
2502 ADD_ARG ("-o");
|
|
2503 ADD_ARG (output_file);
|
|
2504
|
|
2505 /* Don't use the linker plugin.
|
|
2506 If running with just a "make" and not a "make install", then we'd
|
|
2507 run into
|
|
2508 "fatal error: -fuse-linker-plugin, but liblto_plugin.so not found"
|
|
2509 libto_plugin is a .la at build time, with it becoming installed with
|
|
2510 ".so" suffix: i.e. it doesn't exist with a .so suffix until install
|
|
2511 time. */
|
|
2512 ADD_ARG ("-fno-use-linker-plugin");
|
|
2513
|
|
2514 #if defined (DARWIN_X86) || defined (DARWIN_PPC)
|
|
2515 /* OS X's linker defaults to treating undefined symbols as errors.
|
|
2516 If the context has any imported functions or globals they will be
|
|
2517 undefined until the .so is dynamically-linked into the process.
|
|
2518 Ensure that the driver passes in "-undefined dynamic_lookup" to the
|
|
2519 linker. */
|
|
2520 ADD_ARG ("-Wl,-undefined,dynamic_lookup");
|
|
2521 #endif
|
|
2522
|
|
2523 if (0)
|
|
2524 ADD_ARG ("-v");
|
|
2525
|
145
|
2526 /* Add any user-provided driver extra options. */
|
|
2527
|
|
2528 m_recording_ctxt->append_driver_options (&argvec);
|
|
2529
|
111
|
2530 #undef ADD_ARG
|
|
2531
|
|
2532 /* pex_one's error-handling requires pname to be non-NULL. */
|
|
2533 gcc_assert (ctxt_progname);
|
|
2534
|
|
2535 if (get_logger ())
|
|
2536 for (unsigned i = 0; i < argvec.length (); i++)
|
|
2537 get_logger ()->log ("argv[%i]: %s", i, argvec[i]);
|
|
2538
|
|
2539 if (embedded_driver)
|
|
2540 invoke_embedded_driver (&argvec);
|
|
2541 else
|
|
2542 invoke_external_driver (ctxt_progname, &argvec);
|
|
2543 }
|
|
2544
|
|
2545 void
|
|
2546 playback::context::
|
|
2547 invoke_embedded_driver (const vec <char *> *argvec)
|
|
2548 {
|
|
2549 JIT_LOG_SCOPE (get_logger ());
|
|
2550 driver d (true, /* can_finalize */
|
|
2551 false); /* debug */
|
|
2552 int result = d.main (argvec->length (),
|
|
2553 const_cast <char **> (argvec->address ()));
|
|
2554 d.finalize ();
|
|
2555 if (result)
|
|
2556 add_error (NULL, "error invoking gcc driver");
|
|
2557 }
|
|
2558
|
|
2559 void
|
|
2560 playback::context::
|
|
2561 invoke_external_driver (const char *ctxt_progname,
|
|
2562 vec <char *> *argvec)
|
|
2563 {
|
|
2564 JIT_LOG_SCOPE (get_logger ());
|
|
2565 const char *errmsg;
|
|
2566 int exit_status = 0;
|
|
2567 int err = 0;
|
|
2568
|
|
2569 /* pex argv arrays are NULL-terminated. */
|
|
2570 argvec->safe_push (NULL);
|
|
2571
|
|
2572 errmsg = pex_one (PEX_SEARCH, /* int flags, */
|
|
2573 gcc_driver_name,
|
|
2574 const_cast <char *const *> (argvec->address ()),
|
|
2575 ctxt_progname, /* const char *pname */
|
|
2576 NULL, /* const char *outname */
|
|
2577 NULL, /* const char *errname */
|
|
2578 &exit_status, /* int *status */
|
|
2579 &err); /* int *err*/
|
|
2580 if (errmsg)
|
|
2581 {
|
|
2582 add_error (NULL, "error invoking gcc driver: %s", errmsg);
|
|
2583 return;
|
|
2584 }
|
|
2585
|
|
2586 /* pex_one can return a NULL errmsg when the executable wasn't
|
|
2587 found (or doesn't exist), so trap these cases also. */
|
|
2588 if (exit_status || err)
|
|
2589 {
|
|
2590 add_error (NULL,
|
|
2591 "error invoking gcc driver: exit_status: %i err: %i",
|
|
2592 exit_status, err);
|
|
2593 add_error (NULL,
|
|
2594 "whilst attempting to run a driver named: %s",
|
|
2595 gcc_driver_name);
|
|
2596 add_error (NULL,
|
|
2597 "PATH was: %s",
|
|
2598 getenv ("PATH"));
|
|
2599 return;
|
|
2600 }
|
|
2601 }
|
|
2602
|
|
2603 /* Extract the target-specific MULTILIB_DEFAULTS to
|
|
2604 multilib_defaults_raw for use by
|
|
2605 playback::context::add_multilib_driver_arguments (). */
|
|
2606
|
|
2607 #ifndef MULTILIB_DEFAULTS
|
|
2608 #define MULTILIB_DEFAULTS { "" }
|
|
2609 #endif
|
|
2610
|
|
2611 static const char *const multilib_defaults_raw[] = MULTILIB_DEFAULTS;
|
|
2612
|
|
2613 /* Helper function for playback::context::invoke_driver ().
|
|
2614
|
|
2615 32-bit and 64-bit multilib peer builds of libgccjit.so may share
|
|
2616 a driver binary. We need to pass in options to the shared driver
|
|
2617 to get the appropriate assembler/linker options for this multilib
|
|
2618 peer. */
|
|
2619
|
|
2620 void
|
|
2621 playback::context::
|
|
2622 add_multilib_driver_arguments (vec <char *> *argvec)
|
|
2623 {
|
|
2624 JIT_LOG_SCOPE (get_logger ());
|
|
2625
|
|
2626 /* Add copies of the arguments in multilib_defaults_raw to argvec,
|
|
2627 prepending each with a "-". */
|
|
2628 for (size_t i = 0; i < ARRAY_SIZE (multilib_defaults_raw); i++)
|
|
2629 if (multilib_defaults_raw[i][0])
|
|
2630 argvec->safe_push (concat ("-", multilib_defaults_raw[i], NULL));
|
|
2631 }
|
|
2632
|
|
2633 /* Dynamically-link the built DSO file into this process, using dlopen.
|
|
2634 Wrap it up within a jit::result *, and return that.
|
|
2635 Return NULL if any errors occur, reporting them on this context. */
|
|
2636
|
|
2637 result *
|
|
2638 playback::context::
|
|
2639 dlopen_built_dso ()
|
|
2640 {
|
|
2641 JIT_LOG_SCOPE (get_logger ());
|
|
2642 auto_timevar load_timevar (get_timer (), TV_LOAD);
|
|
2643 void *handle = NULL;
|
|
2644 const char *error = NULL;
|
|
2645 result *result_obj = NULL;
|
|
2646
|
|
2647 /* Clear any existing error. */
|
|
2648 dlerror ();
|
|
2649
|
|
2650 handle = dlopen (m_tempdir->get_path_so_file (),
|
|
2651 RTLD_NOW | RTLD_LOCAL);
|
|
2652 if ((error = dlerror()) != NULL) {
|
|
2653 add_error (NULL, "%s", error);
|
|
2654 }
|
|
2655 if (handle)
|
|
2656 {
|
|
2657 /* We've successfully dlopened the result; create a
|
|
2658 jit::result object to wrap it.
|
|
2659
|
|
2660 We're done with the tempdir for now, but if the user
|
|
2661 has requested debugging, the user's debugger might not
|
|
2662 be capable of dealing with the .so file being unlinked
|
|
2663 immediately, so keep it around until after the result
|
|
2664 is released. We do this by handing over ownership of
|
|
2665 the jit::tempdir to the result. See PR jit/64206. */
|
|
2666 tempdir *handover_tempdir;
|
|
2667 if (get_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO))
|
|
2668 {
|
|
2669 handover_tempdir = m_tempdir;
|
|
2670 m_tempdir = NULL;
|
|
2671 /* The tempdir will eventually be cleaned up in the
|
|
2672 jit::result's dtor. */
|
|
2673 log ("GCC_JIT_BOOL_OPTION_DEBUGINFO was set:"
|
|
2674 " handing over tempdir to jit::result");
|
|
2675 }
|
|
2676 else
|
|
2677 {
|
|
2678 handover_tempdir = NULL;
|
|
2679 /* ... and retain ownership of m_tempdir so we clean it
|
|
2680 up it the playback::context's dtor. */
|
|
2681 log ("GCC_JIT_BOOL_OPTION_DEBUGINFO was not set:"
|
|
2682 " retaining ownership of tempdir");
|
|
2683 }
|
|
2684
|
|
2685 result_obj = new result (get_logger (), handle, handover_tempdir);
|
|
2686 }
|
|
2687 else
|
|
2688 result_obj = NULL;
|
|
2689
|
|
2690 return result_obj;
|
|
2691 }
|
|
2692
|
|
2693 /* Top-level hook for playing back a recording context.
|
|
2694
|
|
2695 This plays back m_recording_ctxt, and, if no errors
|
|
2696 occurred builds statement lists for and then postprocesses
|
|
2697 every function in the result. */
|
|
2698
|
|
2699 void
|
|
2700 playback::context::
|
|
2701 replay ()
|
|
2702 {
|
|
2703 JIT_LOG_SCOPE (get_logger ());
|
|
2704 /* Adapted from c-common.c:c_common_nodes_and_builtins. */
|
|
2705 tree array_domain_type = build_index_type (size_int (200));
|
|
2706 m_char_array_type_node
|
|
2707 = build_array_type (char_type_node, array_domain_type);
|
|
2708
|
|
2709 m_const_char_ptr
|
|
2710 = build_pointer_type (build_qualified_type (char_type_node,
|
|
2711 TYPE_QUAL_CONST));
|
|
2712
|
|
2713 /* Replay the recorded events: */
|
|
2714 timevar_push (TV_JIT_REPLAY);
|
|
2715
|
|
2716 m_recording_ctxt->replay_into (this);
|
|
2717
|
|
2718 /* Clean away the temporary references from recording objects
|
|
2719 to playback objects. We have to do this now since the
|
|
2720 latter are GC-allocated, but the former don't mark these
|
|
2721 refs. Hence we must stop using them before the GC can run. */
|
|
2722 m_recording_ctxt->disassociate_from_playback ();
|
|
2723
|
|
2724 /* The builtins_manager, if any, is associated with the recording::context
|
|
2725 and might be reused for future compiles on other playback::contexts,
|
|
2726 but its m_attributes array is not GTY-labeled and hence will become
|
|
2727 nonsense if the GC runs. Purge this state. */
|
|
2728 builtins_manager *bm = get_builtins_manager ();
|
|
2729 if (bm)
|
|
2730 bm->finish_playback ();
|
|
2731
|
|
2732 timevar_pop (TV_JIT_REPLAY);
|
|
2733
|
|
2734 if (!errors_occurred ())
|
|
2735 {
|
|
2736 int i;
|
|
2737 function *func;
|
|
2738
|
|
2739 /* No GC can happen yet; process the cached source locations. */
|
|
2740 handle_locations ();
|
|
2741
|
|
2742 /* We've now created tree nodes for the stmts in the various blocks
|
|
2743 in each function, but we haven't built each function's single stmt
|
|
2744 list yet. Do so now. */
|
|
2745 FOR_EACH_VEC_ELT (m_functions, i, func)
|
|
2746 func->build_stmt_list ();
|
|
2747
|
|
2748 /* No GC can have happened yet. */
|
|
2749
|
|
2750 /* Postprocess the functions. This could trigger GC. */
|
|
2751 FOR_EACH_VEC_ELT (m_functions, i, func)
|
|
2752 {
|
|
2753 gcc_assert (func);
|
|
2754 func->postprocess ();
|
|
2755 }
|
|
2756 }
|
|
2757 }
|
|
2758
|
|
2759 /* Dump the generated .s file to stderr. */
|
|
2760
|
|
2761 void
|
|
2762 playback::context::
|
|
2763 dump_generated_code ()
|
|
2764 {
|
|
2765 JIT_LOG_SCOPE (get_logger ());
|
|
2766 char buf[4096];
|
|
2767 size_t sz;
|
|
2768 FILE *f_in = fopen (get_path_s_file (), "r");
|
|
2769 if (!f_in)
|
|
2770 return;
|
|
2771
|
|
2772 while ( (sz = fread (buf, 1, sizeof (buf), f_in)) )
|
|
2773 fwrite (buf, 1, sz, stderr);
|
|
2774
|
|
2775 fclose (f_in);
|
|
2776 }
|
|
2777
|
|
2778 /* Get the supposed path of the notional "fake.c" file within the
|
|
2779 tempdir. This file doesn't exist, but the rest of the compiler
|
|
2780 needs a name. */
|
|
2781
|
|
2782 const char *
|
|
2783 playback::context::
|
|
2784 get_path_c_file () const
|
|
2785 {
|
|
2786 return m_tempdir->get_path_c_file ();
|
|
2787 }
|
|
2788
|
|
2789 /* Get the path of the assembler output file "fake.s" file within the
|
|
2790 tempdir. */
|
|
2791
|
|
2792 const char *
|
|
2793 playback::context::
|
|
2794 get_path_s_file () const
|
|
2795 {
|
|
2796 return m_tempdir->get_path_s_file ();
|
|
2797 }
|
|
2798
|
|
2799 /* Get the path of the DSO object file "fake.so" file within the
|
|
2800 tempdir. */
|
|
2801
|
|
2802 const char *
|
|
2803 playback::context::
|
|
2804 get_path_so_file () const
|
|
2805 {
|
|
2806 return m_tempdir->get_path_so_file ();
|
|
2807 }
|
|
2808
|
|
2809 /* qsort comparator for comparing pairs of playback::source_line *,
|
|
2810 ordering them by line number. */
|
|
2811
|
|
2812 static int
|
|
2813 line_comparator (const void *lhs, const void *rhs)
|
|
2814 {
|
|
2815 const playback::source_line *line_lhs = \
|
|
2816 *static_cast<const playback::source_line * const*> (lhs);
|
|
2817 const playback::source_line *line_rhs = \
|
|
2818 *static_cast<const playback::source_line * const*> (rhs);
|
|
2819 return line_lhs->get_line_num () - line_rhs->get_line_num ();
|
|
2820 }
|
|
2821
|
|
2822 /* qsort comparator for comparing pairs of playback::location *,
|
|
2823 ordering them by column number. */
|
|
2824
|
|
2825 static int
|
|
2826 location_comparator (const void *lhs, const void *rhs)
|
|
2827 {
|
|
2828 const playback::location *loc_lhs = \
|
|
2829 *static_cast<const playback::location * const *> (lhs);
|
|
2830 const playback::location *loc_rhs = \
|
|
2831 *static_cast<const playback::location * const *> (rhs);
|
|
2832 return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
|
|
2833 }
|
|
2834
|
|
2835 /* Our API allows locations to be created in arbitrary orders, but the
|
|
2836 linemap API requires locations to be created in ascending order
|
|
2837 as if we were tokenizing files.
|
|
2838
|
|
2839 This hook sorts all of the locations that have been created, and
|
|
2840 calls into the linemap API, creating linemap entries in sorted order
|
|
2841 for our locations. */
|
|
2842
|
|
2843 void
|
|
2844 playback::context::
|
|
2845 handle_locations ()
|
|
2846 {
|
|
2847 /* Create the source code locations, following the ordering rules
|
|
2848 imposed by the linemap API.
|
|
2849
|
|
2850 line_table is a global. */
|
|
2851 JIT_LOG_SCOPE (get_logger ());
|
|
2852 int i;
|
|
2853 source_file *file;
|
|
2854
|
|
2855 FOR_EACH_VEC_ELT (m_source_files, i, file)
|
|
2856 {
|
|
2857 linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
|
|
2858
|
|
2859 /* Sort lines by ascending line numbers. */
|
|
2860 file->m_source_lines.qsort (&line_comparator);
|
|
2861
|
|
2862 int j;
|
|
2863 source_line *line;
|
|
2864 FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
|
|
2865 {
|
|
2866 int k;
|
|
2867 location *loc;
|
|
2868
|
|
2869 /* Sort locations in line by ascending column numbers. */
|
|
2870 line->m_locations.qsort (&location_comparator);
|
|
2871
|
|
2872 /* Determine maximum column within this line. */
|
|
2873 gcc_assert (line->m_locations.length () > 0);
|
|
2874 location *final_column =
|
|
2875 line->m_locations[line->m_locations.length () - 1];
|
|
2876 int max_col = final_column->get_column_num ();
|
|
2877
|
|
2878 linemap_line_start (line_table, line->get_line_num (), max_col);
|
|
2879 FOR_EACH_VEC_ELT (line->m_locations, k, loc)
|
|
2880 {
|
|
2881 loc->m_srcloc = \
|
|
2882 linemap_position_for_column (line_table, loc->get_column_num ());
|
|
2883 }
|
|
2884 }
|
|
2885
|
|
2886 linemap_add (line_table, LC_LEAVE, false, NULL, 0);
|
|
2887 }
|
|
2888
|
|
2889 /* line_table should now be populated; every playback::location should
|
|
2890 now have an m_srcloc. */
|
|
2891
|
|
2892 /* Now assign them to tree nodes as appropriate. */
|
|
2893 std::pair<tree, location *> *cached_location;
|
|
2894
|
|
2895 FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location)
|
|
2896 {
|
|
2897 tree t = cached_location->first;
|
145
|
2898 location_t srcloc = cached_location->second->m_srcloc;
|
111
|
2899
|
|
2900 /* This covers expressions: */
|
|
2901 if (CAN_HAVE_LOCATION_P (t))
|
|
2902 SET_EXPR_LOCATION (t, srcloc);
|
|
2903 else if (CODE_CONTAINS_STRUCT(TREE_CODE(t), TS_DECL_MINIMAL))
|
|
2904 DECL_SOURCE_LOCATION (t) = srcloc;
|
|
2905 else
|
|
2906 {
|
|
2907 /* Don't know how to set location on this node. */
|
|
2908 }
|
|
2909 }
|
|
2910 }
|
|
2911
|
|
2912 /* We handle errors on a playback::context by adding them to the
|
|
2913 corresponding recording::context. */
|
|
2914
|
|
2915 void
|
|
2916 playback::context::
|
|
2917 add_error (location *loc, const char *fmt, ...)
|
|
2918 {
|
|
2919 va_list ap;
|
|
2920 va_start (ap, fmt);
|
|
2921 m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
|
|
2922 fmt, ap);
|
|
2923 va_end (ap);
|
|
2924 }
|
|
2925
|
|
2926 /* We handle errors on a playback::context by adding them to the
|
|
2927 corresponding recording::context. */
|
|
2928
|
|
2929 void
|
|
2930 playback::context::
|
|
2931 add_error_va (location *loc, const char *fmt, va_list ap)
|
|
2932 {
|
|
2933 m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL,
|
|
2934 fmt, ap);
|
|
2935 }
|
|
2936
|
|
2937 /* Report a diagnostic up to the jit context as an error,
|
|
2938 so that the compilation is treated as a failure.
|
|
2939 For now, any kind of diagnostic is treated as an error by the jit
|
|
2940 API. */
|
|
2941
|
|
2942 void
|
|
2943 playback::context::
|
|
2944 add_diagnostic (struct diagnostic_context *diag_context,
|
|
2945 struct diagnostic_info *diagnostic)
|
|
2946 {
|
|
2947 /* At this point the text has been formatted into the pretty-printer's
|
|
2948 output buffer. */
|
|
2949 pretty_printer *pp = diag_context->printer;
|
|
2950 const char *text = pp_formatted_text (pp);
|
|
2951
|
|
2952 /* Get location information (if any) from the diagnostic.
|
|
2953 The recording::context::add_error[_va] methods require a
|
|
2954 recording::location. We can't lookup the playback::location
|
|
2955 from the file/line/column since any playback location instances
|
|
2956 may have been garbage-collected away by now, so instead we create
|
|
2957 another recording::location directly. */
|
|
2958 location_t gcc_loc = diagnostic_location (diagnostic);
|
|
2959 recording::location *rec_loc = NULL;
|
|
2960 if (gcc_loc)
|
|
2961 {
|
|
2962 expanded_location exploc = expand_location (gcc_loc);
|
|
2963 if (exploc.file)
|
|
2964 rec_loc = m_recording_ctxt->new_location (exploc.file,
|
|
2965 exploc.line,
|
|
2966 exploc.column,
|
|
2967 false);
|
|
2968 }
|
|
2969
|
|
2970 m_recording_ctxt->add_error (rec_loc, "%s", text);
|
|
2971 pp_clear_output_area (pp);
|
|
2972 }
|
|
2973
|
|
2974 /* Dealing with the linemap API. */
|
|
2975
|
|
2976 /* Construct a playback::location for a recording::location, if it
|
|
2977 doesn't exist already. */
|
|
2978
|
|
2979 playback::location *
|
|
2980 playback::context::
|
|
2981 new_location (recording::location *rloc,
|
|
2982 const char *filename,
|
|
2983 int line,
|
|
2984 int column)
|
|
2985 {
|
|
2986 /* Get the source_file for filename, creating if necessary. */
|
|
2987 source_file *src_file = get_source_file (filename);
|
|
2988 /* Likewise for the line within the file. */
|
|
2989 source_line *src_line = src_file->get_source_line (line);
|
|
2990 /* Likewise for the column within the line. */
|
|
2991 location *loc = src_line->get_location (rloc, column);
|
|
2992 return loc;
|
|
2993 }
|
|
2994
|
|
2995 /* Deferred setting of the location for a given tree, by adding the
|
|
2996 (tree, playback::location) pair to a list of deferred associations.
|
|
2997 We will actually set the location on the tree later on once
|
145
|
2998 the location_t for the playback::location exists. */
|
111
|
2999
|
|
3000 void
|
|
3001 playback::context::
|
|
3002 set_tree_location (tree t, location *loc)
|
|
3003 {
|
|
3004 gcc_assert (loc);
|
|
3005 m_cached_locations.safe_push (std::make_pair (t, loc));
|
|
3006 }
|
|
3007
|
|
3008
|
|
3009 /* Construct a playback::source_file for the given source
|
|
3010 filename, if it doesn't exist already. */
|
|
3011
|
|
3012 playback::source_file *
|
|
3013 playback::context::
|
|
3014 get_source_file (const char *filename)
|
|
3015 {
|
|
3016 /* Locate the file.
|
|
3017 For simplicitly, this is currently a linear search.
|
|
3018 Replace with a hash if this shows up in the profile. */
|
|
3019 int i;
|
|
3020 source_file *file;
|
|
3021 tree ident_filename = get_identifier (filename);
|
|
3022
|
|
3023 FOR_EACH_VEC_ELT (m_source_files, i, file)
|
|
3024 if (file->filename_as_tree () == ident_filename)
|
|
3025 return file;
|
|
3026
|
|
3027 /* Not found. */
|
|
3028 file = new source_file (ident_filename);
|
|
3029 m_source_files.safe_push (file);
|
|
3030 return file;
|
|
3031 }
|
|
3032
|
|
3033 /* Constructor for gcc::jit::playback::source_file. */
|
|
3034
|
|
3035 playback::source_file::source_file (tree filename) :
|
|
3036 m_source_lines (),
|
|
3037 m_filename (filename)
|
|
3038 {
|
|
3039 }
|
|
3040
|
|
3041 /* Don't leak vec's internal buffer (in non-GC heap) when we are
|
|
3042 GC-ed. */
|
|
3043
|
|
3044 void
|
|
3045 playback::source_file::finalizer ()
|
|
3046 {
|
|
3047 m_source_lines.release ();
|
|
3048 }
|
|
3049
|
|
3050 /* Construct a playback::source_line for the given line
|
|
3051 within this source file, if one doesn't exist already. */
|
|
3052
|
|
3053 playback::source_line *
|
|
3054 playback::source_file::
|
|
3055 get_source_line (int line_num)
|
|
3056 {
|
|
3057 /* Locate the line.
|
|
3058 For simplicitly, this is currently a linear search.
|
|
3059 Replace with a hash if this shows up in the profile. */
|
|
3060 int i;
|
|
3061 source_line *line;
|
|
3062
|
|
3063 FOR_EACH_VEC_ELT (m_source_lines, i, line)
|
|
3064 if (line->get_line_num () == line_num)
|
|
3065 return line;
|
|
3066
|
|
3067 /* Not found. */
|
|
3068 line = new source_line (this, line_num);
|
|
3069 m_source_lines.safe_push (line);
|
|
3070 return line;
|
|
3071 }
|
|
3072
|
|
3073 /* Constructor for gcc::jit::playback::source_line. */
|
|
3074
|
|
3075 playback::source_line::source_line (source_file *file, int line_num) :
|
|
3076 m_locations (),
|
|
3077 m_source_file (file),
|
|
3078 m_line_num (line_num)
|
|
3079 {
|
|
3080 }
|
|
3081
|
|
3082 /* Don't leak vec's internal buffer (in non-GC heap) when we are
|
|
3083 GC-ed. */
|
|
3084
|
|
3085 void
|
|
3086 playback::source_line::finalizer ()
|
|
3087 {
|
|
3088 m_locations.release ();
|
|
3089 }
|
|
3090
|
|
3091 /* Construct a playback::location for the given column
|
|
3092 within this line of a specific source file, if one doesn't exist
|
|
3093 already. */
|
|
3094
|
|
3095 playback::location *
|
|
3096 playback::source_line::
|
|
3097 get_location (recording::location *rloc, int column_num)
|
|
3098 {
|
|
3099 int i;
|
|
3100 location *loc;
|
|
3101
|
|
3102 /* Another linear search that probably should be a hash table. */
|
|
3103 FOR_EACH_VEC_ELT (m_locations, i, loc)
|
|
3104 if (loc->get_column_num () == column_num)
|
|
3105 return loc;
|
|
3106
|
|
3107 /* Not found. */
|
|
3108 loc = new location (rloc, this, column_num);
|
|
3109 m_locations.safe_push (loc);
|
|
3110 return loc;
|
|
3111 }
|
|
3112
|
|
3113 /* Constructor for gcc::jit::playback::location. */
|
|
3114
|
|
3115 playback::location::location (recording::location *loc,
|
|
3116 source_line *line,
|
|
3117 int column_num) :
|
|
3118 m_srcloc (UNKNOWN_LOCATION),
|
|
3119 m_recording_loc (loc),
|
|
3120 m_line (line),
|
|
3121 m_column_num(column_num)
|
|
3122 {
|
|
3123 }
|
|
3124
|
|
3125 /* The active gcc::jit::playback::context instance. This is a singleton,
|
|
3126 guarded by jit_mutex. */
|
|
3127
|
|
3128 playback::context *active_playback_ctxt;
|
|
3129
|
|
3130 } // namespace gcc::jit
|
|
3131
|
|
3132 } // namespace gcc
|