/* Internals of libgccjit: classes for playing back recorded API calls. Copyright (C) 2013-2016 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #ifndef JIT_PLAYBACK_H #define JIT_PLAYBACK_H #include // for std::pair #include "timevar.h" #include "jit-recording.h" struct diagnostic_context; struct diagnostic_info; namespace gcc { namespace jit { /********************************************************************** Playback. **********************************************************************/ namespace playback { /* playback::context is an abstract base class. The two concrete subclasses are: - playback::compile_to_memory - playback::compile_to_file. */ class context : public log_user { public: context (::gcc::jit::recording::context *ctxt); ~context (); void gt_ggc_mx (); void replay (); location * new_location (recording::location *rloc, const char *filename, int line, int column); type * get_type (enum gcc_jit_types type); type * new_array_type (location *loc, type *element_type, int num_elements); field * new_field (location *loc, type *type, const char *name); compound_type * new_compound_type (location *loc, const char *name, bool is_struct); /* else is union */ type * new_function_type (type *return_type, const auto_vec *param_types, int is_variadic); param * new_param (location *loc, type *type, const char *name); function * new_function (location *loc, enum gcc_jit_function_kind kind, type *return_type, const char *name, const auto_vec *params, int is_variadic, enum built_in_function builtin_id); lvalue * new_global (location *loc, enum gcc_jit_global_kind kind, type *type, const char *name); template rvalue * new_rvalue_from_const (type *type, HOST_TYPE value); rvalue * new_string_literal (const char *value); rvalue * new_unary_op (location *loc, enum gcc_jit_unary_op op, type *result_type, rvalue *a); rvalue * new_binary_op (location *loc, enum gcc_jit_binary_op op, type *result_type, rvalue *a, rvalue *b); rvalue * new_comparison (location *loc, enum gcc_jit_comparison op, rvalue *a, rvalue *b); rvalue * new_call (location *loc, function *func, const auto_vec *args, bool require_tail_call); rvalue * new_call_through_ptr (location *loc, rvalue *fn_ptr, const auto_vec *args, bool require_tail_call); rvalue * new_cast (location *loc, rvalue *expr, type *type_); lvalue * new_array_access (location *loc, rvalue *ptr, rvalue *index); void set_str_option (enum gcc_jit_str_option opt, const char *value); void set_int_option (enum gcc_jit_int_option opt, int value); void set_bool_option (enum gcc_jit_bool_option opt, int value); const char * get_str_option (enum gcc_jit_str_option opt) const { return m_recording_ctxt->get_str_option (opt); } int get_int_option (enum gcc_jit_int_option opt) const { return m_recording_ctxt->get_int_option (opt); } int get_bool_option (enum gcc_jit_bool_option opt) const { return m_recording_ctxt->get_bool_option (opt); } int get_inner_bool_option (enum inner_bool_option opt) const { return m_recording_ctxt->get_inner_bool_option (opt); } builtins_manager *get_builtins_manager () const { return m_recording_ctxt->get_builtins_manager (); } void compile (); void add_error (location *loc, const char *fmt, ...) GNU_PRINTF(3, 4); void add_error_va (location *loc, const char *fmt, va_list ap) GNU_PRINTF(3, 0); const char * get_first_error () const; void add_diagnostic (struct diagnostic_context *context, struct diagnostic_info *diagnostic); void set_tree_location (tree t, location *loc); tree new_field_access (location *loc, tree datum, field *field); tree new_dereference (tree ptr, location *loc); tree as_truth_value (tree expr, location *loc); bool errors_occurred () const { return m_recording_ctxt->errors_occurred (); } timer *get_timer () const { return m_recording_ctxt->get_timer (); } private: void dump_generated_code (); rvalue * build_call (location *loc, tree fn_ptr, const auto_vec *args, bool require_tail_call); tree build_cast (location *loc, rvalue *expr, type *type_); source_file * get_source_file (const char *filename); void handle_locations (); const char * get_path_c_file () const; const char * get_path_s_file () const; const char * get_path_so_file () const; private: /* Functions for implementing "compile". */ void acquire_mutex (); void release_mutex (); void make_fake_args (vec *argvec, const char *ctxt_progname, vec *requested_dumps); void extract_any_requested_dumps (vec *requested_dumps); char * read_dump_file (const char *path); virtual void postprocess (const char *ctxt_progname) = 0; protected: tempdir *get_tempdir () { return m_tempdir; } void convert_to_dso (const char *ctxt_progname); void invoke_driver (const char *ctxt_progname, const char *input_file, const char *output_file, timevar_id_t tv_id, bool shared, bool run_linker); void add_multilib_driver_arguments (vec *argvec); result * dlopen_built_dso (); private: void invoke_embedded_driver (const vec *argvec); void invoke_external_driver (const char *ctxt_progname, vec *argvec); private: ::gcc::jit::recording::context *m_recording_ctxt; tempdir *m_tempdir; auto_vec m_functions; auto_vec m_globals; tree m_char_array_type_node; tree m_const_char_ptr; /* Source location handling. */ auto_vec m_source_files; auto_vec > m_cached_locations; }; class compile_to_memory : public context { public: compile_to_memory (recording::context *ctxt); void postprocess (const char *ctxt_progname) FINAL OVERRIDE; result *get_result_obj () const { return m_result; } private: result *m_result; }; class compile_to_file : public context { public: compile_to_file (recording::context *ctxt, enum gcc_jit_output_kind output_kind, const char *output_path); void postprocess (const char *ctxt_progname) FINAL OVERRIDE; private: void copy_file (const char *src_path, const char *dst_path); private: enum gcc_jit_output_kind m_output_kind; const char *m_output_path; }; /* A temporary wrapper object. These objects are (mostly) only valid during replay. We allocate them on the GC heap, so that they will be cleaned the next time the GC collects. The exception is the "function" class, which is tracked and marked by the jit::context, since it needs to stay alive during post-processing (when the GC could run). */ class wrapper { public: /* Allocate in the GC heap. */ void *operator new (size_t sz); /* Some wrapper subclasses contain vec<> and so need to release them when they are GC-ed. */ virtual void finalizer () { } }; class type : public wrapper { public: type (tree inner) : m_inner(inner) {} tree as_tree () const { return m_inner; } type *get_pointer () const { return new type (build_pointer_type (m_inner)); } type *get_const () const { return new type (build_qualified_type (m_inner, TYPE_QUAL_CONST)); } type *get_volatile () const { return new type (build_qualified_type (m_inner, TYPE_QUAL_VOLATILE)); } private: tree m_inner; }; class compound_type : public type { public: compound_type (tree inner) : type (inner) {} void set_fields (const auto_vec *fields); }; class field : public wrapper { public: field (tree inner) : m_inner(inner) {} tree as_tree () const { return m_inner; } private: tree m_inner; }; class function : public wrapper { public: function(context *ctxt, tree fndecl, enum gcc_jit_function_kind kind); void gt_ggc_mx (); void finalizer () FINAL OVERRIDE; tree get_return_type_as_tree () const; tree as_fndecl () const { return m_inner_fndecl; } enum gcc_jit_function_kind get_kind () const { return m_kind; } lvalue * new_local (location *loc, type *type, const char *name); block* new_block (const char *name); void build_stmt_list (); void postprocess (); public: context *m_ctxt; public: void set_tree_location (tree t, location *loc) { m_ctxt->set_tree_location (t, loc); } private: tree m_inner_fndecl; tree m_inner_block; tree m_inner_bind_expr; enum gcc_jit_function_kind m_kind; tree m_stmt_list; tree_stmt_iterator m_stmt_iter; vec m_blocks; }; struct case_ { case_ (rvalue *min_value, rvalue *max_value, block *dest_block) : m_min_value (min_value), m_max_value (max_value), m_dest_block (dest_block) {} rvalue *m_min_value; rvalue *m_max_value; block *m_dest_block; }; class block : public wrapper { public: block (function *func, const char *name); void finalizer () FINAL OVERRIDE; tree as_label_decl () const { return m_label_decl; } function *get_function () const { return m_func; } void add_eval (location *loc, rvalue *rvalue); void add_assignment (location *loc, lvalue *lvalue, rvalue *rvalue); void add_comment (location *loc, const char *text); void add_conditional (location *loc, rvalue *boolval, block *on_true, block *on_false); block * add_block (location *loc, const char *name); void add_jump (location *loc, block *target); void add_return (location *loc, rvalue *rvalue); void add_switch (location *loc, rvalue *expr, block *default_block, const auto_vec *cases); private: void set_tree_location (tree t, location *loc) { m_func->set_tree_location (t, loc); } void add_stmt (tree stmt) { /* TODO: use one stmt_list per block. */ m_stmts.safe_push (stmt); } private: function *m_func; tree m_label_decl; vec m_stmts; public: // for now tree m_label_expr; friend class function; }; class rvalue : public wrapper { public: rvalue (context *ctxt, tree inner) : m_ctxt (ctxt), m_inner (inner) {} rvalue * as_rvalue () { return this; } tree as_tree () const { return m_inner; } context *get_context () const { return m_ctxt; } type * get_type () { return new type (TREE_TYPE (m_inner)); } rvalue * access_field (location *loc, field *field); lvalue * dereference_field (location *loc, field *field); lvalue * dereference (location *loc); private: context *m_ctxt; tree m_inner; }; class lvalue : public rvalue { public: lvalue (context *ctxt, tree inner) : rvalue(ctxt, inner) {} lvalue * as_lvalue () { return this; } lvalue * access_field (location *loc, field *field); rvalue * get_address (location *loc); }; class param : public lvalue { public: param (context *ctxt, tree inner) : lvalue(ctxt, inner) {} }; /* Dealing with the linemap API. It appears that libcpp requires locations to be created as if by a tokenizer, creating them by filename, in ascending order of line/column, whereas our API doesn't impose any such constraints: we allow client code to create locations in arbitrary orders. To square this circle, we need to cache all location creation, grouping things up by filename/line, and then creating the linemap entries in a post-processing phase. */ /* A set of locations, all sharing a filename */ class source_file : public wrapper { public: source_file (tree filename); void finalizer () FINAL OVERRIDE; source_line * get_source_line (int line_num); tree filename_as_tree () const { return m_filename; } const char* get_filename () const { return IDENTIFIER_POINTER (m_filename); } vec m_source_lines; private: tree m_filename; }; /* A source line, with one or more locations of interest. */ class source_line : public wrapper { public: source_line (source_file *file, int line_num); void finalizer () FINAL OVERRIDE; location * get_location (recording::location *rloc, int column_num); int get_line_num () const { return m_line_num; } vec m_locations; private: source_file *m_source_file; int m_line_num; }; /* A specific location on a source line. This is what we expose to the client API. */ class location : public wrapper { public: location (recording::location *loc, source_line *line, int column_num); int get_column_num () const { return m_column_num; } recording::location *get_recording_loc () const { return m_recording_loc; } source_location m_srcloc; private: recording::location *m_recording_loc; source_line *m_line; int m_column_num; }; } // namespace gcc::jit::playback extern playback::context *active_playback_ctxt; } // namespace gcc::jit } // namespace gcc #endif /* JIT_PLAYBACK_H */