1 /* Internals of libgccjit: classes for playing back recorded API calls.
2 Copyright (C) 2013-2023 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 #ifndef JIT_PLAYBACK_H
22 #define JIT_PLAYBACK_H
23
24 #include <utility> // for std::pair
25
26 #include "timevar.h"
27 #include "varasm.h"
28
29 #include "jit-recording.h"
30
31 struct diagnostic_context;
32 struct diagnostic_info;
33
34 namespace gcc {
35
36 namespace jit {
37
38 /**********************************************************************
39 Playback.
40 **********************************************************************/
41
42 namespace playback {
43
44 /* playback::context is an abstract base class.
45
46 The two concrete subclasses are:
47 - playback::compile_to_memory
48 - playback::compile_to_file. */
49
50 class context : public log_user
51 {
52 public:
53 context (::gcc::jit::recording::context *ctxt);
54 ~context ();
55
56 void gt_ggc_mx ();
57
58 void replay ();
59
60 location *
61 new_location (recording::location *rloc,
62 const char *filename,
63 int line,
64 int column);
65
66 type *
67 get_type (enum gcc_jit_types type);
68
69 type *
70 new_array_type (location *loc,
71 type *element_type,
72 int num_elements);
73
74 field *
75 new_field (location *loc,
76 type *type,
77 const char *name);
78
79 field *
80 new_bitfield (location *loc,
81 type *type,
82 int width,
83 const char *name);
84
85 compound_type *
86 new_compound_type (location *loc,
87 const char *name,
88 bool is_struct); /* else is union */
89
90 type *
91 new_function_type (type *return_type,
92 const auto_vec<type *> *param_types,
93 int is_variadic);
94
95 param *
96 new_param (location *loc,
97 type *type,
98 const char *name);
99
100 function *
101 new_function (location *loc,
102 enum gcc_jit_function_kind kind,
103 type *return_type,
104 const char *name,
105 const auto_vec<param *> *params,
106 int is_variadic,
107 enum built_in_function builtin_id);
108
109 lvalue *
110 new_global (location *loc,
111 enum gcc_jit_global_kind kind,
112 type *type,
113 const char *name,
114 enum global_var_flags flags);
115
116 lvalue *
117 new_global_initialized (location *loc,
118 enum gcc_jit_global_kind kind,
119 type *type,
120 size_t element_size,
121 size_t initializer_num_elem,
122 const void *initializer,
123 const char *name,
124 enum global_var_flags flags);
125
126 rvalue *
127 new_ctor (location *log,
128 type *type,
129 const auto_vec<field*> *fields,
130 const auto_vec<rvalue*> *rvalues);
131
132
133 void
134 global_set_init_rvalue (lvalue* variable,
135 rvalue* init);
136
137 template <typename HOST_TYPE>
138 rvalue *
139 new_rvalue_from_const (type *type,
140 HOST_TYPE value);
141
142 rvalue *
143 new_string_literal (const char *value);
144
145 rvalue *
146 new_rvalue_from_vector (location *loc,
147 type *type,
148 const auto_vec<rvalue *> &elements);
149
150 rvalue *
151 new_unary_op (location *loc,
152 enum gcc_jit_unary_op op,
153 type *result_type,
154 rvalue *a);
155
156 rvalue *
157 new_binary_op (location *loc,
158 enum gcc_jit_binary_op op,
159 type *result_type,
160 rvalue *a, rvalue *b);
161
162 rvalue *
163 new_comparison (location *loc,
164 enum gcc_jit_comparison op,
165 rvalue *a, rvalue *b, type *vec_result_type);
166
167 rvalue *
168 new_call (location *loc,
169 function *func,
170 const auto_vec<rvalue *> *args,
171 bool require_tail_call);
172
173 rvalue *
174 new_call_through_ptr (location *loc,
175 rvalue *fn_ptr,
176 const auto_vec<rvalue *> *args,
177 bool require_tail_call);
178
179 rvalue *
180 new_cast (location *loc,
181 rvalue *expr,
182 type *type_);
183
184 rvalue *
185 new_bitcast (location *loc,
186 rvalue *expr,
187 type *type_);
188
189 lvalue *
190 new_array_access (location *loc,
191 rvalue *ptr,
192 rvalue *index);
193
194 void
195 set_str_option (enum gcc_jit_str_option opt,
196 const char *value);
197
198 void
199 set_int_option (enum gcc_jit_int_option opt,
200 int value);
201
202 void
203 set_bool_option (enum gcc_jit_bool_option opt,
204 int value);
205
206 const char *
207 get_str_option (enum gcc_jit_str_option opt) const
208 {
209 return m_recording_ctxt->get_str_option (opt);
210 }
211
212 int
213 get_int_option (enum gcc_jit_int_option opt) const
214 {
215 return m_recording_ctxt->get_int_option (opt);
216 }
217
218 int
219 get_bool_option (enum gcc_jit_bool_option opt) const
220 {
221 return m_recording_ctxt->get_bool_option (opt);
222 }
223
224 int
225 get_inner_bool_option (enum inner_bool_option opt) const
226 {
227 return m_recording_ctxt->get_inner_bool_option (opt);
228 }
229
230 builtins_manager *get_builtins_manager () const
231 {
232 return m_recording_ctxt->get_builtins_manager ();
233 }
234
235 void
236 compile ();
237
238 void
239 add_error (location *loc, const char *fmt, ...)
240 GNU_PRINTF(3, 4);
241
242 void
243 add_error_va (location *loc, const char *fmt, va_list ap)
244 GNU_PRINTF(3, 0);
245
246 const char *
247 get_first_error () const;
248
249 void
250 add_diagnostic (struct diagnostic_context *context,
251 struct diagnostic_info *diagnostic);
252
253 void
254 set_tree_location (tree t, location *loc);
255
256 tree
257 new_field_access (location *loc,
258 tree datum,
259 field *field);
260
261 tree
262 new_dereference (tree ptr, location *loc);
263
264 tree
265 as_truth_value (tree expr, location *loc);
266
267 bool errors_occurred () const
268 {
269 return m_recording_ctxt->errors_occurred ();
270 }
271
272 timer *get_timer () const { return m_recording_ctxt->get_timer (); }
273
274 void add_top_level_asm (const char *asm_stmts);
275
276 private:
277 void dump_generated_code ();
278
279 rvalue *
280 build_call (location *loc,
281 tree fn_ptr,
282 const auto_vec<rvalue *> *args,
283 bool require_tail_call);
284
285 tree
286 build_cast (location *loc,
287 rvalue *expr,
288 type *type_);
289
290 source_file *
291 get_source_file (const char *filename);
292
293 tree
294 get_tree_node_for_type (enum gcc_jit_types type_);
295
296 void handle_locations ();
297
298 void init_types ();
299
300 const char * get_path_c_file () const;
301 const char * get_path_s_file () const;
302 const char * get_path_so_file () const;
303
304 tree
305 global_new_decl (location *loc,
306 enum gcc_jit_global_kind kind,
307 type *type,
308 const char *name,
309 enum global_var_flags flags);
310 lvalue *
311 global_finalize_lvalue (tree inner);
312
313 private:
314
315 /* Functions for implementing "compile". */
316
317 void lock ();
318 void unlock ();
319 struct scoped_lock;
320
321 void
322 make_fake_args (vec <char *> *argvec,
323 const char *ctxt_progname,
324 vec <recording::requested_dump> *requested_dumps);
325
326 void
327 extract_any_requested_dumps
328 (vec <recording::requested_dump> *requested_dumps);
329
330 char *
331 read_dump_file (const char *path);
332
333 virtual void postprocess (const char *ctxt_progname) = 0;
334
335 protected:
336 tempdir *get_tempdir () { return m_tempdir; }
337
338 void
339 convert_to_dso (const char *ctxt_progname);
340
341 void
342 invoke_driver (const char *ctxt_progname,
343 const char *input_file,
344 const char *output_file,
345 timevar_id_t tv_id,
346 bool shared,
347 bool run_linker);
348
349 void
350 add_multilib_driver_arguments (vec <char *> *argvec);
351
352 result *
353 dlopen_built_dso ();
354
355 private:
356 void
357 invoke_embedded_driver (const vec <char *> *argvec);
358
359 void
360 invoke_external_driver (const char *ctxt_progname,
361 vec <char *> *argvec);
362
363 private:
364 ::gcc::jit::recording::context *m_recording_ctxt;
365
366 tempdir *m_tempdir;
367
368 auto_vec<function *> m_functions;
369 auto_vec<tree> m_globals;
370 tree m_const_char_ptr;
371
372 /* Source location handling. */
373 auto_vec<source_file *> m_source_files;
374
375 auto_vec<std::pair<tree, location *> > m_cached_locations;
376 };
377
378 class compile_to_memory : public context
379 {
380 public:
381 compile_to_memory (recording::context *ctxt);
382 void postprocess (const char *ctxt_progname) final override;
383
384 result *get_result_obj () const { return m_result; }
385
386 private:
387 result *m_result;
388 };
389
390 class compile_to_file : public context
391 {
392 public:
393 compile_to_file (recording::context *ctxt,
394 enum gcc_jit_output_kind output_kind,
395 const char *output_path);
396 void postprocess (const char *ctxt_progname) final override;
397
398 private:
399 void
400 copy_file (const char *src_path,
401 const char *dst_path);
402
403 private:
404 enum gcc_jit_output_kind m_output_kind;
405 const char *m_output_path;
406 };
407
408
409 /* A temporary wrapper object.
410 These objects are (mostly) only valid during replay.
411 We allocate them on the GC heap, so that they will be cleaned
412 the next time the GC collects.
413 The exception is the "function" class, which is tracked and marked by
414 the jit::context, since it needs to stay alive during post-processing
415 (when the GC could run). */
416 class wrapper
417 {
418 public:
419 /* Allocate in the GC heap. */
420 void *operator new (size_t sz);
421
422 /* Some wrapper subclasses contain vec<> and so need to
423 release them when they are GC-ed. */
424 virtual void finalizer () { }
425
426 };
427
428 class type : public wrapper
429 {
430 public:
431 type (tree inner)
432 : m_inner(inner)
433 {}
434
435 tree as_tree () const { return m_inner; }
436
437 type *get_pointer () const { return new type (build_pointer_type (m_inner)); }
438
439 type *get_const () const
440 {
441 return new type (build_qualified_type (m_inner, TYPE_QUAL_CONST));
442 }
443
444 type *get_volatile () const
445 {
446 return new type (build_qualified_type (m_inner, TYPE_QUAL_VOLATILE));
447 }
448
449 type *get_aligned (size_t alignment_in_bytes) const;
450 type *get_vector (size_t num_units) const;
451
452 private:
453 tree m_inner;
454 };
455
456 class compound_type : public type
457 {
458 public:
459 compound_type (tree inner)
460 : type (inner)
461 {}
462
463 void set_fields (const auto_vec<field *> *fields);
464 };
465
466 class field : public wrapper
467 {
468 public:
469 field (tree inner)
470 : m_inner(inner)
471 {}
472
473 tree as_tree () const { return m_inner; }
474
475 private:
476 tree m_inner;
477 };
478
479 class bitfield : public field {};
480
481 class function : public wrapper
482 {
483 public:
484 function(context *ctxt, tree fndecl, enum gcc_jit_function_kind kind);
485
486 void gt_ggc_mx ();
487 void finalizer () final override;
488
489 tree get_return_type_as_tree () const;
490
491 tree as_fndecl () const { return m_inner_fndecl; }
492
493 enum gcc_jit_function_kind get_kind () const { return m_kind; }
494
495 lvalue *
496 new_local (location *loc,
497 type *type,
498 const char *name);
499
500 block*
501 new_block (const char *name);
502
503 rvalue *
504 get_address (location *loc);
505
506 void
507 build_stmt_list ();
508
509 void
510 postprocess ();
511
512 public:
513 context *m_ctxt;
514
515 public:
516 void
517 set_tree_location (tree t, location *loc)
518 {
519 m_ctxt->set_tree_location (t, loc);
520 }
521
522 private:
523 tree m_inner_fndecl;
524 tree m_inner_block;
525 tree m_inner_bind_expr;
526 enum gcc_jit_function_kind m_kind;
527 tree m_stmt_list;
528 tree_stmt_iterator m_stmt_iter;
529 vec<block *> m_blocks;
530 };
531
532 struct case_
533 {
534 case_ (rvalue *min_value, rvalue *max_value, block *dest_block)
535 : m_min_value (min_value),
536 m_max_value (max_value),
537 m_dest_block (dest_block)
538 {}
539
540 rvalue *m_min_value;
541 rvalue *m_max_value;
542 block *m_dest_block;
543 };
544
545 struct asm_operand
546 {
547 asm_operand (const char *asm_symbolic_name,
548 const char *constraint,
549 tree expr)
550 : m_asm_symbolic_name (asm_symbolic_name),
551 m_constraint (constraint),
552 m_expr (expr)
553 {}
554
555 const char *m_asm_symbolic_name;
556 const char *m_constraint;
557 tree m_expr;
558 };
559
560 class block : public wrapper
561 {
562 public:
563 block (function *func,
564 const char *name);
565
566 void finalizer () final override;
567
568 tree as_label_decl () const { return m_label_decl; }
569
570 function *get_function () const { return m_func; }
571
572 void
573 add_eval (location *loc,
574 rvalue *rvalue);
575
576 void
577 add_assignment (location *loc,
578 lvalue *lvalue,
579 rvalue *rvalue);
580
581 void
582 add_comment (location *loc,
583 const char *text);
584
585 void
586 add_conditional (location *loc,
587 rvalue *boolval,
588 block *on_true,
589 block *on_false);
590
591 block *
592 add_block (location *loc,
593 const char *name);
594
595 void
596 add_jump (location *loc,
597 block *target);
598
599 void
600 add_return (location *loc,
601 rvalue *rvalue);
602
603 void
604 add_switch (location *loc,
605 rvalue *expr,
606 block *default_block,
607 const auto_vec <case_> *cases);
608
609 void
610 add_extended_asm (location *loc,
611 const char *asm_template,
612 bool is_volatile,
613 bool is_inline,
614 const auto_vec <asm_operand> *outputs,
615 const auto_vec <asm_operand> *inputs,
616 const auto_vec <const char *> *clobbers,
617 const auto_vec <block *> *goto_blocks);
618
619 private:
620 void
621 set_tree_location (tree t, location *loc)
622 {
623 m_func->set_tree_location (t, loc);
624 }
625
626 void add_stmt (tree stmt)
627 {
628 /* TODO: use one stmt_list per block. */
629 m_stmts.safe_push (stmt);
630 }
631
632 private:
633 function *m_func;
634 tree m_label_decl;
635 vec<tree> m_stmts;
636
637 public: // for now
638 tree m_label_expr;
639
640 friend class function;
641 };
642
643 class rvalue : public wrapper
644 {
645 public:
646 rvalue (context *ctxt, tree inner)
647 : m_ctxt (ctxt),
648 m_inner (inner)
649 {
650 /* Pre-mark tree nodes with TREE_VISITED so that they can be
651 deeply unshared during gimplification (including across
652 functions); this requires LANG_HOOKS_DEEP_UNSHARING to be true. */
653 TREE_VISITED (inner) = 1;
654 }
655
656 rvalue *
657 as_rvalue () { return this; }
658
659 tree as_tree () const { return m_inner; }
660
661 context *get_context () const { return m_ctxt; }
662
663 type *
664 get_type () { return new type (TREE_TYPE (m_inner)); }
665
666 rvalue *
667 access_field (location *loc,
668 field *field);
669
670 lvalue *
671 dereference_field (location *loc,
672 field *field);
673
674 lvalue *
675 dereference (location *loc);
676
677 private:
678 context *m_ctxt;
679 tree m_inner;
680 };
681
682 class lvalue : public rvalue
683 {
684 public:
685 lvalue (context *ctxt, tree inner)
686 : rvalue(ctxt, inner)
687 {}
688
689 lvalue *
690 as_lvalue () { return this; }
691
692 lvalue *
693 access_field (location *loc,
694 field *field);
695
696 rvalue *
697 get_address (location *loc);
698
699 void
700 set_tls_model (enum tls_model tls_model)
701 {
702 set_decl_tls_model (as_tree (), tls_model);
703 }
704
705 void
706 set_link_section (const char* name)
707 {
708 set_decl_section_name (as_tree (), name);
709 }
710
711 void
712 set_register_name (const char* reg_name)
713 {
714 set_user_assembler_name (as_tree (), reg_name);
715 DECL_REGISTER (as_tree ()) = 1;
716 DECL_HARD_REGISTER (as_tree ()) = 1;
717 }
718
719 void
720 set_alignment (int alignment)
721 {
722 SET_DECL_ALIGN (as_tree (), alignment * BITS_PER_UNIT);
723 DECL_USER_ALIGN (as_tree ()) = 1;
724 }
725
726 private:
727 bool mark_addressable (location *loc);
728 };
729
730 class param : public lvalue
731 {
732 public:
733 param (context *ctxt, tree inner)
734 : lvalue(ctxt, inner)
735 {}
736 };
737
738 /* Dealing with the linemap API.
739
740 It appears that libcpp requires locations to be created as if by
741 a tokenizer, creating them by filename, in ascending order of
742 line/column, whereas our API doesn't impose any such constraints:
743 we allow client code to create locations in arbitrary orders.
744
745 To square this circle, we need to cache all location creation,
746 grouping things up by filename/line, and then creating the linemap
747 entries in a post-processing phase. */
748
749 /* A set of locations, all sharing a filename */
750 class source_file : public wrapper
751 {
752 public:
753 source_file (tree filename);
754 void finalizer () final override;
755
756 source_line *
757 get_source_line (int line_num);
758
759 tree filename_as_tree () const { return m_filename; }
760
761 const char*
762 get_filename () const { return IDENTIFIER_POINTER (m_filename); }
763
764 vec<source_line *> m_source_lines;
765
766 private:
767 tree m_filename;
768 };
769
770 /* A source line, with one or more locations of interest. */
771 class source_line : public wrapper
772 {
773 public:
774 source_line (source_file *file, int line_num);
775 void finalizer () final override;
776
777 location *
778 get_location (recording::location *rloc, int column_num);
779
780 int get_line_num () const { return m_line_num; }
781
782 vec<location *> m_locations;
783
784 private:
785 source_file *m_source_file ATTRIBUTE_UNUSED;
786 int m_line_num;
787 };
788
789 /* A specific location on a source line. This is what we expose
790 to the client API. */
791 class location : public wrapper
792 {
793 public:
794 location (recording::location *loc, source_line *line, int column_num);
795
796 int get_column_num () const { return m_column_num; }
797
798 recording::location *get_recording_loc () const { return m_recording_loc; }
799
800 location_t m_srcloc;
801
802 private:
803 recording::location *m_recording_loc;
804 source_line *m_line ATTRIBUTE_UNUSED;
805 int m_column_num;
806 };
807
808 } // namespace gcc::jit::playback
809
810 extern playback::context *active_playback_ctxt;
811
812 } // namespace gcc::jit
813
814 } // namespace gcc
815
816 #endif /* JIT_PLAYBACK_H */