/**************************************************************************
 *
 * Copyright 2011-2012 Advanced Micro Devices, Inc.
 * Copyright 2009 VMware, Inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

/**
 * @file
 * TGSI to LLVM IR translation.
 *
 * @author Jose Fonseca <jfonseca@vmware.com>
 * @author Tom Stellard <thomas.stellard@amd.com>
 */

#ifndef LP_BLD_TGSI_H
#define LP_BLD_TGSI_H

#include "gallivm/lp_bld.h"
#include "gallivm/lp_bld_tgsi_action.h"
#include "gallivm/lp_bld_limits.h"
#include "gallivm/lp_bld_sample.h"
#include "lp_bld_type.h"
#include "pipe/p_compiler.h"
#include "pipe/p_state.h"
#include "tgsi/tgsi_exec.h"
#include "tgsi/tgsi_scan.h"
#include "tgsi/tgsi_info.h"

#define LP_CHAN_ALL ~0

#define LP_MAX_INSTRUCTIONS 256

struct tgsi_full_declaration;
struct tgsi_full_immediate;
struct tgsi_full_instruction;
struct tgsi_full_src_register;
struct tgsi_opcode_info;
struct tgsi_token;
struct tgsi_shader_info;
struct lp_build_mask_context;
struct gallivm_state;
struct lp_derivatives;
struct lp_build_tgsi_gs_iface;


enum lp_build_tex_modifier {
   LP_BLD_TEX_MODIFIER_NONE = 0,
   LP_BLD_TEX_MODIFIER_PROJECTED,
   LP_BLD_TEX_MODIFIER_LOD_BIAS,
   LP_BLD_TEX_MODIFIER_EXPLICIT_LOD,
   LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV,
   LP_BLD_TEX_MODIFIER_LOD_ZERO
};


/**
 * Describe a channel of a register.
 *
 * The value can be a:
 * - immediate value (i.e. derived from a IMM register)
 * - CONST[n].x/y/z/w
 * - IN[n].x/y/z/w
 * - undetermined (when .file == TGSI_FILE_NULL)
 *
 * This is one of the analysis results, and is used to described
 * the output color in terms of inputs.
 */
struct lp_tgsi_channel_info
{
   unsigned file:4; /* TGSI_FILE_* */
   unsigned swizzle:3; /* PIPE_SWIZZLE_x */
   union {
      uint32_t index;
      float value; /* for TGSI_FILE_IMMEDIATE */
   } u;
};


/**
 * Describe a texture sampler interpolator.
 *
 * The interpolation is described in terms of regular inputs.
 */
struct lp_tgsi_texture_info
{
   struct lp_tgsi_channel_info coord[4];
   unsigned target:8; /* TGSI_TEXTURE_* */
   unsigned sampler_unit:8;  /* Sampler unit */
   unsigned texture_unit:8;  /* Texture unit */
   unsigned modifier:8; /* LP_BLD_TEX_MODIFIER_* */
};


struct lp_tgsi_info
{
   struct tgsi_shader_info base;

   /*
    * Whether any of the texture opcodes access a register file other than
    * TGSI_FILE_INPUT.
    *
    * We could also handle TGSI_FILE_CONST/IMMEDIATE here, but there is little
    * benefit.
    */
   unsigned indirect_textures:1;

   /*
    * Whether any immediate values are outside the range of 0 and 1
    */
   unsigned unclamped_immediates:1;

   /*
    * Texture opcode description. Aimed at detecting and described direct
    * texture opcodes.
    */
   unsigned num_texs;
   struct lp_tgsi_texture_info tex[PIPE_MAX_SAMPLERS];

   /*
    * Output description. Aimed at detecting and describing simple blit
    * shaders.
    */
   struct lp_tgsi_channel_info output[PIPE_MAX_SHADER_OUTPUTS][4];

   /*
    * Shortcut pointers into the above (for fragment shaders).
    */
   const struct lp_tgsi_channel_info *cbuf[PIPE_MAX_COLOR_BUFS];
};

/**
 * Reference to system values.
 */
struct lp_bld_tgsi_system_values {
   LLVMValueRef instance_id;
   LLVMValueRef vertex_id;
   LLVMValueRef prim_id;
};


