1/* Internals of libgccjit: classes for playing back recorded API calls. 2 Copyright (C) 2013-2015 Free Software Foundation, Inc. 3 Contributed by David Malcolm <dmalcolm@redhat.com>. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it 8under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 3, or (at your option) 10any later version. 11 12GCC is distributed in the hope that it will be useful, but 13WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along 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 "opts.h" 25#include "hashtab.h" 26#include "hash-set.h" 27#include "machmode.h" 28#include "input.h" 29#include "statistics.h" 30#include "vec.h" 31#include "double-int.h" 32#include "real.h" 33#include "fixed-value.h" 34#include "alias.h" 35#include "flags.h" 36#include "symtab.h" 37#include "tree-core.h" 38#include "inchash.h" 39#include "tree.h" 40#include "hash-map.h" 41#include "is-a.h" 42#include "plugin-api.h" 43#include "vec.h" 44#include "hashtab.h" 45#include "machmode.h" 46#include "tm.h" 47#include "hard-reg-set.h" 48#include "function.h" 49#include "ipa-ref.h" 50#include "dumpfile.h" 51#include "cgraph.h" 52#include "toplev.h" 53#include "timevar.h" 54#include "tree-cfg.h" 55#include "target.h" 56#include "convert.h" 57#include "stringpool.h" 58#include "stor-layout.h" 59#include "print-tree.h" 60#include "gimplify.h" 61#include "gcc-driver-name.h" 62#include "attribs.h" 63#include "context.h" 64#include "fold-const.h" 65#include "debug.h" 66#include "gcc.h" 67 68#include "jit-common.h" 69#include "jit-logging.h" 70#include "jit-playback.h" 71#include "jit-result.h" 72#include "jit-builtins.h" 73#include "jit-tempdir.h" 74 75 76/* gcc::jit::playback::context::build_cast uses the convert.h API, 77 which in turn requires the frontend to provide a "convert" 78 function, apparently as a fallback. 79 80 Hence we provide this dummy one, with the requirement that any casts 81 are handled before reaching this. */ 82extern tree convert (tree type, tree expr); 83 84tree 85convert (tree dst_type, tree expr) 86{ 87 gcc_assert (gcc::jit::active_playback_ctxt); 88 gcc::jit::active_playback_ctxt->add_error (NULL, "unhandled conversion"); 89 fprintf (stderr, "input expression:\n"); 90 debug_tree (expr); 91 fprintf (stderr, "requested type:\n"); 92 debug_tree (dst_type); 93 return error_mark_node; 94} 95 96namespace gcc { 97namespace jit { 98 99/********************************************************************** 100 Playback. 101 **********************************************************************/ 102 103/* The constructor for gcc::jit::playback::context. */ 104 105playback::context::context (recording::context *ctxt) 106 : log_user (ctxt->get_logger ()), 107 m_recording_ctxt (ctxt), 108 m_tempdir (NULL), 109 m_char_array_type_node (NULL), 110 m_const_char_ptr (NULL) 111{ 112 JIT_LOG_SCOPE (get_logger ()); 113 m_functions.create (0); 114 m_globals.create (0); 115 m_source_files.create (0); 116 m_cached_locations.create (0); 117} 118 119/* The destructor for gcc::jit::playback::context. */ 120 121playback::context::~context () 122{ 123 JIT_LOG_SCOPE (get_logger ()); 124 125 /* Normally the playback::context is responsible for cleaning up the 126 tempdir (including "fake.so" within the filesystem). 127 128 In the normal case, clean it up now. 129 130 However m_tempdir can be NULL if the context has handed over 131 responsibility for the tempdir cleanup to the jit::result object, so 132 that the cleanup can be delayed (see PR jit/64206). If that's the 133 case this "delete NULL;" is a no-op. */ 134 delete m_tempdir; 135 136 m_functions.release (); 137} 138 139/* A playback::context can reference GC-managed pointers. Mark them 140 ("by hand", rather than by gengtype). 141 142 This is called on the active playback context (if any) by the 143 my_ggc_walker hook in the jit_root_table in dummy-frontend.c. */ 144 145void 146playback::context:: 147gt_ggc_mx () 148{ 149 int i; 150 function *func; 151 FOR_EACH_VEC_ELT (m_functions, i, func) 152 { 153 if (ggc_test_and_set_mark (func)) 154 func->gt_ggc_mx (); 155 } 156} 157 158/* Given an enum gcc_jit_types value, get a "tree" type. */ 159 160static tree 161get_tree_node_for_type (enum gcc_jit_types type_) 162{ 163 switch (type_) 164 { 165 case GCC_JIT_TYPE_VOID: 166 return void_type_node; 167 168 case GCC_JIT_TYPE_VOID_PTR: 169 return ptr_type_node; 170 171 case GCC_JIT_TYPE_BOOL: 172 return boolean_type_node; 173 174 case GCC_JIT_TYPE_CHAR: 175 return char_type_node; 176 case GCC_JIT_TYPE_SIGNED_CHAR: 177 return signed_char_type_node; 178 case GCC_JIT_TYPE_UNSIGNED_CHAR: 179 return unsigned_char_type_node; 180 181 case GCC_JIT_TYPE_SHORT: 182 return short_integer_type_node; 183 case GCC_JIT_TYPE_UNSIGNED_SHORT: 184 return short_unsigned_type_node; 185 186 case GCC_JIT_TYPE_CONST_CHAR_PTR: 187 { 188 tree const_char = build_qualified_type (char_type_node, 189 TYPE_QUAL_CONST); 190 return build_pointer_type (const_char); 191 } 192 193 case GCC_JIT_TYPE_INT: 194 return integer_type_node; 195 case GCC_JIT_TYPE_UNSIGNED_INT: 196 return unsigned_type_node; 197 198 case GCC_JIT_TYPE_LONG: 199 return long_integer_type_node; 200 case GCC_JIT_TYPE_UNSIGNED_LONG: 201 return long_unsigned_type_node; 202 203 case GCC_JIT_TYPE_LONG_LONG: 204 return long_long_integer_type_node; 205 case GCC_JIT_TYPE_UNSIGNED_LONG_LONG: 206 return long_long_unsigned_type_node; 207 208 case GCC_JIT_TYPE_FLOAT: 209 return float_type_node; 210 case GCC_JIT_TYPE_DOUBLE: 211 return double_type_node; 212 case GCC_JIT_TYPE_LONG_DOUBLE: 213 return long_double_type_node; 214 215 case GCC_JIT_TYPE_SIZE_T: 216 return size_type_node; 217 218 case GCC_JIT_TYPE_FILE_PTR: 219 return fileptr_type_node; 220 221 case GCC_JIT_TYPE_COMPLEX_FLOAT: 222 return complex_float_type_node; 223 case GCC_JIT_TYPE_COMPLEX_DOUBLE: 224 return complex_double_type_node; 225 case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE: 226 return complex_long_double_type_node; 227 } 228 229 return NULL; 230} 231 232/* Construct a playback::type instance (wrapping a tree) for the given 233 enum value. */ 234 235playback::type * 236playback::context:: 237get_type (enum gcc_jit_types type_) 238{ 239 tree type_node = get_tree_node_for_type (type_); 240 if (NULL == type_node) 241 { 242 add_error (NULL, 243 "unrecognized (enum gcc_jit_types) value: %i", type_); 244 return NULL; 245 } 246 247 return new type (type_node); 248} 249 250/* Construct a playback::type instance (wrapping a tree) for the given 251 array type. */ 252 253playback::type * 254playback::context:: 255new_array_type (playback::location *loc, 256 playback::type *element_type, 257 int num_elements) 258{ 259 gcc_assert (element_type); 260 261 tree t = build_array_type_nelts (element_type->as_tree (), 262 num_elements); 263 layout_type (t); 264 265 if (loc) 266 set_tree_location (t, loc); 267 268 return new type (t); 269} 270 271/* Construct a playback::field instance (wrapping a tree). */ 272 273playback::field * 274playback::context:: 275new_field (location *loc, 276 type *type, 277 const char *name) 278{ 279 gcc_assert (type); 280 gcc_assert (name); 281 282 /* compare with c/c-decl.c:grokfield and grokdeclarator. */ 283 tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL, 284 get_identifier (name), type->as_tree ()); 285 286 if (loc) 287 set_tree_location (decl, loc); 288 289 return new field (decl); 290} 291 292/* Construct a playback::compound_type instance (wrapping a tree). */ 293 294playback::compound_type * 295playback::context:: 296new_compound_type (location *loc, 297 const char *name, 298 bool is_struct) /* else is union */ 299{ 300 gcc_assert (name); 301 302 /* Compare with c/c-decl.c: start_struct. */ 303 304 tree t = make_node (is_struct ? RECORD_TYPE : UNION_TYPE); 305 TYPE_NAME (t) = get_identifier (name); 306 TYPE_SIZE (t) = 0; 307 308 if (loc) 309 set_tree_location (t, loc); 310 311 return new compound_type (t); 312} 313 314void 315playback::compound_type::set_fields (const auto_vec<playback::field *> *fields) 316{ 317 /* Compare with c/c-decl.c: finish_struct. */ 318 tree t = as_tree (); 319 320 tree fieldlist = NULL; 321 for (unsigned i = 0; i < fields->length (); i++) 322 { 323 field *f = (*fields)[i]; 324 DECL_CONTEXT (f->as_tree ()) = t; 325 fieldlist = chainon (f->as_tree (), fieldlist); 326 } 327 fieldlist = nreverse (fieldlist); 328 TYPE_FIELDS (t) = fieldlist; 329 330 layout_type (t); 331} 332 333/* Construct a playback::type instance (wrapping a tree) for a function 334 type. */ 335 336playback::type * 337playback::context:: 338new_function_type (type *return_type, 339 const auto_vec<type *> *param_types, 340 int is_variadic) 341{ 342 int i; 343 type *param_type; 344 345 tree *arg_types = (tree *)xcalloc(param_types->length (), sizeof(tree*)); 346 347 FOR_EACH_VEC_ELT (*param_types, i, param_type) 348 arg_types[i] = param_type->as_tree (); 349 350 tree fn_type; 351 if (is_variadic) 352 fn_type = 353 build_varargs_function_type_array (return_type->as_tree (), 354 param_types->length (), 355 arg_types); 356 else 357 fn_type = build_function_type_array (return_type->as_tree (), 358 param_types->length (), 359 arg_types); 360 free (arg_types); 361 362 return new type (fn_type); 363} 364 365/* Construct a playback::param instance (wrapping a tree). */ 366 367playback::param * 368playback::context:: 369new_param (location *loc, 370 type *type, 371 const char *name) 372{ 373 gcc_assert (type); 374 gcc_assert (name); 375 tree inner = build_decl (UNKNOWN_LOCATION, PARM_DECL, 376 get_identifier (name), type->as_tree ()); 377 if (loc) 378 set_tree_location (inner, loc); 379 380 return new param (this, inner); 381} 382 383/* Construct a playback::function instance. */ 384 385playback::function * 386playback::context:: 387new_function (location *loc, 388 enum gcc_jit_function_kind kind, 389 type *return_type, 390 const char *name, 391 const auto_vec<param *> *params, 392 int is_variadic, 393 enum built_in_function builtin_id) 394{ 395 int i; 396 param *param; 397 398 //can return_type be NULL? 399 gcc_assert (name); 400 401 tree *arg_types = (tree *)xcalloc(params->length (), sizeof(tree*)); 402 FOR_EACH_VEC_ELT (*params, i, param) 403 arg_types[i] = TREE_TYPE (param->as_tree ()); 404 405 tree fn_type; 406 if (is_variadic) 407 fn_type = build_varargs_function_type_array (return_type->as_tree (), 408 params->length (), arg_types); 409 else 410 fn_type = build_function_type_array (return_type->as_tree (), 411 params->length (), arg_types); 412 free (arg_types); 413 414 /* FIXME: this uses input_location: */ 415 tree fndecl = build_fn_decl (name, fn_type); 416 417 if (loc) 418 set_tree_location (fndecl, loc); 419 420 tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, 421 NULL_TREE, return_type->as_tree ()); 422 DECL_ARTIFICIAL (resdecl) = 1; 423 DECL_IGNORED_P (resdecl) = 1; 424 DECL_RESULT (fndecl) = resdecl; 425 426 if (builtin_id) 427 { 428 DECL_FUNCTION_CODE (fndecl) = builtin_id; 429 gcc_assert (loc == NULL); 430 DECL_SOURCE_LOCATION (fndecl) = BUILTINS_LOCATION; 431 432 DECL_BUILT_IN_CLASS (fndecl) = 433 builtins_manager::get_class (builtin_id); 434 set_builtin_decl (builtin_id, fndecl, 435 builtins_manager::implicit_p (builtin_id)); 436 437 builtins_manager *bm = get_builtins_manager (); 438 tree attrs = bm->get_attrs_tree (builtin_id); 439 if (attrs) 440 decl_attributes (&fndecl, attrs, ATTR_FLAG_BUILT_IN); 441 else 442 decl_attributes (&fndecl, NULL_TREE, 0); 443 } 444 445 if (kind != GCC_JIT_FUNCTION_IMPORTED) 446 { 447 tree param_decl_list = NULL; 448 FOR_EACH_VEC_ELT (*params, i, param) 449 { 450 param_decl_list = chainon (param->as_tree (), param_decl_list); 451 } 452 453 /* The param list was created in reverse order; fix it: */ 454 param_decl_list = nreverse (param_decl_list); 455 456 tree t; 457 for (t = param_decl_list; t; t = DECL_CHAIN (t)) 458 { 459 DECL_CONTEXT (t) = fndecl; 460 DECL_ARG_TYPE (t) = TREE_TYPE (t); 461 } 462 463 /* Set it up on DECL_ARGUMENTS */ 464 DECL_ARGUMENTS(fndecl) = param_decl_list; 465 } 466 467 if (kind == GCC_JIT_FUNCTION_ALWAYS_INLINE) 468 { 469 DECL_DECLARED_INLINE_P (fndecl) = 1; 470 471 /* Add attribute "always_inline": */ 472 DECL_ATTRIBUTES (fndecl) = 473 tree_cons (get_identifier ("always_inline"), 474 NULL, 475 DECL_ATTRIBUTES (fndecl)); 476 } 477 478 function *func = new function (this, fndecl, kind); 479 m_functions.safe_push (func); 480 return func; 481} 482 483/* Construct a playback::lvalue instance (wrapping a tree). */ 484 485playback::lvalue * 486playback::context:: 487new_global (location *loc, 488 enum gcc_jit_global_kind kind, 489 type *type, 490 const char *name) 491{ 492 gcc_assert (type); 493 gcc_assert (name); 494 tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL, 495 get_identifier (name), 496 type->as_tree ()); 497 TREE_PUBLIC (inner) = (kind != GCC_JIT_GLOBAL_INTERNAL); 498 DECL_COMMON (inner) = 1; 499 switch (kind) 500 { 501 default: 502 gcc_unreachable (); 503 504 case GCC_JIT_GLOBAL_EXPORTED: 505 TREE_STATIC (inner) = 1; 506 break; 507 508 case GCC_JIT_GLOBAL_INTERNAL: 509 TREE_STATIC (inner) = 1; 510 break; 511 512 case GCC_JIT_GLOBAL_IMPORTED: 513 DECL_EXTERNAL (inner) = 1; 514 break; 515 } 516 517 if (loc) 518 set_tree_location (inner, loc); 519 520 varpool_node::get_create (inner); 521 522 m_globals.safe_push (inner); 523 524 return new lvalue (this, inner); 525} 526 527/* Implementation of the various 528 gcc::jit::playback::context::new_rvalue_from_const <HOST_TYPE> 529 methods. 530 Each of these constructs a playback::rvalue instance (wrapping a tree). 531 532 These specializations are required to be in the same namespace 533 as the template, hence we now have to enter the gcc::jit::playback 534 namespace. */ 535 536namespace playback 537{ 538 539/* Specialization of making an rvalue from a const, for host <int>. */ 540 541template <> 542rvalue * 543context:: 544new_rvalue_from_const <int> (type *type, 545 int 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 <long>. */ 564 565template <> 566rvalue * 567context:: 568new_rvalue_from_const <long> (type *type, 569 long value) 570{ 571 // FIXME: type-checking, or coercion? 572 tree inner_type = type->as_tree (); 573 if (INTEGRAL_TYPE_P (inner_type)) 574 { 575 tree inner = build_int_cst (inner_type, value); 576 return new rvalue (this, inner); 577 } 578 else 579 { 580 REAL_VALUE_TYPE real_value; 581 real_from_integer (&real_value, VOIDmode, value, SIGNED); 582 tree inner = build_real (inner_type, real_value); 583 return new rvalue (this, inner); 584 } 585} 586 587/* Specialization of making an rvalue from a const, for host <double>. */ 588 589template <> 590rvalue * 591context:: 592new_rvalue_from_const <double> (type *type, 593 double value) 594{ 595 // FIXME: type-checking, or coercion? 596 tree inner_type = type->as_tree (); 597 598 /* We have a "double", we want a REAL_VALUE_TYPE. 599 600 real.c:real_from_target appears to require the representation to be 601 split into 32-bit values, and then sent as an pair of host long 602 ints. */ 603 REAL_VALUE_TYPE real_value; 604 union 605 { 606 double as_double; 607 uint32_t as_uint32s[2]; 608 } u; 609 u.as_double = value; 610 long int as_long_ints[2]; 611 as_long_ints[0] = u.as_uint32s[0]; 612 as_long_ints[1] = u.as_uint32s[1]; 613 real_from_target (&real_value, as_long_ints, DFmode); 614 tree inner = build_real (inner_type, real_value); 615 return new rvalue (this, inner); 616} 617 618/* Specialization of making an rvalue from a const, for host <void *>. */ 619 620template <> 621rvalue * 622context:: 623new_rvalue_from_const <void *> (type *type, 624 void *value) 625{ 626 tree inner_type = type->as_tree (); 627 /* FIXME: how to ensure we have a wide enough type? */ 628 tree inner = build_int_cstu (inner_type, (unsigned HOST_WIDE_INT)value); 629 return new rvalue (this, inner); 630} 631 632/* We're done implementing the specializations of 633 gcc::jit::playback::context::new_rvalue_from_const <T> 634 so we can exit the gcc::jit::playback namespace. */ 635 636} // namespace playback 637 638/* Construct a playback::rvalue instance (wrapping a tree). */ 639 640playback::rvalue * 641playback::context:: 642new_string_literal (const char *value) 643{ 644 tree t_str = build_string (strlen (value), value); 645 gcc_assert (m_char_array_type_node); 646 TREE_TYPE (t_str) = m_char_array_type_node; 647 648 /* Convert to (const char*), loosely based on 649 c/c-typeck.c: array_to_pointer_conversion, 650 by taking address of start of string. */ 651 tree t_addr = build1 (ADDR_EXPR, m_const_char_ptr, t_str); 652 653 return new rvalue (this, t_addr); 654} 655 656/* Coerce a tree expression into a boolean tree expression. */ 657 658tree 659playback::context:: 660as_truth_value (tree expr, location *loc) 661{ 662 /* Compare to c-typeck.c:c_objc_common_truthvalue_conversion */ 663 tree typed_zero = fold_build1 (CONVERT_EXPR, 664 TREE_TYPE (expr), 665 integer_zero_node); 666 if (loc) 667 set_tree_location (typed_zero, loc); 668 669 expr = build2 (NE_EXPR, integer_type_node, expr, typed_zero); 670 if (loc) 671 set_tree_location (expr, loc); 672 673 return expr; 674} 675 676/* For use by jit_langhook_write_globals. 677 Calls varpool_node::finalize_decl on each global. */ 678 679void 680playback::context:: 681write_global_decls_1 () 682{ 683 /* Compare with e.g. the C frontend's c_write_global_declarations. */ 684 JIT_LOG_SCOPE (get_logger ()); 685 686 int i; 687 tree decl; 688 FOR_EACH_VEC_ELT (m_globals, i, decl) 689 { 690 gcc_assert (TREE_CODE (decl) == VAR_DECL); 691 varpool_node::finalize_decl (decl); 692 } 693} 694 695/* For use by jit_langhook_write_globals. 696 Calls debug_hooks->global_decl on each global. */ 697 698void 699playback::context:: 700write_global_decls_2 () 701{ 702 /* Compare with e.g. the C frontend's c_write_global_declarations_2. */ 703 JIT_LOG_SCOPE (get_logger ()); 704 705 int i; 706 tree decl; 707 FOR_EACH_VEC_ELT (m_globals, i, decl) 708 { 709 gcc_assert (TREE_CODE (decl) == VAR_DECL); 710 debug_hooks->global_decl (decl); 711 } 712} 713 714 715/* Construct a playback::rvalue instance (wrapping a tree) for a 716 unary op. */ 717 718playback::rvalue * 719playback::context:: 720new_unary_op (location *loc, 721 enum gcc_jit_unary_op op, 722 type *result_type, 723 rvalue *a) 724{ 725 // FIXME: type-checking, or coercion? 726 enum tree_code inner_op; 727 728 gcc_assert (result_type); 729 gcc_assert (a); 730 731 tree node = a->as_tree (); 732 tree inner_result = NULL; 733 734 switch (op) 735 { 736 default: 737 add_error (loc, "unrecognized (enum gcc_jit_unary_op) value: %i", op); 738 return NULL; 739 740 case GCC_JIT_UNARY_OP_MINUS: 741 inner_op = NEGATE_EXPR; 742 break; 743 744 case GCC_JIT_UNARY_OP_BITWISE_NEGATE: 745 inner_op = BIT_NOT_EXPR; 746 break; 747 748 case GCC_JIT_UNARY_OP_LOGICAL_NEGATE: 749 node = as_truth_value (node, loc); 750 inner_result = invert_truthvalue (node); 751 if (loc) 752 set_tree_location (inner_result, loc); 753 return new rvalue (this, inner_result); 754 755 case GCC_JIT_UNARY_OP_ABS: 756 inner_op = ABS_EXPR; 757 break; 758 } 759 760 inner_result = build1 (inner_op, 761 result_type->as_tree (), 762 node); 763 if (loc) 764 set_tree_location (inner_result, loc); 765 766 return new rvalue (this, inner_result); 767} 768 769/* Construct a playback::rvalue instance (wrapping a tree) for a 770 binary op. */ 771 772playback::rvalue * 773playback::context:: 774new_binary_op (location *loc, 775 enum gcc_jit_binary_op op, 776 type *result_type, 777 rvalue *a, rvalue *b) 778{ 779 // FIXME: type-checking, or coercion? 780 enum tree_code inner_op; 781 782 gcc_assert (result_type); 783 gcc_assert (a); 784 gcc_assert (b); 785 786 tree node_a = a->as_tree (); 787 tree node_b = b->as_tree (); 788 789 switch (op) 790 { 791 default: 792 add_error (loc, "unrecognized (enum gcc_jit_binary_op) value: %i", op); 793 return NULL; 794 795 case GCC_JIT_BINARY_OP_PLUS: 796 inner_op = PLUS_EXPR; 797 break; 798 799 case GCC_JIT_BINARY_OP_MINUS: 800 inner_op = MINUS_EXPR; 801 break; 802 803 case GCC_JIT_BINARY_OP_MULT: 804 inner_op = MULT_EXPR; 805 break; 806 807 case GCC_JIT_BINARY_OP_DIVIDE: 808 if (FLOAT_TYPE_P (result_type->as_tree ())) 809 /* Floating-point division: */ 810 inner_op = RDIV_EXPR; 811 else 812 /* Truncating to zero: */ 813 inner_op = TRUNC_DIV_EXPR; 814 break; 815 816 case GCC_JIT_BINARY_OP_MODULO: 817 inner_op = TRUNC_MOD_EXPR; 818 break; 819 820 case GCC_JIT_BINARY_OP_BITWISE_AND: 821 inner_op = BIT_AND_EXPR; 822 break; 823 824 case GCC_JIT_BINARY_OP_BITWISE_XOR: 825 inner_op = BIT_XOR_EXPR; 826 break; 827 828 case GCC_JIT_BINARY_OP_BITWISE_OR: 829 inner_op = BIT_IOR_EXPR; 830 break; 831 832 case GCC_JIT_BINARY_OP_LOGICAL_AND: 833 node_a = as_truth_value (node_a, loc); 834 node_b = as_truth_value (node_b, loc); 835 inner_op = TRUTH_ANDIF_EXPR; 836 break; 837 838 case GCC_JIT_BINARY_OP_LOGICAL_OR: 839 node_a = as_truth_value (node_a, loc); 840 node_b = as_truth_value (node_b, loc); 841 inner_op = TRUTH_ORIF_EXPR; 842 break; 843 844 case GCC_JIT_BINARY_OP_LSHIFT: 845 inner_op = LSHIFT_EXPR; 846 break; 847 848 case GCC_JIT_BINARY_OP_RSHIFT: 849 inner_op = RSHIFT_EXPR; 850 break; 851 } 852 853 tree inner_expr = build2 (inner_op, 854 result_type->as_tree (), 855 node_a, 856 node_b); 857 if (loc) 858 set_tree_location (inner_expr, loc); 859 860 return new rvalue (this, inner_expr); 861} 862 863/* Construct a playback::rvalue instance (wrapping a tree) for a 864 comparison. */ 865 866playback::rvalue * 867playback::context:: 868new_comparison (location *loc, 869 enum gcc_jit_comparison op, 870 rvalue *a, rvalue *b) 871{ 872 // FIXME: type-checking, or coercion? 873 enum tree_code inner_op; 874 875 gcc_assert (a); 876 gcc_assert (b); 877 878 switch (op) 879 { 880 default: 881 add_error (loc, "unrecognized (enum gcc_jit_comparison) value: %i", op); 882 return NULL; 883 884 case GCC_JIT_COMPARISON_EQ: 885 inner_op = EQ_EXPR; 886 break; 887 case GCC_JIT_COMPARISON_NE: 888 inner_op = NE_EXPR; 889 break; 890 case GCC_JIT_COMPARISON_LT: 891 inner_op = LT_EXPR; 892 break; 893 case GCC_JIT_COMPARISON_LE: 894 inner_op = LE_EXPR; 895 break; 896 case GCC_JIT_COMPARISON_GT: 897 inner_op = GT_EXPR; 898 break; 899 case GCC_JIT_COMPARISON_GE: 900 inner_op = GE_EXPR; 901 break; 902 } 903 904 tree inner_expr = build2 (inner_op, 905 boolean_type_node, 906 a->as_tree (), 907 b->as_tree ()); 908 if (loc) 909 set_tree_location (inner_expr, loc); 910 return new rvalue (this, inner_expr); 911} 912 913/* Construct a playback::rvalue instance (wrapping a tree) for a 914 function call. */ 915 916playback::rvalue * 917playback::context:: 918build_call (location *loc, 919 tree fn_ptr, 920 const auto_vec<rvalue *> *args) 921{ 922 vec<tree, va_gc> *tree_args; 923 vec_alloc (tree_args, args->length ()); 924 for (unsigned i = 0; i < args->length (); i++) 925 tree_args->quick_push ((*args)[i]->as_tree ()); 926 927 if (loc) 928 set_tree_location (fn_ptr, loc); 929 930 tree fn = TREE_TYPE (fn_ptr); 931 tree fn_type = TREE_TYPE (fn); 932 tree return_type = TREE_TYPE (fn_type); 933 934 return new rvalue (this, 935 build_call_vec (return_type, 936 fn_ptr, tree_args)); 937 938 /* see c-typeck.c: build_function_call 939 which calls build_function_call_vec 940 941 which does lots of checking, then: 942 result = build_call_array_loc (loc, TREE_TYPE (fntype), 943 function, nargs, argarray); 944 which is in tree.c 945 (see also build_call_vec) 946 */ 947} 948 949/* Construct a playback::rvalue instance (wrapping a tree) for a 950 call to a specific function. */ 951 952playback::rvalue * 953playback::context:: 954new_call (location *loc, 955 function *func, 956 const auto_vec<rvalue *> *args) 957{ 958 tree fndecl; 959 960 gcc_assert (func); 961 962 fndecl = func->as_fndecl (); 963 964 tree fntype = TREE_TYPE (fndecl); 965 966 tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl); 967 968 return build_call (loc, fn, args); 969} 970 971/* Construct a playback::rvalue instance (wrapping a tree) for a 972 call through a function pointer. */ 973 974playback::rvalue * 975playback::context:: 976new_call_through_ptr (location *loc, 977 rvalue *fn_ptr, 978 const auto_vec<rvalue *> *args) 979{ 980 gcc_assert (fn_ptr); 981 tree t_fn_ptr = fn_ptr->as_tree (); 982 983 return build_call (loc, t_fn_ptr, args); 984} 985 986/* Construct a tree for a cast. */ 987 988tree 989playback::context::build_cast (playback::location *loc, 990 playback::rvalue *expr, 991 playback::type *type_) 992{ 993 /* For comparison, see: 994 - c/c-typeck.c:build_c_cast 995 - c/c-convert.c: convert 996 - convert.h 997 998 Only some kinds of cast are currently supported here. */ 999 tree t_expr = expr->as_tree (); 1000 tree t_dst_type = type_->as_tree (); 1001 tree t_ret = NULL; 1002 t_ret = targetm.convert_to_type (t_dst_type, t_expr); 1003 if (t_ret) 1004 return t_ret; 1005 enum tree_code dst_code = TREE_CODE (t_dst_type); 1006 switch (dst_code) 1007 { 1008 case INTEGER_TYPE: 1009 case ENUMERAL_TYPE: 1010 t_ret = convert_to_integer (t_dst_type, t_expr); 1011 goto maybe_fold; 1012 1013 case BOOLEAN_TYPE: 1014 /* Compare with c_objc_common_truthvalue_conversion and 1015 c_common_truthvalue_conversion. */ 1016 /* For now, convert to: (t_expr != 0) */ 1017 t_ret = build2 (NE_EXPR, t_dst_type, 1018 t_expr, 1019 build_int_cst (TREE_TYPE (t_expr), 0)); 1020 goto maybe_fold; 1021 1022 case REAL_TYPE: 1023 t_ret = convert_to_real (t_dst_type, t_expr); 1024 goto maybe_fold; 1025 1026 case POINTER_TYPE: 1027 t_ret = build1 (NOP_EXPR, t_dst_type, t_expr); 1028 goto maybe_fold; 1029 1030 default: 1031 add_error (loc, "couldn't handle cast during playback"); 1032 fprintf (stderr, "input expression:\n"); 1033 debug_tree (t_expr); 1034 fprintf (stderr, "requested type:\n"); 1035 debug_tree (t_dst_type); 1036 return error_mark_node; 1037 1038 maybe_fold: 1039 if (TREE_CODE (t_ret) != C_MAYBE_CONST_EXPR) 1040 t_ret = fold (t_ret); 1041 return t_ret; 1042 } 1043} 1044 1045/* Construct a playback::rvalue instance (wrapping a tree) for a 1046 cast. */ 1047 1048playback::rvalue * 1049playback::context:: 1050new_cast (playback::location *loc, 1051 playback::rvalue *expr, 1052 playback::type *type_) 1053{ 1054 1055 tree t_cast = build_cast (loc, expr, type_); 1056 if (loc) 1057 set_tree_location (t_cast, loc); 1058 return new rvalue (this, t_cast); 1059} 1060 1061/* Construct a playback::lvalue instance (wrapping a tree) for an 1062 array access. */ 1063 1064playback::lvalue * 1065playback::context:: 1066new_array_access (location *loc, 1067 rvalue *ptr, 1068 rvalue *index) 1069{ 1070 gcc_assert (ptr); 1071 gcc_assert (index); 1072 1073 /* For comparison, see: 1074 c/c-typeck.c: build_array_ref 1075 c-family/c-common.c: pointer_int_sum 1076 */ 1077 tree t_ptr = ptr->as_tree (); 1078 tree t_index = index->as_tree (); 1079 tree t_type_ptr = TREE_TYPE (t_ptr); 1080 tree t_type_star_ptr = TREE_TYPE (t_type_ptr); 1081 1082 if (TREE_CODE (t_type_ptr) == ARRAY_TYPE) 1083 { 1084 tree t_result = build4 (ARRAY_REF, t_type_star_ptr, t_ptr, t_index, 1085 NULL_TREE, NULL_TREE); 1086 if (loc) 1087 set_tree_location (t_result, loc); 1088 return new lvalue (this, t_result); 1089 } 1090 else 1091 { 1092 /* Convert index to an offset in bytes. */ 1093 tree t_sizeof = size_in_bytes (t_type_star_ptr); 1094 t_index = fold_build1 (CONVERT_EXPR, sizetype, t_index); 1095 tree t_offset = build2 (MULT_EXPR, sizetype, t_index, t_sizeof); 1096 1097 /* Locate (ptr + offset). */ 1098 tree t_address = build2 (POINTER_PLUS_EXPR, t_type_ptr, t_ptr, t_offset); 1099 1100 tree t_indirection = build1 (INDIRECT_REF, t_type_star_ptr, t_address); 1101 if (loc) 1102 { 1103 set_tree_location (t_sizeof, loc); 1104 set_tree_location (t_offset, loc); 1105 set_tree_location (t_address, loc); 1106 set_tree_location (t_indirection, loc); 1107 } 1108 1109 return new lvalue (this, t_indirection); 1110 } 1111} 1112 1113/* Construct a tree for a field access. */ 1114 1115tree 1116playback::context:: 1117new_field_access (location *loc, 1118 tree datum, 1119 field *field) 1120{ 1121 gcc_assert (datum); 1122 gcc_assert (field); 1123 1124 /* Compare with c/c-typeck.c:lookup_field, build_indirect_ref, and 1125 build_component_ref. */ 1126 tree type = TREE_TYPE (datum); 1127 gcc_assert (type); 1128 gcc_assert (TREE_CODE (type) != POINTER_TYPE); 1129 1130 tree t_field = field->as_tree (); 1131 tree ref = build3 (COMPONENT_REF, TREE_TYPE (t_field), datum, 1132 t_field, NULL_TREE); 1133 if (loc) 1134 set_tree_location (ref, loc); 1135 return ref; 1136} 1137 1138/* Construct a tree for a dereference. */ 1139 1140tree 1141playback::context:: 1142new_dereference (tree ptr, 1143 location *loc) 1144{ 1145 gcc_assert (ptr); 1146 1147 tree type = TREE_TYPE (TREE_TYPE(ptr)); 1148 tree datum = build1 (INDIRECT_REF, type, ptr); 1149 if (loc) 1150 set_tree_location (datum, loc); 1151 return datum; 1152} 1153 1154/* Construct a playback::lvalue instance (wrapping a tree) for a 1155 field access. */ 1156 1157playback::lvalue * 1158playback::lvalue:: 1159access_field (location *loc, 1160 field *field) 1161{ 1162 tree datum = as_tree (); 1163 tree ref = get_context ()->new_field_access (loc, datum, field); 1164 if (!ref) 1165 return NULL; 1166 return new lvalue (get_context (), ref); 1167} 1168 1169/* Construct a playback::rvalue instance (wrapping a tree) for a 1170 field access. */ 1171 1172playback::rvalue * 1173playback::rvalue:: 1174access_field (location *loc, 1175 field *field) 1176{ 1177 tree datum = as_tree (); 1178 tree ref = get_context ()->new_field_access (loc, datum, field); 1179 if (!ref) 1180 return NULL; 1181 return new rvalue (get_context (), ref); 1182} 1183 1184/* Construct a playback::lvalue instance (wrapping a tree) for a 1185 dereferenced field access. */ 1186 1187playback::lvalue * 1188playback::rvalue:: 1189dereference_field (location *loc, 1190 field *field) 1191{ 1192 tree ptr = as_tree (); 1193 tree datum = get_context ()->new_dereference (ptr, loc); 1194 if (!datum) 1195 return NULL; 1196 tree ref = get_context ()->new_field_access (loc, datum, field); 1197 if (!ref) 1198 return NULL; 1199 return new lvalue (get_context (), ref); 1200} 1201 1202/* Construct a playback::lvalue instance (wrapping a tree) for a 1203 dereference. */ 1204 1205playback::lvalue * 1206playback::rvalue:: 1207dereference (location *loc) 1208{ 1209 tree ptr = as_tree (); 1210 tree datum = get_context ()->new_dereference (ptr, loc); 1211 return new lvalue (get_context (), datum); 1212} 1213 1214/* Mark EXP saying that we need to be able to take the 1215 address of it; it should not be allocated in a register. 1216 Compare with e.g. c/c-typeck.c: c_mark_addressable. */ 1217 1218static void 1219jit_mark_addressable (tree exp) 1220{ 1221 tree x = exp; 1222 1223 while (1) 1224 switch (TREE_CODE (x)) 1225 { 1226 case COMPONENT_REF: 1227 /* (we don't yet support bitfields) */ 1228 /* fallthrough */ 1229 case ADDR_EXPR: 1230 case ARRAY_REF: 1231 case REALPART_EXPR: 1232 case IMAGPART_EXPR: 1233 x = TREE_OPERAND (x, 0); 1234 break; 1235 1236 case COMPOUND_LITERAL_EXPR: 1237 case CONSTRUCTOR: 1238 TREE_ADDRESSABLE (x) = 1; 1239 return; 1240 1241 case VAR_DECL: 1242 case CONST_DECL: 1243 case PARM_DECL: 1244 case RESULT_DECL: 1245 /* (we don't have a concept of a "register" declaration) */ 1246 /* fallthrough */ 1247 case FUNCTION_DECL: 1248 TREE_ADDRESSABLE (x) = 1; 1249 /* fallthrough */ 1250 default: 1251 return; 1252 } 1253} 1254 1255/* Construct a playback::rvalue instance (wrapping a tree) for an 1256 address-lookup. */ 1257 1258playback::rvalue * 1259playback::lvalue:: 1260get_address (location *loc) 1261{ 1262 tree t_lvalue = as_tree (); 1263 tree t_thistype = TREE_TYPE (t_lvalue); 1264 tree t_ptrtype = build_pointer_type (t_thistype); 1265 tree ptr = build1 (ADDR_EXPR, t_ptrtype, t_lvalue); 1266 if (loc) 1267 get_context ()->set_tree_location (ptr, loc); 1268 jit_mark_addressable (t_lvalue); 1269 return new rvalue (get_context (), ptr); 1270} 1271 1272/* The wrapper subclasses are GC-managed, but can own non-GC memory. 1273 Provide this finalization hook for calling then they are collected, 1274 which calls the finalizer vfunc. This allows them to call "release" 1275 on any vec<> within them. */ 1276 1277static void 1278wrapper_finalizer (void *ptr) 1279{ 1280 playback::wrapper *wrapper = reinterpret_cast <playback::wrapper *> (ptr); 1281 wrapper->finalizer (); 1282} 1283 1284/* gcc::jit::playback::wrapper subclasses are GC-managed: 1285 allocate them using ggc_internal_cleared_alloc. */ 1286 1287void * 1288playback::wrapper:: 1289operator new (size_t sz) 1290{ 1291 return ggc_internal_cleared_alloc (sz, wrapper_finalizer, 0, 1); 1292 1293} 1294 1295/* Constructor for gcc:jit::playback::function. */ 1296 1297playback::function:: 1298function (context *ctxt, 1299 tree fndecl, 1300 enum gcc_jit_function_kind kind) 1301: m_ctxt(ctxt), 1302 m_inner_fndecl (fndecl), 1303 m_inner_bind_expr (NULL), 1304 m_kind (kind) 1305{ 1306 if (m_kind != GCC_JIT_FUNCTION_IMPORTED) 1307 { 1308 /* Create a BIND_EXPR, and within it, a statement list. */ 1309 m_stmt_list = alloc_stmt_list (); 1310 m_stmt_iter = tsi_start (m_stmt_list); 1311 m_inner_block = make_node (BLOCK); 1312 m_inner_bind_expr = 1313 build3 (BIND_EXPR, void_type_node, NULL, m_stmt_list, m_inner_block); 1314 } 1315 else 1316 { 1317 m_inner_block = NULL; 1318 m_stmt_list = NULL; 1319 } 1320} 1321 1322/* Hand-written GC-marking hook for playback functions. */ 1323 1324void 1325playback::function:: 1326gt_ggc_mx () 1327{ 1328 gt_ggc_m_9tree_node (m_inner_fndecl); 1329 gt_ggc_m_9tree_node (m_inner_bind_expr); 1330 gt_ggc_m_9tree_node (m_stmt_list); 1331 gt_ggc_m_9tree_node (m_inner_block); 1332} 1333 1334/* Don't leak vec's internal buffer (in non-GC heap) when we are 1335 GC-ed. */ 1336 1337void 1338playback::function::finalizer () 1339{ 1340 m_blocks.release (); 1341} 1342 1343/* Get the return type of a playback function, in tree form. */ 1344 1345tree 1346playback::function:: 1347get_return_type_as_tree () const 1348{ 1349 return TREE_TYPE (TREE_TYPE(m_inner_fndecl)); 1350} 1351 1352/* Construct a new local within this playback::function. */ 1353 1354playback::lvalue * 1355playback::function:: 1356new_local (location *loc, 1357 type *type, 1358 const char *name) 1359{ 1360 gcc_assert (type); 1361 gcc_assert (name); 1362 tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL, 1363 get_identifier (name), 1364 type->as_tree ()); 1365 DECL_CONTEXT (inner) = this->m_inner_fndecl; 1366 1367 /* Prepend to BIND_EXPR_VARS: */ 1368 DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr); 1369 BIND_EXPR_VARS (m_inner_bind_expr) = inner; 1370 1371 if (loc) 1372 set_tree_location (inner, loc); 1373 return new lvalue (m_ctxt, inner); 1374} 1375 1376/* Construct a new block within this playback::function. */ 1377 1378playback::block * 1379playback::function:: 1380new_block (const char *name) 1381{ 1382 gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); 1383 1384 block *result = new playback::block (this, name); 1385 m_blocks.safe_push (result); 1386 return result; 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 1393void 1394playback::function:: 1395build_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 1423void 1424playback::function:: 1425postprocess () 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 1484void 1485playback::block::finalizer () 1486{ 1487 m_stmts.release (); 1488} 1489 1490/* Add an eval of the rvalue to the function's statement list. */ 1491 1492void 1493playback::block:: 1494add_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 1507void 1508playback::block:: 1509add_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 1538void 1539playback::block:: 1540add_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 1567void 1568playback::block:: 1569add_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 1601void 1602playback::block:: 1603add_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: 1621tree 1622c_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 1640void 1641playback::block:: 1642add_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 1672static void 1673add_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 1700void 1701playback::block:: 1702add_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 1744playback::block:: 1745block (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 1767class 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 1776auto_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 1805void 1806playback::context:: 1807compile () 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 (false, /* use_TV_TOTAL */ 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 1894playback::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 1907void 1908playback::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 1922playback::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 1939void 1940playback::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 break; 1958 1959 case GCC_JIT_OUTPUT_KIND_OBJECT_FILE: 1960 { 1961 char *tmp_o_path = ::concat (get_tempdir ()->get_path (), 1962 "/fake.o", 1963 NULL); 1964 invoke_driver (ctxt_progname, 1965 get_tempdir ()->get_path_s_file (), 1966 tmp_o_path, 1967 TV_ASSEMBLE, 1968 false, /* bool shared, */ 1969 false);/* bool run_linker */ 1970 if (!errors_occurred ()) 1971 copy_file (tmp_o_path, 1972 m_output_path); 1973 free (tmp_o_path); 1974 } 1975 break; 1976 1977 case GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY: 1978 invoke_driver (ctxt_progname, 1979 get_tempdir ()->get_path_s_file (), 1980 get_tempdir ()->get_path_so_file (), 1981 TV_ASSEMBLE, 1982 true, /* bool shared, */ 1983 true);/* bool run_linker */ 1984 if (!errors_occurred ()) 1985 copy_file (get_tempdir ()->get_path_so_file (), 1986 m_output_path); 1987 break; 1988 1989 case GCC_JIT_OUTPUT_KIND_EXECUTABLE: 1990 { 1991 char *tmp_exe_path = ::concat (get_tempdir ()->get_path (), 1992 "/fake.exe", 1993 NULL); 1994 invoke_driver (ctxt_progname, 1995 get_tempdir ()->get_path_s_file (), 1996 tmp_exe_path, 1997 TV_ASSEMBLE, 1998 false, /* bool shared, */ 1999 true);/* bool run_linker */ 2000 if (!errors_occurred ()) 2001 copy_file (tmp_exe_path, 2002 m_output_path); 2003 free (tmp_exe_path); 2004 } 2005 break; 2006 2007 } 2008 2009} 2010 2011/* Copy SRC_PATH to DST_PATH, preserving permission bits (in particular, 2012 the "executable" bits). 2013 2014 Any errors that occur are reported on the context and hence count as 2015 a failure of the compile. 2016 2017 We can't in general hardlink or use "rename" from the tempdir since 2018 it might be on a different filesystem to the destination. For example, 2019 I get EXDEV: "Invalid cross-device link". */ 2020 2021void 2022playback::compile_to_file::copy_file (const char *src_path, 2023 const char *dst_path) 2024{ 2025 JIT_LOG_SCOPE (get_logger ()); 2026 if (get_logger ()) 2027 { 2028 get_logger ()->log ("src_path: %s", src_path); 2029 get_logger ()->log ("dst_path: %s", dst_path); 2030 } 2031 2032 FILE *f_in = NULL; 2033 FILE *f_out = NULL; 2034 size_t total_sz_in = 0; 2035 size_t total_sz_out = 0; 2036 char buf[4096]; 2037 size_t sz_in; 2038 struct stat stat_buf; 2039 2040 f_in = fopen (src_path, "rb"); 2041 if (!f_in) 2042 { 2043 add_error (NULL, 2044 "unable to open %s for reading: %s", 2045 src_path, 2046 xstrerror (errno)); 2047 return; 2048 } 2049 2050 /* Use stat on the filedescriptor to get the mode, 2051 so that we can copy it over (in particular, the 2052 "executable" bits). */ 2053 if (-1 == fstat (fileno (f_in), &stat_buf)) 2054 { 2055 add_error (NULL, 2056 "unable to fstat %s: %s", 2057 src_path, 2058 xstrerror (errno)); 2059 fclose (f_in); 2060 return; 2061 } 2062 2063 f_out = fopen (dst_path, "wb"); 2064 if (!f_out) 2065 { 2066 add_error (NULL, 2067 "unable to open %s for writing: %s", 2068 dst_path, 2069 xstrerror (errno)); 2070 fclose (f_in); 2071 return; 2072 } 2073 2074 while ( (sz_in = fread (buf, 1, sizeof (buf), f_in)) ) 2075 { 2076 total_sz_in += sz_in; 2077 size_t sz_out_remaining = sz_in; 2078 size_t sz_out_so_far = 0; 2079 while (sz_out_remaining) 2080 { 2081 size_t sz_out = fwrite (buf + sz_out_so_far, 2082 1, 2083 sz_out_remaining, 2084 f_out); 2085 gcc_assert (sz_out <= sz_out_remaining); 2086 if (!sz_out) 2087 { 2088 add_error (NULL, 2089 "error writing to %s: %s", 2090 dst_path, 2091 xstrerror (errno)); 2092 fclose (f_in); 2093 fclose (f_out); 2094 return; 2095 } 2096 total_sz_out += sz_out; 2097 sz_out_so_far += sz_out; 2098 sz_out_remaining -= sz_out; 2099 } 2100 gcc_assert (sz_out_so_far == sz_in); 2101 } 2102 2103 if (!feof (f_in)) 2104 add_error (NULL, 2105 "error reading from %s: %s", 2106 src_path, 2107 xstrerror (errno)); 2108 2109 fclose (f_in); 2110 2111 gcc_assert (total_sz_in == total_sz_out); 2112 if (get_logger ()) 2113 get_logger ()->log ("total bytes copied: %ld", total_sz_out); 2114 2115 /* Set the permissions of the copy to those of the original file, 2116 in particular the "executable" bits. */ 2117 if (-1 == fchmod (fileno (f_out), stat_buf.st_mode)) 2118 add_error (NULL, 2119 "error setting mode of %s: %s", 2120 dst_path, 2121 xstrerror (errno)); 2122 2123 fclose (f_out); 2124} 2125 2126/* Helper functions for gcc::jit::playback::context::compile. */ 2127 2128/* This mutex guards gcc::jit::recording::context::compile, so that only 2129 one thread can be accessing the bulk of GCC's state at once. */ 2130 2131static pthread_mutex_t jit_mutex = PTHREAD_MUTEX_INITIALIZER; 2132 2133/* Acquire jit_mutex and set "this" as the active playback ctxt. */ 2134 2135void 2136playback::context::acquire_mutex () 2137{ 2138 /* Acquire the big GCC mutex. */ 2139 JIT_LOG_SCOPE (get_logger ()); 2140 pthread_mutex_lock (&jit_mutex); 2141 gcc_assert (NULL == active_playback_ctxt); 2142 active_playback_ctxt = this; 2143} 2144 2145/* Release jit_mutex and clear the active playback ctxt. */ 2146 2147void 2148playback::context::release_mutex () 2149{ 2150 /* Release the big GCC mutex. */ 2151 JIT_LOG_SCOPE (get_logger ()); 2152 gcc_assert (active_playback_ctxt == this); 2153 active_playback_ctxt = NULL; 2154 pthread_mutex_unlock (&jit_mutex); 2155} 2156 2157/* Callback used by gcc::jit::playback::context::make_fake_args when 2158 invoking driver_get_configure_time_options. 2159 Populate a vec <char * > with the configure-time options. */ 2160 2161static void 2162append_arg_from_driver (const char *option, void *user_data) 2163{ 2164 gcc_assert (option); 2165 gcc_assert (user_data); 2166 vec <char *> *argvec = static_cast <vec <char *> *> (user_data); 2167 argvec->safe_push (concat ("-", option, NULL)); 2168} 2169 2170/* Build a fake argv for toplev::main from the options set 2171 by the user on the context . */ 2172 2173void 2174playback::context:: 2175make_fake_args (vec <char *> *argvec, 2176 const char *ctxt_progname, 2177 vec <recording::requested_dump> *requested_dumps) 2178{ 2179 JIT_LOG_SCOPE (get_logger ()); 2180 2181#define ADD_ARG(arg) argvec->safe_push (xstrdup (arg)) 2182#define ADD_ARG_TAKE_OWNERSHIP(arg) argvec->safe_push (arg) 2183 2184 ADD_ARG (ctxt_progname); 2185 ADD_ARG (get_path_c_file ()); 2186 ADD_ARG ("-fPIC"); 2187 2188 /* Handle int options: */ 2189 switch (get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL)) 2190 { 2191 default: 2192 add_error (NULL, 2193 "unrecognized optimization level: %i", 2194 get_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL)); 2195 return; 2196 2197 case 0: 2198 ADD_ARG ("-O0"); 2199 break; 2200 2201 case 1: 2202 ADD_ARG ("-O1"); 2203 break; 2204 2205 case 2: 2206 ADD_ARG ("-O2"); 2207 break; 2208 2209 case 3: 2210 ADD_ARG ("-O3"); 2211 break; 2212 } 2213 /* What about -Os? */ 2214 2215 /* Handle bool options: */ 2216 if (get_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO)) 2217 ADD_ARG ("-g"); 2218 2219 /* Suppress timing (and other) info. */ 2220 if (!get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_SUMMARY)) 2221 { 2222 ADD_ARG ("-quiet"); 2223 quiet_flag = 1; 2224 } 2225 2226 /* Aggressively garbage-collect, to shake out bugs: */ 2227 if (get_bool_option (GCC_JIT_BOOL_OPTION_SELFCHECK_GC)) 2228 { 2229 ADD_ARG ("--param"); 2230 ADD_ARG ("ggc-min-expand=0"); 2231 ADD_ARG ("--param"); 2232 ADD_ARG ("ggc-min-heapsize=0"); 2233 } 2234 2235 if (get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING)) 2236 { 2237 ADD_ARG ("-fdump-tree-all"); 2238 ADD_ARG ("-fdump-rtl-all"); 2239 ADD_ARG ("-fdump-ipa-all"); 2240 } 2241 2242 /* Add "-fdump-" options for any calls to 2243 gcc_jit_context_enable_dump. */ 2244 { 2245 int i; 2246 recording::requested_dump *d; 2247 FOR_EACH_VEC_ELT (*requested_dumps, i, d) 2248 { 2249 char *arg = concat ("-fdump-", d->m_dumpname, NULL); 2250 ADD_ARG_TAKE_OWNERSHIP (arg); 2251 } 2252 } 2253 2254 /* PR jit/64810: Add any target-specific default options 2255 from OPTION_DEFAULT_SPECS, normally provided by the driver 2256 in the non-jit case. 2257 2258 The target-specific code can define OPTION_DEFAULT_SPECS: 2259 default command options in the form of spec macros for the 2260 driver to expand (). 2261 2262 For cc1 etc, the driver processes OPTION_DEFAULT_SPECS and, 2263 if not overriden, injects the defaults as extra arguments to 2264 cc1 etc. 2265 For the jit case, we need to add these arguments here. The 2266 input format (using the specs language) means that we have to run 2267 part of the driver code here (driver_get_configure_time_options). 2268 2269 To avoid running the spec-expansion code every time, we just do 2270 it the first time (via a function-static flag), saving the result 2271 into a function-static vec. 2272 This flag and vec are global state (i.e. per-process). 2273 They are guarded by the jit mutex. */ 2274 { 2275 static bool have_configure_time_options = false; 2276 static vec <char *> configure_time_options; 2277 2278 if (have_configure_time_options) 2279 log ("reusing cached configure-time options"); 2280 else 2281 { 2282 have_configure_time_options = true; 2283 log ("getting configure-time options from driver"); 2284 driver_get_configure_time_options (append_arg_from_driver, 2285 &configure_time_options); 2286 } 2287 2288 int i; 2289 char *opt; 2290 2291 if (get_logger ()) 2292 FOR_EACH_VEC_ELT (configure_time_options, i, opt) 2293 log ("configure_time_options[%i]: %s", i, opt); 2294 2295 /* configure_time_options should now contain the expanded options 2296 from OPTION_DEFAULT_SPECS (if any). */ 2297 FOR_EACH_VEC_ELT (configure_time_options, i, opt) 2298 { 2299 gcc_assert (opt); 2300 gcc_assert (opt[0] == '-'); 2301 ADD_ARG (opt); 2302 } 2303 } 2304 2305 /* Add any user-provided extra options, starting with any from 2306 parent contexts. */ 2307 m_recording_ctxt->append_command_line_options (argvec); 2308 2309#undef ADD_ARG 2310#undef ADD_ARG_TAKE_OWNERSHIP 2311} 2312 2313/* The second half of the implementation of gcc_jit_context_enable_dump. 2314 Iterate through the requested dumps, reading the underlying files 2315 into heap-allocated buffers, writing pointers to the buffers into 2316 the char ** pointers provided by client code. 2317 Client code is responsible for calling free on the results. */ 2318 2319void 2320playback::context:: 2321extract_any_requested_dumps (vec <recording::requested_dump> *requested_dumps) 2322{ 2323 JIT_LOG_SCOPE (get_logger ()); 2324 2325 int i; 2326 recording::requested_dump *d; 2327 FOR_EACH_VEC_ELT (*requested_dumps, i, d) 2328 { 2329 dump_file_info *dfi; 2330 char *filename; 2331 char *content; 2332 2333 dfi = g->get_dumps ()->get_dump_file_info_by_switch (d->m_dumpname); 2334 if (!dfi) 2335 { 2336 add_error (NULL, "unrecognized dump: %s", d->m_dumpname); 2337 continue; 2338 } 2339 2340 filename = g->get_dumps ()->get_dump_file_name (dfi); 2341 content = read_dump_file (filename); 2342 *(d->m_out_ptr) = content; 2343 free (filename); 2344 } 2345} 2346 2347/* Helper function for playback::context::extract_any_requested_dumps 2348 (itself for use in implementation of gcc_jit_context_enable_dump). 2349 2350 Attempt to read the complete file at the given path, returning the 2351 bytes found there as a buffer. 2352 The caller is responsible for calling free on the result. 2353 Errors will be reported on the context, and lead to NULL being 2354 returned; an out-of-memory error will terminate the process. */ 2355 2356char * 2357playback::context::read_dump_file (const char *path) 2358{ 2359 char *result = NULL; 2360 size_t total_sz = 0; 2361 char buf[4096]; 2362 size_t sz; 2363 FILE *f_in; 2364 2365 f_in = fopen (path, "r"); 2366 if (!f_in) 2367 { 2368 add_error (NULL, "unable to open %s for reading", path); 2369 return NULL; 2370 } 2371 2372 while ( (sz = fread (buf, 1, sizeof (buf), f_in)) ) 2373 { 2374 size_t old_total_sz = total_sz; 2375 total_sz += sz; 2376 result = reinterpret_cast <char *> (xrealloc (result, total_sz + 1)); 2377 memcpy (result + old_total_sz, buf, sz); 2378 } 2379 2380 if (!feof (f_in)) 2381 { 2382 add_error (NULL, "error reading from %s", path); 2383 free (result); 2384 fclose (f_in); 2385 return NULL; 2386 } 2387 2388 fclose (f_in); 2389 2390 if (result) 2391 { 2392 result[total_sz] = '\0'; 2393 return result; 2394 } 2395 else 2396 return xstrdup (""); 2397} 2398 2399/* Part of playback::context::compile (). 2400 2401 We have a .s file; we want a .so file. 2402 We could reuse parts of gcc/gcc.c to do this. 2403 For now, just use the driver binary from the install, as 2404 named in gcc-driver-name.h 2405 e.g. "x86_64-unknown-linux-gnu-gcc-5.0.0". */ 2406 2407void 2408playback::context:: 2409convert_to_dso (const char *ctxt_progname) 2410{ 2411 JIT_LOG_SCOPE (get_logger ()); 2412 2413 invoke_driver (ctxt_progname, 2414 m_tempdir->get_path_s_file (), 2415 m_tempdir->get_path_so_file (), 2416 TV_ASSEMBLE, 2417 true, /* bool shared, */ 2418 true);/* bool run_linker */ 2419} 2420 2421void 2422playback::context:: 2423invoke_driver (const char *ctxt_progname, 2424 const char *input_file, 2425 const char *output_file, 2426 timevar_id_t tv_id, 2427 bool shared, 2428 bool run_linker) 2429{ 2430 JIT_LOG_SCOPE (get_logger ()); 2431 /* Currently this lumps together both assembling and linking into 2432 TV_ASSEMBLE. */ 2433 auto_timevar assemble_timevar (tv_id); 2434 const char *errmsg; 2435 auto_argvec argvec; 2436#define ADD_ARG(arg) argvec.safe_push (xstrdup (arg)) 2437 int exit_status = 0; 2438 int err = 0; 2439 const char *gcc_driver_name = GCC_DRIVER_NAME; 2440 2441 ADD_ARG (gcc_driver_name); 2442 2443 add_multilib_driver_arguments (&argvec); 2444 2445 if (shared) 2446 ADD_ARG ("-shared"); 2447 2448 if (!run_linker) 2449 ADD_ARG ("-c"); 2450 2451 ADD_ARG (input_file); 2452 ADD_ARG ("-o"); 2453 ADD_ARG (output_file); 2454 2455 /* Don't use the linker plugin. 2456 If running with just a "make" and not a "make install", then we'd 2457 run into 2458 "fatal error: -fuse-linker-plugin, but liblto_plugin.so not found" 2459 libto_plugin is a .la at build time, with it becoming installed with 2460 ".so" suffix: i.e. it doesn't exist with a .so suffix until install 2461 time. */ 2462 ADD_ARG ("-fno-use-linker-plugin"); 2463 2464#if defined (DARWIN_X86) || defined (DARWIN_PPC) 2465 /* OS X's linker defaults to treating undefined symbols as errors. 2466 If the context has any imported functions or globals they will be 2467 undefined until the .so is dynamically-linked into the process. 2468 Ensure that the driver passes in "-undefined dynamic_lookup" to the 2469 linker. */ 2470 ADD_ARG ("-Wl,-undefined,dynamic_lookup"); 2471#endif 2472 2473 /* pex argv arrays are NULL-terminated. */ 2474 argvec.safe_push (NULL); 2475 2476 /* pex_one's error-handling requires pname to be non-NULL. */ 2477 gcc_assert (ctxt_progname); 2478 2479 if (get_logger ()) 2480 for (unsigned i = 0; i < argvec.length (); i++) 2481 get_logger ()->log ("argv[%i]: %s", i, argvec[i]); 2482 2483 errmsg = pex_one (PEX_SEARCH, /* int flags, */ 2484 gcc_driver_name, 2485 const_cast <char *const *> (argvec.address ()), 2486 ctxt_progname, /* const char *pname */ 2487 NULL, /* const char *outname */ 2488 NULL, /* const char *errname */ 2489 &exit_status, /* int *status */ 2490 &err); /* int *err*/ 2491 if (errmsg) 2492 { 2493 add_error (NULL, "error invoking gcc driver: %s", errmsg); 2494 return; 2495 } 2496 2497 /* pex_one can return a NULL errmsg when the executable wasn't 2498 found (or doesn't exist), so trap these cases also. */ 2499 if (exit_status || err) 2500 { 2501 add_error (NULL, 2502 "error invoking gcc driver: exit_status: %i err: %i", 2503 exit_status, err); 2504 add_error (NULL, 2505 "whilst attempting to run a driver named: %s", 2506 gcc_driver_name); 2507 add_error (NULL, 2508 "PATH was: %s", 2509 getenv ("PATH")); 2510 return; 2511 } 2512#undef ADD_ARG 2513} 2514 2515/* Extract the target-specific MULTILIB_DEFAULTS to 2516 multilib_defaults_raw for use by 2517 playback::context::add_multilib_driver_arguments (). */ 2518 2519#ifndef MULTILIB_DEFAULTS 2520#define MULTILIB_DEFAULTS { "" } 2521#endif 2522 2523static const char *const multilib_defaults_raw[] = MULTILIB_DEFAULTS; 2524 2525/* Helper function for playback::context::invoke_driver (). 2526 2527 32-bit and 64-bit multilib peer builds of libgccjit.so may share 2528 a driver binary. We need to pass in options to the shared driver 2529 to get the appropriate assembler/linker options for this multilib 2530 peer. */ 2531 2532void 2533playback::context:: 2534add_multilib_driver_arguments (vec <char *> *argvec) 2535{ 2536 JIT_LOG_SCOPE (get_logger ()); 2537 2538 /* Add copies of the arguments in multilib_defaults_raw to argvec, 2539 prepending each with a "-". */ 2540 for (size_t i = 0; i < ARRAY_SIZE (multilib_defaults_raw); i++) 2541 if (multilib_defaults_raw[i][0]) 2542 argvec->safe_push (concat ("-", multilib_defaults_raw[i], NULL)); 2543} 2544 2545/* Dynamically-link the built DSO file into this process, using dlopen. 2546 Wrap it up within a jit::result *, and return that. 2547 Return NULL if any errors occur, reporting them on this context. */ 2548 2549result * 2550playback::context:: 2551dlopen_built_dso () 2552{ 2553 JIT_LOG_SCOPE (get_logger ()); 2554 auto_timevar load_timevar (TV_LOAD); 2555 void *handle = NULL; 2556 const char *error = NULL; 2557 result *result_obj = NULL; 2558 2559 /* Clear any existing error. */ 2560 dlerror (); 2561 2562 handle = dlopen (m_tempdir->get_path_so_file (), 2563 RTLD_NOW | RTLD_LOCAL); 2564 if ((error = dlerror()) != NULL) { 2565 add_error (NULL, "%s", error); 2566 } 2567 if (handle) 2568 { 2569 /* We've successfully dlopened the result; create a 2570 jit::result object to wrap it. 2571 2572 We're done with the tempdir for now, but if the user 2573 has requested debugging, the user's debugger might not 2574 be capable of dealing with the .so file being unlinked 2575 immediately, so keep it around until after the result 2576 is released. We do this by handing over ownership of 2577 the jit::tempdir to the result. See PR jit/64206. */ 2578 tempdir *handover_tempdir; 2579 if (get_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO)) 2580 { 2581 handover_tempdir = m_tempdir; 2582 m_tempdir = NULL; 2583 /* The tempdir will eventually be cleaned up in the 2584 jit::result's dtor. */ 2585 log ("GCC_JIT_BOOL_OPTION_DEBUGINFO was set:" 2586 " handing over tempdir to jit::result"); 2587 } 2588 else 2589 { 2590 handover_tempdir = NULL; 2591 /* ... and retain ownership of m_tempdir so we clean it 2592 up it the playback::context's dtor. */ 2593 log ("GCC_JIT_BOOL_OPTION_DEBUGINFO was not set:" 2594 " retaining ownership of tempdir"); 2595 } 2596 2597 result_obj = new result (get_logger (), handle, handover_tempdir); 2598 } 2599 else 2600 result_obj = NULL; 2601 2602 return result_obj; 2603} 2604 2605/* Top-level hook for playing back a recording context. 2606 2607 This plays back m_recording_ctxt, and, if no errors 2608 occurred builds statement lists for and then postprocesses 2609 every function in the result. */ 2610 2611void 2612playback::context:: 2613replay () 2614{ 2615 JIT_LOG_SCOPE (get_logger ()); 2616 /* Adapted from c-common.c:c_common_nodes_and_builtins. */ 2617 tree array_domain_type = build_index_type (size_int (200)); 2618 m_char_array_type_node 2619 = build_array_type (char_type_node, array_domain_type); 2620 2621 m_const_char_ptr 2622 = build_pointer_type (build_qualified_type (char_type_node, 2623 TYPE_QUAL_CONST)); 2624 2625 /* Replay the recorded events: */ 2626 timevar_push (TV_JIT_REPLAY); 2627 2628 m_recording_ctxt->replay_into (this); 2629 2630 /* Clean away the temporary references from recording objects 2631 to playback objects. We have to do this now since the 2632 latter are GC-allocated, but the former don't mark these 2633 refs. Hence we must stop using them before the GC can run. */ 2634 m_recording_ctxt->disassociate_from_playback (); 2635 2636 /* The builtins_manager, if any, is associated with the recording::context 2637 and might be reused for future compiles on other playback::contexts, 2638 but its m_attributes array is not GTY-labeled and hence will become 2639 nonsense if the GC runs. Purge this state. */ 2640 builtins_manager *bm = get_builtins_manager (); 2641 if (bm) 2642 bm->finish_playback (); 2643 2644 timevar_pop (TV_JIT_REPLAY); 2645 2646 if (!errors_occurred ()) 2647 { 2648 int i; 2649 function *func; 2650 2651 /* No GC can happen yet; process the cached source locations. */ 2652 handle_locations (); 2653 2654 /* We've now created tree nodes for the stmts in the various blocks 2655 in each function, but we haven't built each function's single stmt 2656 list yet. Do so now. */ 2657 FOR_EACH_VEC_ELT (m_functions, i, func) 2658 func->build_stmt_list (); 2659 2660 /* No GC can have happened yet. */ 2661 2662 /* Postprocess the functions. This could trigger GC. */ 2663 FOR_EACH_VEC_ELT (m_functions, i, func) 2664 { 2665 gcc_assert (func); 2666 func->postprocess (); 2667 } 2668 } 2669} 2670 2671/* Dump the generated .s file to stderr. */ 2672 2673void 2674playback::context:: 2675dump_generated_code () 2676{ 2677 JIT_LOG_SCOPE (get_logger ()); 2678 char buf[4096]; 2679 size_t sz; 2680 FILE *f_in = fopen (get_path_s_file (), "r"); 2681 if (!f_in) 2682 return; 2683 2684 while ( (sz = fread (buf, 1, sizeof (buf), f_in)) ) 2685 fwrite (buf, 1, sz, stderr); 2686 2687 fclose (f_in); 2688} 2689 2690/* Get the supposed path of the notional "fake.c" file within the 2691 tempdir. This file doesn't exist, but the rest of the compiler 2692 needs a name. */ 2693 2694const char * 2695playback::context:: 2696get_path_c_file () const 2697{ 2698 return m_tempdir->get_path_c_file (); 2699} 2700 2701/* Get the path of the assembler output file "fake.s" file within the 2702 tempdir. */ 2703 2704const char * 2705playback::context:: 2706get_path_s_file () const 2707{ 2708 return m_tempdir->get_path_s_file (); 2709} 2710 2711/* Get the path of the DSO object file "fake.so" file within the 2712 tempdir. */ 2713 2714const char * 2715playback::context:: 2716get_path_so_file () const 2717{ 2718 return m_tempdir->get_path_so_file (); 2719} 2720 2721/* qsort comparator for comparing pairs of playback::source_line *, 2722 ordering them by line number. */ 2723 2724static int 2725line_comparator (const void *lhs, const void *rhs) 2726{ 2727 const playback::source_line *line_lhs = \ 2728 *static_cast<const playback::source_line * const*> (lhs); 2729 const playback::source_line *line_rhs = \ 2730 *static_cast<const playback::source_line * const*> (rhs); 2731 return line_lhs->get_line_num () - line_rhs->get_line_num (); 2732} 2733 2734/* qsort comparator for comparing pairs of playback::location *, 2735 ordering them by column number. */ 2736 2737static int 2738location_comparator (const void *lhs, const void *rhs) 2739{ 2740 const playback::location *loc_lhs = \ 2741 *static_cast<const playback::location * const *> (lhs); 2742 const playback::location *loc_rhs = \ 2743 *static_cast<const playback::location * const *> (rhs); 2744 return loc_lhs->get_column_num () - loc_rhs->get_column_num (); 2745} 2746 2747/* Our API allows locations to be created in arbitrary orders, but the 2748 linemap API requires locations to be created in ascending order 2749 as if we were tokenizing files. 2750 2751 This hook sorts all of the the locations that have been created, and 2752 calls into the linemap API, creating linemap entries in sorted order 2753 for our locations. */ 2754 2755void 2756playback::context:: 2757handle_locations () 2758{ 2759 /* Create the source code locations, following the ordering rules 2760 imposed by the linemap API. 2761 2762 line_table is a global. */ 2763 JIT_LOG_SCOPE (get_logger ()); 2764 int i; 2765 source_file *file; 2766 2767 FOR_EACH_VEC_ELT (m_source_files, i, file) 2768 { 2769 linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0); 2770 2771 /* Sort lines by ascending line numbers. */ 2772 file->m_source_lines.qsort (&line_comparator); 2773 2774 int j; 2775 source_line *line; 2776 FOR_EACH_VEC_ELT (file->m_source_lines, j, line) 2777 { 2778 int k; 2779 location *loc; 2780 2781 /* Sort locations in line by ascending column numbers. */ 2782 line->m_locations.qsort (&location_comparator); 2783 2784 /* Determine maximum column within this line. */ 2785 gcc_assert (line->m_locations.length () > 0); 2786 location *final_column = 2787 line->m_locations[line->m_locations.length () - 1]; 2788 int max_col = final_column->get_column_num (); 2789 2790 linemap_line_start (line_table, line->get_line_num (), max_col); 2791 FOR_EACH_VEC_ELT (line->m_locations, k, loc) 2792 { 2793 loc->m_srcloc = \ 2794 linemap_position_for_column (line_table, loc->get_column_num ()); 2795 } 2796 } 2797 2798 linemap_add (line_table, LC_LEAVE, false, NULL, 0); 2799 } 2800 2801 /* line_table should now be populated; every playback::location should 2802 now have an m_srcloc. */ 2803 2804 /* Now assign them to tree nodes as appropriate. */ 2805 std::pair<tree, location *> *cached_location; 2806 2807 FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location) 2808 { 2809 tree t = cached_location->first; 2810 source_location srcloc = cached_location->second->m_srcloc; 2811 2812 /* This covers expressions: */ 2813 if (CAN_HAVE_LOCATION_P (t)) 2814 SET_EXPR_LOCATION (t, srcloc); 2815 else if (CODE_CONTAINS_STRUCT(TREE_CODE(t), TS_DECL_MINIMAL)) 2816 DECL_SOURCE_LOCATION (t) = srcloc; 2817 else 2818 { 2819 /* Don't know how to set location on this node. */ 2820 } 2821 } 2822} 2823 2824/* We handle errors on a playback::context by adding them to the 2825 corresponding recording::context. */ 2826 2827void 2828playback::context:: 2829add_error (location *loc, const char *fmt, ...) 2830{ 2831 va_list ap; 2832 va_start (ap, fmt); 2833 m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL, 2834 fmt, ap); 2835 va_end (ap); 2836} 2837 2838/* We handle errors on a playback::context by adding them to the 2839 corresponding recording::context. */ 2840 2841void 2842playback::context:: 2843add_error_va (location *loc, const char *fmt, va_list ap) 2844{ 2845 m_recording_ctxt->add_error_va (loc ? loc->get_recording_loc () : NULL, 2846 fmt, ap); 2847} 2848 2849/* Dealing with the linemap API. */ 2850 2851/* Construct a playback::location for a recording::location, if it 2852 doesn't exist already. */ 2853 2854playback::location * 2855playback::context:: 2856new_location (recording::location *rloc, 2857 const char *filename, 2858 int line, 2859 int column) 2860{ 2861 /* Get the source_file for filename, creating if necessary. */ 2862 source_file *src_file = get_source_file (filename); 2863 /* Likewise for the line within the file. */ 2864 source_line *src_line = src_file->get_source_line (line); 2865 /* Likewise for the column within the line. */ 2866 location *loc = src_line->get_location (rloc, column); 2867 return loc; 2868} 2869 2870/* Deferred setting of the location for a given tree, by adding the 2871 (tree, playback::location) pair to a list of deferred associations. 2872 We will actually set the location on the tree later on once 2873 the source_location for the playback::location exists. */ 2874 2875void 2876playback::context:: 2877set_tree_location (tree t, location *loc) 2878{ 2879 gcc_assert (loc); 2880 m_cached_locations.safe_push (std::make_pair (t, loc)); 2881} 2882 2883 2884/* Construct a playback::source_file for the given source 2885 filename, if it doesn't exist already. */ 2886 2887playback::source_file * 2888playback::context:: 2889get_source_file (const char *filename) 2890{ 2891 /* Locate the file. 2892 For simplicitly, this is currently a linear search. 2893 Replace with a hash if this shows up in the profile. */ 2894 int i; 2895 source_file *file; 2896 tree ident_filename = get_identifier (filename); 2897 2898 FOR_EACH_VEC_ELT (m_source_files, i, file) 2899 if (file->filename_as_tree () == ident_filename) 2900 return file; 2901 2902 /* Not found. */ 2903 file = new source_file (ident_filename); 2904 m_source_files.safe_push (file); 2905 return file; 2906} 2907 2908/* Constructor for gcc::jit::playback::source_file. */ 2909 2910playback::source_file::source_file (tree filename) : 2911 m_source_lines (), 2912 m_filename (filename) 2913{ 2914} 2915 2916/* Don't leak vec's internal buffer (in non-GC heap) when we are 2917 GC-ed. */ 2918 2919void 2920playback::source_file::finalizer () 2921{ 2922 m_source_lines.release (); 2923} 2924 2925/* Construct a playback::source_line for the given line 2926 within this source file, if one doesn't exist already. */ 2927 2928playback::source_line * 2929playback::source_file:: 2930get_source_line (int line_num) 2931{ 2932 /* Locate the line. 2933 For simplicitly, this is currently a linear search. 2934 Replace with a hash if this shows up in the profile. */ 2935 int i; 2936 source_line *line; 2937 2938 FOR_EACH_VEC_ELT (m_source_lines, i, line) 2939 if (line->get_line_num () == line_num) 2940 return line; 2941 2942 /* Not found. */ 2943 line = new source_line (this, line_num); 2944 m_source_lines.safe_push (line); 2945 return line; 2946} 2947 2948/* Constructor for gcc::jit::playback::source_line. */ 2949 2950playback::source_line::source_line (source_file *file, int line_num) : 2951 m_locations (), 2952 m_source_file (file), 2953 m_line_num (line_num) 2954{ 2955} 2956 2957/* Don't leak vec's internal buffer (in non-GC heap) when we are 2958 GC-ed. */ 2959 2960void 2961playback::source_line::finalizer () 2962{ 2963 m_locations.release (); 2964} 2965 2966/* Construct a playback::location for the given column 2967 within this line of a specific source file, if one doesn't exist 2968 already. */ 2969 2970playback::location * 2971playback::source_line:: 2972get_location (recording::location *rloc, int column_num) 2973{ 2974 int i; 2975 location *loc; 2976 2977 /* Another linear search that probably should be a hash table. */ 2978 FOR_EACH_VEC_ELT (m_locations, i, loc) 2979 if (loc->get_column_num () == column_num) 2980 return loc; 2981 2982 /* Not found. */ 2983 loc = new location (rloc, this, column_num); 2984 m_locations.safe_push (loc); 2985 return loc; 2986} 2987 2988/* Constructor for gcc::jit::playback::location. */ 2989 2990playback::location::location (recording::location *loc, 2991 source_line *line, 2992 int column_num) : 2993 m_srcloc (UNKNOWN_LOCATION), 2994 m_recording_loc (loc), 2995 m_line (line), 2996 m_column_num(column_num) 2997{ 2998} 2999 3000/* The active gcc::jit::playback::context instance. This is a singleton, 3001 guarded by jit_mutex. */ 3002 3003playback::context *active_playback_ctxt; 3004 3005} // namespace gcc::jit 3006 3007} // namespace gcc 3008