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