/**
 * Sampler code generation interface.
 *
 * Although texture sampling is a requirement for TGSI translation, it is
 * a very different problem with several different approaches to it. This
 * structure establishes an interface for texture sampling code generation, so
 * that we can easily use different texture sampling strategies.
 */
struct lp_build_sampler_soa
{
   void
   (*destroy)( struct lp_build_sampler_soa *sampler );

   void
   (*emit_fetch_texel)( const struct lp_build_sampler_soa *sampler,
                        struct gallivm_state *gallivm,
                        struct lp_type type,
                        boolean is_fetch,
                        unsigned texture_index,
                        unsigned sampler_index,
                        const LLVMValueRef *coords,
                        const LLVMValueRef *offsets,
                        const struct lp_derivatives *derivs,
                        LLVMValueRef lod_bias, /* optional */
                        LLVMValueRef explicit_lod, /* optional */
                        enum lp_sampler_lod_property,
                        LLVMValueRef *texel);

   void
   (*emit_size_query)( const struct lp_build_sampler_soa *sampler,
                       struct gallivm_state *gallivm,
                       struct lp_type type,
                       unsigned unit,
                       unsigned target,
                       boolean need_nr_mips,
                       enum lp_sampler_lod_property,
                       LLVMValueRef explicit_lod, /* optional */
                       LLVMValueRef *sizes_out);
};


struct lp_build_sampler_aos
{
   LLVMValueRef
   (*emit_fetch_texel)( struct lp_build_sampler_aos *sampler,
                        struct lp_build_context *bld,
                        unsigned target, /* TGSI_TEXTURE_* */
                        unsigned unit,
                        LLVMValueRef coords,
                        const struct lp_derivatives derivs,
                        enum lp_build_tex_modifier modifier);
};


void
lp_build_tgsi_info(const struct tgsi_token *tokens,
                   struct lp_tgsi_info *info);


void
lp_build_tgsi_soa(struct gallivm_state *gallivm,
                  const struct tgsi_token *tokens,
                  struct lp_type type,
                  struct lp_build_mask_context *mask,
                  LLVMValueRef consts_ptr,
                  LLVMValueRef const_sizes_ptr,
                  const struct lp_bld_tgsi_system_values *system_values,
                  const LLVMValueRef (*inputs)[4],
                  LLVMValueRef (*outputs)[4],
                  struct lp_build_sampler_soa *sampler,
                  const struct tgsi_shader_info *info,
                  const struct lp_build_tgsi_gs_iface *gs_iface);


void
lp_build_tgsi_aos(struct gallivm_state *gallivm,
                  const struct tgsi_token *tokens,
                  struct lp_type type,
                  const unsigned char swizzles[4],
                  LLVMValueRef consts_ptr,
                  const LLVMValueRef *inputs,
                  LLVMValueRef *outputs,
                  struct lp_build_sampler_aos *sampler,
                  const struct tgsi_shader_info *info);


enum lp_exec_mask_break_type {
   LP_EXEC_MASK_BREAK_TYPE_LOOP,
   LP_EXEC_MASK_BREAK_TYPE_SWITCH
};


struct lp_exec_mask {
   struct lp_build_context *bld;

   boolean has_mask;
   boolean ret_in_main;

   LLVMTypeRef int_vec_type;

   LLVMValueRef exec_mask;

   LLVMValueRef ret_mask;
   LLVMValueRef cond_mask;
   LLVMValueRef switch_mask;         /* current switch exec mask */
   LLVMValueRef cont_mask;
   LLVMValueRef break_mask;

   struct function_ctx {
      int pc;
      LLVMValueRef ret_mask;

      LLVMValueRef cond_stack[LP_MAX_TGSI_NESTING];
      int cond_stack_size;

      /* keep track if break belongs to switch or loop */
      enum lp_exec_mask_break_type break_type_stack[LP_MAX_TGSI_NESTING];
      enum lp_exec_mask_break_type break_type;

