/* brig-code-entry-handler.h -- a gccbrig base class
   Copyright (C) 2016-2017 Free Software Foundation, Inc.
   Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
   for General Processor Tech.

   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 GCC_BRIG_CODE_ENTRY_HANDLER_H
#define GCC_BRIG_CODE_ENTRY_HANDLER_H

#include "brig-to-generic.h"

#include <map>
#include <vector>

class tree_element_unary_visitor;

/* An interface to organize the different types of element handlers
   for the code section.  */

class brig_code_entry_handler : public brig_entry_handler
{
public:
  typedef std::map<std::pair<BrigOpcode16_t, BrigType16_t>, tree> builtin_map;

  brig_code_entry_handler (brig_to_generic &parent);

  /* Handles the brig_code data at the given pointer and adds it to the
     currently built tree.  Returns the number of consumed bytes.  */

  virtual size_t operator () (const BrigBase *base) = 0;

  void append_statement (tree stmt);

protected:

  tree get_tree_expr_type_for_hsa_type (BrigType16_t brig_type) const;
  tree get_tree_cst_for_hsa_operand (const BrigOperandConstantBytes *brigConst,
				     tree type) const;
  tree get_builtin_for_hsa_opcode (tree type, BrigOpcode16_t brig_opcode,
				   BrigType16_t brig_type) const;
  tree get_comparison_result_type (tree source_type);

  tree build_code_ref (const BrigBase &ref);

  tree build_tree_operand (const BrigInstBase &brig_inst,
			   const BrigBase &operand,
			   tree operand_type = NULL_TREE,
			   bool is_input = false);

  tree build_address_operand (const BrigInstBase &brig_inst,
			      const BrigOperandAddress &addr_operand);

  tree build_tree_operand_from_brig (const BrigInstBase *brig_inst,
				     tree operand_type, size_t operand_index);

  tree build_tree_cst_element (BrigType16_t element_type,
			       const unsigned char *next_data) const;

  bool needs_workitem_context_data (BrigOpcode16_t brig_opcode) const;

  void unpack (tree value, tree_stl_vec &elements);
  tree pack (tree_stl_vec &elements);

  bool can_expand_builtin (BrigOpcode16_t brig_opcode) const;
  tree expand_builtin (BrigOpcode16_t brig_opcode, tree_stl_vec &operands);

  tree expand_or_call_builtin (BrigOpcode16_t brig_opcode,
			       BrigType16_t brig_type, tree arith_type,
			       tree_stl_vec &operands);

  tree add_temp_var (std::string name, tree expr);

  tree build_f2h_conversion (tree source);
  tree build_h2f_conversion (tree source);

  tree_stl_vec build_operands (const BrigInstBase &brig_inst);
  tree build_output_assignment (const BrigInstBase &brig_inst, tree output,
				tree inst_expr);

  tree apply_to_all_elements (tree_element_unary_visitor &visitor,
			      tree operand);

  HOST_WIDE_INT int_constant_value (tree node);

  tree extend_int (tree input, tree dest_type, tree src_type);

  /* HSAIL-specific builtin functions not yet integrated to gcc.  */

  static builtin_map s_custom_builtins;
};

/* Implement the Visitor software pattern for performing various actions on
   elements of vector operands.  This enables separating the vector element
   traversal/extraction/packing code from whatever different actions are
   performed to each element.  */

class tree_element_unary_visitor
{
public:
  tree operator () (brig_code_entry_handler &handler, tree operand);

  /* Performs an action to a single element, which can have originally
     been a vector element or a scalar.  */

  virtual tree visit_element (brig_code_entry_handler &handler, tree operand)
    = 0;
};

class tree_element_binary_visitor
{
public:
  tree operator () (brig_code_entry_handler &handler, tree operand0,
		   tree operand1);

  /* Performs an action to a pair of elements, which can have originally
     been a vector element or a scalar.  */

  virtual tree visit_element (brig_code_entry_handler &handler, tree operand0,
			      tree operand1)
    = 0;
};

/* Visitor for flushing float elements to zero.  */

class flush_to_zero : public tree_element_unary_visitor
{
public:
  flush_to_zero (bool fp16) : m_fp16 (fp16)
  {
  }

  virtual tree visit_element (brig_code_entry_handler &caller, tree operand);

private:

  /* True if the value should be flushed according to fp16 limits.  */

  bool m_fp16;
};

/* Visitor for converting F16 elements to F32.  */

class half_to_float : public tree_element_unary_visitor
{
public:
  virtual tree visit_element (brig_code_entry_handler &caller, tree operand);
};

/* Visitor for converting F32 elements to F16.  */

class float_to_half : public tree_element_unary_visitor
{
public:
  virtual tree visit_element (brig_code_entry_handler &caller, tree operand);
};

/* A base class for instruction types that support floating point
   modifiers.

   operator () delegates to subclasses (template method pattern) in
   type specific parts.  */

