/* Internals of libgccjit: classes for playing back recorded API calls.
   Copyright (C) 2013-2017 Free Software Foundation, Inc.
   Contributed by David Malcolm <dmalcolm@redhat.com>.

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
<http://www.gnu.org/licenses/>.  */

#ifndef JIT_PLAYBACK_H
#define JIT_PLAYBACK_H

#include <utility> // 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<type *> *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<param *> *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 <typename HOST_TYPE>
  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<rvalue *> *args,
	    bool require_tail_call);

  rvalue *
  new_call_through_ptr (location *loc,
			rvalue *fn_ptr,
			const auto_vec<rvalue *> *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<rvalue *> *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 <char *> *argvec,
		  const char *ctxt_progname,
		  vec <recording::requested_dump> *requested_dumps);

  void
  extract_any_requested_dumps
    (vec <recording::requested_dump> *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 <char *> *argvec);

  result *
  dlopen_built_dso ();

 private:
  void
  invoke_embedded_driver (const vec <char *> *argvec);

  void
  invoke_external_driver (const char *ctxt_progname,
			  vec <char *> *argvec);

private:
  ::gcc::jit::recording::context *m_recording_ctxt;

  tempdir *m_tempdir;

  auto_vec<function *> m_functions;
  auto_vec<tree> m_globals;
  tree m_char_array_type_node;
  tree m_const_char_ptr;

  /* Source location handling.  */
  auto_vec<source_file *> m_source_files;

  auto_vec<std::pair<tree, location *> > 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<field *> *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<block *> 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 <case_> *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<tree> 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<source_line *> 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<location *> 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 */