      struct {
         LLVMValueRef switch_val;
         LLVMValueRef switch_mask;
         LLVMValueRef switch_mask_default;
         boolean switch_in_default;
         unsigned switch_pc;
      } switch_stack[LP_MAX_TGSI_NESTING];
      int switch_stack_size;
      LLVMValueRef switch_val;
      LLVMValueRef switch_mask_default; /* reverse of switch mask used for default */
      boolean switch_in_default;        /* if switch exec is currently in default */
      unsigned switch_pc;               /* when used points to default or endswitch-1 */

      LLVMValueRef loop_limiter;
      LLVMBasicBlockRef loop_block;
      LLVMValueRef break_var;
      struct {
         LLVMBasicBlockRef loop_block;
         LLVMValueRef cont_mask;
         LLVMValueRef break_mask;
         LLVMValueRef break_var;
      } loop_stack[LP_MAX_TGSI_NESTING];
      int loop_stack_size;

   } *function_stack;
   int function_stack_size;
};

struct lp_build_tgsi_inst_list
{
   struct tgsi_full_instruction *instructions;
   uint max_instructions;
   uint num_instructions;
};

unsigned lp_bld_tgsi_list_init(struct lp_build_tgsi_context * bld_base);


unsigned lp_bld_tgsi_add_instruction(
   struct lp_build_tgsi_context * bld_base,
   const struct tgsi_full_instruction *inst_to_add);


struct lp_build_tgsi_context;


typedef LLVMValueRef (*lp_build_emit_fetch_fn)(struct lp_build_tgsi_context *,
                                        const struct tgsi_full_src_register *,
                                        enum tgsi_opcode_type,
                                        unsigned);

struct lp_build_tgsi_context
{
   struct lp_build_context base;

   struct lp_build_context uint_bld;
   struct lp_build_context int_bld;

   /** This array stores functions that are used to transform TGSI opcodes to
     * LLVM instructions.
     */
   struct lp_build_tgsi_action op_actions[TGSI_OPCODE_LAST];

   /* TGSI_OPCODE_RSQ is defined as 1 / sqrt( abs(src0.x) ), rsq_action
    * should compute 1 / sqrt (src0.x) */
   struct lp_build_tgsi_action rsq_action;

   struct lp_build_tgsi_action sqrt_action;

   const struct tgsi_shader_info *info;

   lp_build_emit_fetch_fn emit_fetch_funcs[TGSI_FILE_COUNT];

   LLVMValueRef (*emit_swizzle)(struct lp_build_tgsi_context *,
                         LLVMValueRef, unsigned, unsigned, unsigned, unsigned);


   void (*emit_debug)(struct lp_build_tgsi_context *,
                      const struct tgsi_full_instruction *,
                      const struct tgsi_opcode_info *);

   void (*emit_store)(struct lp_build_tgsi_context *,
                      const struct tgsi_full_instruction *,
                      const struct tgsi_opcode_info *,
                      LLVMValueRef dst[4]);

   void (*emit_declaration)(struct lp_build_tgsi_context *,
                             const struct tgsi_full_declaration *decl);

   void (*emit_immediate)(struct lp_build_tgsi_context *,
                          const struct tgsi_full_immediate *imm);


   /* Allow the user to store data in this structure rather than passing it
    * to every function. */
   void * userdata;

   boolean soa;

   int pc;

   struct tgsi_full_instruction *instructions;
   uint max_instructions;
   uint num_instructions;

   /** This function allows the user to insert some instructions at the
     * beginning of the program.  It is optional and does not need to be
     * implemented.
     */
   void (*emit_prologue)(struct lp_build_tgsi_context*);

   /** This function allows the user to insert some instructions at the end of
     * the program.  This callback is intended to be used for emitting
     * instructions to handle the export for the output registers, but it can
     * be used for any purpose.  Implementing this function is optiona, but
     * recommended.
     */
   void (*emit_epilogue)(struct lp_build_tgsi_context*);
};

