/* Internals of libgccjit: classes for playing back recorded API calls. Copyright (C) 2013-2014 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 "jit-recording.h" namespace gcc { namespace jit { /********************************************************************** Playback. **********************************************************************/ namespace playback { class context { 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, 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, vec *params, int is_variadic, enum built_in_function builtin_id); lvalue * new_global (location *loc, type *type, const char *name); rvalue * new_rvalue_from_int (type *type, int value); rvalue * new_rvalue_from_double (type *type, double value); rvalue * new_rvalue_from_ptr (type *type, void *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, vec args); rvalue * new_call_through_ptr (location *loc, rvalue *fn_ptr, vec args); 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); } result * 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 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 (); } private: void dump_generated_code (); rvalue * build_call (location *loc, tree fn_ptr, vec args); tree build_cast (location *loc, rvalue *expr, type *type_); source_file * get_source_file (const char *filename); void handle_locations (); private: ::gcc::jit::recording::context *m_recording_ctxt; /* Allocated using xmalloc (by xstrdup). */ char *m_path_template; /* This either aliases m_path_template, or is NULL. */ char *m_path_tempdir; /* The following are allocated using xmalloc. */ char *m_path_c_file; char *m_path_s_file; char *m_path_so_file; vec m_functions; tree m_char_array_type_node; tree m_const_char_ptr; /* Source location handling. */ vec m_source_files; vec > m_cached_locations; }; /* 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); }; 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 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 (); 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; }; class block : public wrapper { public: block (function *func, const char *name); tree as_label_decl () const { return m_label_decl; } 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); 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); 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); 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 */