class brig_inst_mod_handler : public brig_code_entry_handler
{
public:
  brig_inst_mod_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  virtual size_t generate (const BrigBase *base);
  virtual const BrigAluModifier8_t *modifier (const BrigBase *base) const;
  virtual const BrigRound8_t *round (const BrigBase *base) const;

  size_t operator () (const BrigBase *base);
};

class brig_directive_function_handler : public brig_code_entry_handler
{
public:
  brig_directive_function_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }
  size_t operator () (const BrigBase *base);
};

class brig_directive_control_handler : public brig_code_entry_handler
{
public:
  brig_directive_control_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_directive_variable_handler : public brig_code_entry_handler
{
public:
  brig_directive_variable_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);

  tree build_variable (const BrigDirectiveVariable *brigVar,
		       tree_code var_decltype = VAR_DECL);

  size_t get_brig_var_alignment (const BrigDirectiveVariable *brigVar);
};

class brig_directive_fbarrier_handler : public brig_code_entry_handler
{
public:
  brig_directive_fbarrier_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_directive_label_handler : public brig_code_entry_handler
{
public:
  brig_directive_label_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_directive_comment_handler : public brig_code_entry_handler
{
public:
  brig_directive_comment_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_directive_arg_block_handler : public brig_code_entry_handler
{
public:
  brig_directive_arg_block_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_basic_inst_handler : public brig_code_entry_handler
{
public:
  brig_basic_inst_handler (brig_to_generic &parent);

  size_t operator () (const BrigBase *base);

private:
  tree build_lower_element_broadcast (tree vec_operand);

  bool must_be_scalarized (const BrigInstBase *brig_inst,
			   tree instr_type) const;

  tree build_inst_expr (BrigOpcode16_t brig_opcode, BrigType16_t brig_type,
			 tree arith_type, tree_stl_vec &operands);

  tree build_shuffle (tree arith_type, tree_stl_vec &operands);
  tree build_unpack (tree_stl_vec &operands);
  tree build_pack (tree_stl_vec &operands);

  tree build_unpack_lo_or_hi (BrigOpcode16_t brig_opcode, tree arith_type,
			      tree_stl_vec &operands);

  tree_code get_tree_code_for_hsa_opcode (BrigOpcode16_t brig_opcode,
					  BrigType16_t brig_type) const;
};

class brig_cvt_inst_handler : public brig_inst_mod_handler
{
public:
  brig_cvt_inst_handler (brig_to_generic &parent)
    : brig_inst_mod_handler (parent)
  {
  }

  virtual size_t generate (const BrigBase *base);
  virtual const BrigAluModifier8_t *modifier (const BrigBase *base) const;
  virtual const BrigRound8_t *round (const BrigBase *base) const;
};

class brig_branch_inst_handler : public brig_code_entry_handler
{
public:
  brig_branch_inst_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_mem_inst_handler : public brig_code_entry_handler
{
public:
  brig_mem_inst_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);

private:
  tree build_mem_access (const BrigInstBase *brig_inst, tree addr, tree data);
};

class brig_copy_move_inst_handler : public brig_code_entry_handler
{
public:
  brig_copy_move_inst_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);

private:
  size_t handle_lda (const BrigInstBase *base);
};

class brig_atomic_inst_handler : public brig_code_entry_handler
{
private:
  typedef std::map<std::string, tree> atomic_builtins_map;

public:
  brig_atomic_inst_handler (brig_to_generic &parent);

  size_t operator () (const BrigBase *base);

protected:
  size_t generate_tree (const BrigInstBase &inst,
			BrigAtomicOperation8_t atomic_opcode);
};

class brig_signal_inst_handler : public brig_atomic_inst_handler
{
public:
  brig_signal_inst_handler (brig_to_generic &parent)
    : brig_atomic_inst_handler (parent)
  {
  }
  size_t operator () (const BrigBase *base);
};

class brig_cmp_inst_handler : public brig_code_entry_handler
{
public:
  brig_cmp_inst_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};

class brig_seg_inst_handler : public brig_code_entry_handler
{
public:
  brig_seg_inst_handler (brig_to_generic &parent);

  size_t operator () (const BrigBase *base);
};

class brig_lane_inst_handler : public brig_code_entry_handler
{
public:
  brig_lane_inst_handler (brig_to_generic &parent);

  size_t operator () (const BrigBase *base);
};

class brig_queue_inst_handler : public brig_code_entry_handler
{
public:
  brig_queue_inst_handler (brig_to_generic &parent);

  size_t operator () (const BrigBase *base);
};

class brig_directive_module_handler : public brig_code_entry_handler
{
public:
  brig_directive_module_handler (brig_to_generic &parent)
    : brig_code_entry_handler (parent)
  {
  }

  size_t operator () (const BrigBase *base);
};


#endif