struct lp_build_tgsi_gs_iface
{
   LLVMValueRef (*fetch_input)(const struct lp_build_tgsi_gs_iface *gs_iface,
                               struct lp_build_tgsi_context * bld_base,
                               boolean is_vindex_indirect,
                               LLVMValueRef vertex_index,
                               boolean is_aindex_indirect,
                               LLVMValueRef attrib_index,
                               LLVMValueRef swizzle_index);
   void (*emit_vertex)(const struct lp_build_tgsi_gs_iface *gs_iface,
                       struct lp_build_tgsi_context * bld_base,
                       LLVMValueRef (*outputs)[4],
                       LLVMValueRef emitted_vertices_vec);
   void (*end_primitive)(const struct lp_build_tgsi_gs_iface *gs_iface,
                         struct lp_build_tgsi_context * bld_base,
                         LLVMValueRef verts_per_prim_vec,
                         LLVMValueRef emitted_prims_vec);
   void (*gs_epilogue)(const struct lp_build_tgsi_gs_iface *gs_iface,
                       struct lp_build_tgsi_context * bld_base,
                       LLVMValueRef total_emitted_vertices_vec,
                       LLVMValueRef emitted_prims_vec);
};

struct lp_build_tgsi_soa_context
{
   struct lp_build_tgsi_context bld_base;

   /* Builder for scalar elements of shader's data type (float) */
   struct lp_build_context elem_bld;

   const struct lp_build_tgsi_gs_iface *gs_iface;
   LLVMValueRef emitted_prims_vec_ptr;
   LLVMValueRef total_emitted_vertices_vec_ptr;
   LLVMValueRef emitted_vertices_vec_ptr;
   LLVMValueRef max_output_vertices_vec;

   LLVMValueRef consts_ptr;
   LLVMValueRef const_sizes_ptr;
   const LLVMValueRef (*inputs)[TGSI_NUM_CHANNELS];
   LLVMValueRef (*outputs)[TGSI_NUM_CHANNELS];

   const struct lp_build_sampler_soa *sampler;

   struct tgsi_declaration_sampler_view sv[PIPE_MAX_SHADER_SAMPLER_VIEWS];

   LLVMValueRef immediates[LP_MAX_INLINED_IMMEDIATES][TGSI_NUM_CHANNELS];
   LLVMValueRef temps[LP_MAX_INLINED_TEMPS][TGSI_NUM_CHANNELS];
   LLVMValueRef addr[LP_MAX_TGSI_ADDRS][TGSI_NUM_CHANNELS];
   LLVMValueRef preds[LP_MAX_TGSI_PREDS][TGSI_NUM_CHANNELS];

   /* We allocate/use this array of temps if (1 << TGSI_FILE_TEMPORARY) is
    * set in the indirect_files field.
    * The temps[] array above is unused then.
    */
   LLVMValueRef temps_array;

   /* We allocate/use this array of output if (1 << TGSI_FILE_OUTPUT) is
    * set in the indirect_files field.
    * The outputs[] array above is unused then.
    */
   LLVMValueRef outputs_array;

   /* We allocate/use this array of inputs if (1 << TGSI_FILE_INPUT) is
    * set in the indirect_files field.
    * The inputs[] array above is unused then.
    */
   LLVMValueRef inputs_array;

   /* We allocate/use this array of temps if (1 << TGSI_FILE_IMMEDIATE) is
    * set in the indirect_files field.
    */
   LLVMValueRef imms_array;


   struct lp_bld_tgsi_system_values system_values;

   /** bitmask indicating which register files are accessed indirectly */
   unsigned indirect_files;

   struct lp_build_mask_context *mask;
   struct lp_exec_mask exec_mask;

   uint num_immediates;
   boolean use_immediates_array;
};

void
lp_emit_declaration_soa(
   struct lp_build_tgsi_context *bld,
   const struct tgsi_full_declaration *decl);

void lp_emit_immediate_soa(
   struct lp_build_tgsi_context *bld_base,
   const struct tgsi_full_immediate *imm);

boolean
lp_emit_instruction_soa(
   struct lp_build_tgsi_soa_context *bld,
   const struct tgsi_full_instruction *inst,
   const struct tgsi_opcode_info *info);


LLVMValueRef
lp_get_temp_ptr_soa(
   struct lp_build_tgsi_soa_context *bld,
   unsigned index,
   unsigned chan);

LLVMValueRef
lp_get_output_ptr(
   struct lp_build_tgsi_soa_context *bld,
   unsigned index,
   unsigned chan);

struct lp_build_tgsi_aos_context
{
   struct lp_build_tgsi_context bld_base;

   /* Builder for integer masks and indices */
   struct lp_build_context int_bld;

   /*
    * AoS swizzle used:
    * - swizzles[0] = red index
    * - swizzles[1] = green index
    * - swizzles[2] = blue index
    * - swizzles[3] = alpha index
    */
   unsigned char swizzles[4];
   unsigned char inv_swizzles[4];

   LLVMValueRef consts_ptr;
   const LLVMValueRef *inputs;
   LLVMValueRef *outputs;

   struct lp_build_sampler_aos *sampler;

   LLVMValueRef immediates[LP_MAX_INLINED_IMMEDIATES];
   LLVMValueRef temps[LP_MAX_INLINED_TEMPS];
   LLVMValueRef addr[LP_MAX_TGSI_ADDRS];
   LLVMValueRef preds[LP_MAX_TGSI_PREDS];

   /* We allocate/use this array of temps if (1 << TGSI_FILE_TEMPORARY) is
    * set in the indirect_files field.
    * The temps[] array above is unused then.
    */
   LLVMValueRef temps_array;

   /** bitmask indicating which register files are accessed indirectly */
   unsigned indirect_files;

};

static INLINE struct lp_build_tgsi_soa_context *
lp_soa_context(struct lp_build_tgsi_context *bld_base)
{
   return (struct lp_build_tgsi_soa_context *)bld_base;
}

static INLINE struct lp_build_tgsi_aos_context *
lp_aos_context(struct lp_build_tgsi_context *bld_base)
{
   return (struct lp_build_tgsi_aos_context *)bld_base;
}

void
lp_emit_declaration_aos(
   struct lp_build_tgsi_aos_context *bld,
   const struct tgsi_full_declaration *decl);


boolean
lp_emit_instruction_aos(
   struct lp_build_tgsi_aos_context *bld,
   const struct tgsi_full_instruction *inst,
   const struct tgsi_opcode_info *info,
   int *pc);

void
lp_emit_store_aos(
   struct lp_build_tgsi_aos_context *bld,
   const struct tgsi_full_instruction *inst,
   unsigned index,
   LLVMValueRef value);

void lp_build_fetch_args(
   struct lp_build_tgsi_context * bld_base,
   struct lp_build_emit_data * emit_data);

LLVMValueRef
lp_build_tgsi_inst_llvm_aos(
   struct lp_build_tgsi_context * bld_base,
   const struct tgsi_full_instruction *inst);

void
lp_build_tgsi_intrinsic(
 const struct lp_build_tgsi_action * action,
 struct lp_build_tgsi_context * bld_base,
 struct lp_build_emit_data * emit_data);

LLVMValueRef
lp_build_emit_llvm(
   struct lp_build_tgsi_context *bld_base,
   unsigned tgsi_opcode,
   struct lp_build_emit_data * emit_data);

LLVMValueRef
lp_build_emit_llvm_unary(
   struct lp_build_tgsi_context *bld_base,
   unsigned tgsi_opcode,
   LLVMValueRef arg0);

LLVMValueRef
lp_build_emit_llvm_binary(
   struct lp_build_tgsi_context *bld_base,
   unsigned tgsi_opcode,
   LLVMValueRef arg0,
   LLVMValueRef arg1);

LLVMValueRef
lp_build_emit_llvm_ternary(
   struct lp_build_tgsi_context *bld_base,
   unsigned tgsi_opcode,
   LLVMValueRef arg0,
   LLVMValueRef arg1,
   LLVMValueRef arg2);

boolean
lp_build_tgsi_inst_llvm(
   struct lp_build_tgsi_context * bld_base,
   const struct tgsi_full_instruction *inst);

LLVMValueRef
lp_build_emit_fetch(
   struct lp_build_tgsi_context *bld_base,
   const struct tgsi_full_instruction *inst,
   unsigned src_op,
   const unsigned chan_index);


LLVMValueRef
lp_build_emit_fetch_texoffset(
   struct lp_build_tgsi_context *bld_base,
   const struct tgsi_full_instruction *inst,
   unsigned tex_off_op,
   const unsigned chan_index);

boolean
lp_build_tgsi_llvm(
   struct lp_build_tgsi_context * bld_base,
   const struct tgsi_token *tokens);

#endif /* LP_BLD_TGSI_H */
