/*
 * Copyright © Microsoft Corporation
 *
 * 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, sublicense,
 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS 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.
 */

 /*
  * See the DirectX Shader Compiler for documentation for DXIL details:
  * https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/DXIL.rst
  */

#ifndef DXIL_MODULE_H
#define DXIL_MODULE_H

#ifdef __cplusplus
extern "C" {
#endif

#include "dxil_buffer.h"
#include "dxil_signature.h"

#include "util/list.h"


#define DXIL_SHADER_MAX_IO_ROWS 128

enum dxil_shader_kind {
   DXIL_PIXEL_SHADER = 0,
   DXIL_VERTEX_SHADER = 1,
   DXIL_GEOMETRY_SHADER = 2,
   DXIL_HULL_SHADER = 3,
   DXIL_DOMAIN_SHADER = 4,
   DXIL_COMPUTE_SHADER = 5,
};

extern int debug_dxil;

enum dxil_debug_flags {
   DXIL_DEBUG_VERBOSE    = 1 << 0,
   DXIL_DEBUG_DUMP_BLOB  = 1 << 1,
   DXIL_DEBUG_TRACE      = 1 << 2,
   DXIL_DEBUG_DUMP_MODULE = 1 << 3,
};

enum dxil_bin_opcode {
   DXIL_BINOP_ADD = 0,
   DXIL_BINOP_SUB = 1,
   DXIL_BINOP_MUL = 2,
   DXIL_BINOP_UDIV = 3,
   DXIL_BINOP_SDIV = 4,
   DXIL_BINOP_UREM = 5,
   DXIL_BINOP_SREM = 6,
   DXIL_BINOP_SHL = 7,
   DXIL_BINOP_LSHR = 8,
   DXIL_BINOP_ASHR = 9,
   DXIL_BINOP_AND = 10,
   DXIL_BINOP_OR = 11,
   DXIL_BINOP_XOR = 12,
   DXIL_BINOP_INSTR_COUNT
};

enum dxil_cast_opcode {
   DXIL_CAST_TRUNC = 0,
   DXIL_CAST_ZEXT = 1,
   DXIL_CAST_SEXT = 2,
   DXIL_CAST_FPTOUI = 3,
   DXIL_CAST_FPTOSI = 4,
   DXIL_CAST_UITOFP = 5,
   DXIL_CAST_SITOFP = 6,
   DXIL_CAST_FPTRUNC = 7,
   DXIL_CAST_FPEXT = 8,
   DXIL_CAST_PTRTOINT = 9,
   DXIL_CAST_INTTOPTR = 10,
   DXIL_CAST_BITCAST = 11,
   DXIL_CAST_ADDRSPACECAST = 12,
   DXIL_CAST_INSTR_COUNT
};

enum dxil_cmp_pred {
   DXIL_FCMP_FALSE = 0,
   DXIL_FCMP_OEQ = 1,
   DXIL_FCMP_OGT = 2,
   DXIL_FCMP_OGE = 3,
   DXIL_FCMP_OLT = 4,
   DXIL_FCMP_OLE = 5,
   DXIL_FCMP_ONE = 6,
   DXIL_FCMP_ORD = 7,
   DXIL_FCMP_UNO = 8,
   DXIL_FCMP_UEQ = 9,
   DXIL_FCMP_UGT = 10,
   DXIL_FCMP_UGE = 11,
   DXIL_FCMP_ULT = 12,
   DXIL_FCMP_ULE = 13,
   DXIL_FCMP_UNE = 14,
   DXIL_FCMP_TRUE = 15,
   DXIL_ICMP_EQ = 32,
   DXIL_ICMP_NE = 33,
   DXIL_ICMP_UGT = 34,
   DXIL_ICMP_UGE = 35,
   DXIL_ICMP_ULT = 36,
   DXIL_ICMP_ULE = 37,
   DXIL_ICMP_SGT = 38,
   DXIL_ICMP_SGE = 39,
   DXIL_ICMP_SLT = 40,
   DXIL_ICMP_SLE = 41,
   DXIL_CMP_INSTR_COUNT
};

enum dxil_opt_flags {
  DXIL_UNSAFE_ALGEBRA = (1 << 0),
  DXIL_NO_NANS = (1 << 1),
  DXIL_NO_INFS = (1 << 2),
  DXIL_NO_SIGNED_ZEROS = (1 << 3),
  DXIL_ALLOW_RECIPROCAL = (1 << 4)
};

struct dxil_features {
   uint64_t doubles : 1,
            cs_4x_raw_sb : 1,
            uavs_at_every_stage : 1,
            use_64uavs : 1,
            min_precision : 1,
            dx11_1_double_extensions : 1,
            dx11_1_shader_extensions : 1,
            dx9_comparison_filtering : 1,
            tiled_resources : 1,
            stencil_ref : 1,
            inner_coverage : 1,
            typed_uav_load_additional_formats : 1,
            rovs : 1,
            array_layer_from_vs_or_ds : 1,
            wave_ops : 1,
            int64_ops : 1,
            view_id : 1,
            barycentrics : 1,
            native_low_precision : 1,
            shading_rate : 1,
            raytracing_tier_1_1 : 1,
            sampler_feedback : 1,
            atomic_int64_typed : 1,
            atomic_int64_tgsm : 1,
            derivatives_in_mesh_or_amp : 1,
            resource_descriptor_heap_indexing : 1,
            sampler_descriptor_heap_indexing : 1,
            unnamed : 1,
            atomic_int64_heap_resource : 1,
            advanced_texture_ops : 1,
            writable_msaa : 1,
            sample_cmp_bias_gradient : 1,
            extended_command_info : 1;
};

struct dxil_shader_info {
   unsigned has_out_position:1;
   unsigned has_out_depth:1;
   unsigned has_per_sample_input:1;
};

struct dxil_func_def {
   struct list_head head;
   const struct dxil_func *func;

   struct list_head instr_list;
   int *basic_block_ids; /* maps from "user" ids to LLVM ids */
   size_t num_basic_block_ids;
   unsigned curr_block;
};

struct dxil_module {
   void *ralloc_ctx;
   enum dxil_shader_kind shader_kind;
   unsigned major_version, minor_version;
   unsigned major_validator, minor_validator;
   struct dxil_features feats;
   unsigned raw_and_structured_buffers : 1;
   struct dxil_shader_info info;

   struct dxil_buffer buf;

   /* The number of entries in the arrays below */
   unsigned num_sig_inputs;
   unsigned num_sig_outputs;
   unsigned num_sig_patch_consts;

   /* The number of "vectors" of elements. This is used to determine the sizes
    * of the dependency tables.
    */
   unsigned num_psv_inputs;
   unsigned num_psv_outputs[4];
   unsigned num_psv_patch_consts;

   struct dxil_signature_record inputs[DXIL_SHADER_MAX_IO_ROWS];
   struct dxil_signature_record outputs[DXIL_SHADER_MAX_IO_ROWS];
   struct dxil_signature_record patch_consts[DXIL_SHADER_MAX_IO_ROWS];

   /* This array is indexed using var->data.driver_location, which
    * is not a direct match to IO rows, since a row is a vec4, and
    * variables can occupy less than that, and several vars can
    * be packed in a row. Hence the x4, but I doubt we can end up
    * with more than 80x4 variables in practice. Maybe this array
    * should be allocated dynamically based on on the maximum
    * driver_location across all input vars.
    */
   uint8_t input_mappings[DXIL_SHADER_MAX_IO_ROWS * 4];
   uint8_t output_mappings[DXIL_SHADER_MAX_IO_ROWS * 4];
   uint8_t patch_mappings[DXIL_SHADER_MAX_IO_ROWS * 4];

   struct dxil_psv_signature_element psv_inputs[DXIL_SHADER_MAX_IO_ROWS];
   struct dxil_psv_signature_element psv_outputs[DXIL_SHADER_MAX_IO_ROWS];
   struct dxil_psv_signature_element psv_patch_consts[DXIL_SHADER_MAX_IO_ROWS];

   struct _mesa_string_buffer *sem_string_table;
   struct dxil_psv_sem_index_table sem_index_table;

   /* These tables are a bitmask per input, with one bit per output
    * to indicate whether or not that input contributes to the output.
    * Each input's bitmask size is rounded up to a uint32 (DWORD),
    * so a bitbask for one output component is the same as for 8 output vec4s.
    * Sizes are in number of uint32s.
    * Meaning of each array entry depends on shader stage.
    * GS: [i] = output stream index
    * HS: [0] = control point outputs, [1] = patch constant outputs
    * DS: [0] = control point inputs, [1] = patch constant inputs (only for io table)
    * PS/VS: only 0 is used. */
   uint32_t *serialized_dependency_table;
   uint32_t *viewid_dependency_table[4];
   uint32_t *io_dependency_table[4];
   uint32_t dependency_table_dwords_per_input[4];
   uint32_t io_dependency_table_size[4];
   uint32_t serialized_dependency_table_size;

   struct {
      unsigned abbrev_width;
      intptr_t offset;
   } blocks[16];
   size_t num_blocks;

   struct list_head type_list;
   struct list_head gvar_list;
   struct list_head func_list;
   struct list_head func_def_list;
   struct list_head attr_set_list;
   struct list_head const_list;
   struct list_head mdnode_list;
   struct list_head md_named_node_list;
   const struct dxil_type *void_type;
   const struct dxil_type *int1_type, *int8_type, *int16_type,
                          *int32_type, *int64_type;
   const struct dxil_type *float16_type, *float32_type, *float64_type;

   struct rb_tree *functions;

   struct dxil_func_def *cur_emitting_func;
};

struct dxil_instr;
struct dxil_value;

void
dxil_module_init(struct dxil_module *m, void *ralloc_ctx);

void
dxil_module_release(struct dxil_module *m);

const struct dxil_value *
dxil_add_global_var(struct dxil_module *m, const char *name,
                    const struct dxil_type *type,
                    enum dxil_address_space as, int align,
                    const struct dxil_value *value);

const struct dxil_value *
dxil_add_global_ptr_var(struct dxil_module *m, const char *name,
                        const struct dxil_type *type,
                        enum dxil_address_space as, int align,
                        const struct dxil_value *value);

struct dxil_func_def *
dxil_add_function_def(struct dxil_module *m, const char *name,
                      const struct dxil_type *type, unsigned num_blocks,
                      const char *const *attr_keys, const char *const *attr_values);

const struct dxil_func *
dxil_add_function_decl(struct dxil_module *m, const char *name,
                       const struct dxil_type *type,
                       enum dxil_attr_kind attr);

const struct dxil_type *
dxil_module_get_void_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_int_type(struct dxil_module *m, unsigned bit_size);

const struct dxil_type *
dxil_module_get_float_type(struct dxil_module *m, unsigned bit_size);

const struct dxil_type *
dxil_module_get_pointer_type(struct dxil_module *m,
                             const struct dxil_type *target);

const struct dxil_type *
dxil_get_overload_type(struct dxil_module *mod, enum overload_type overload);

const struct dxil_type *
dxil_module_get_handle_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_cbuf_ret_type(struct dxil_module *mod, enum overload_type overload);

const struct dxil_type *
dxil_module_get_split_double_ret_type(struct dxil_module *mod);

const struct dxil_type *
dxil_module_get_res_type(struct dxil_module *m, enum dxil_resource_kind kind,
                         enum dxil_component_type comp_type, unsigned num_comps,
                         bool readwrite);

const struct dxil_type *
dxil_module_get_resret_type(struct dxil_module *m, enum overload_type overload);

const struct dxil_type *
dxil_module_get_dimret_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_samplepos_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_res_bind_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_res_props_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_fouri32_type(struct dxil_module *m);

const struct dxil_type *
dxil_module_get_struct_type(struct dxil_module *m,
                            const char *name,
                            const struct dxil_type **elem_types,
                            size_t num_elem_types);

const struct dxil_type *
dxil_module_get_array_type(struct dxil_module *m,
                           const struct dxil_type *elem_type,
                           size_t num_elems);

const struct dxil_type *
dxil_module_get_vector_type(struct dxil_module *m,
                            const struct dxil_type *elem_type,
                            size_t num_elems);

const struct dxil_type *
dxil_module_add_function_type(struct dxil_module *m,
                              const struct dxil_type *ret_type,
                              const struct dxil_type **arg_types,
                              size_t num_arg_types);

nir_alu_type
dxil_type_to_nir_type(const struct dxil_type *type);

bool
dxil_value_type_equal_to(const struct dxil_value *value,
                         const struct dxil_type *lhs);

bool
dxil_value_type_bitsize_equal_to(const struct dxil_value *value, unsigned bitsize);

const struct dxil_type *
dxil_value_get_type(const struct dxil_value *value);

const struct dxil_value *
dxil_module_get_int1_const(struct dxil_module *m, bool value);

const struct dxil_value *
dxil_module_get_int8_const(struct dxil_module *m, int8_t value);

const struct dxil_value *
dxil_module_get_int16_const(struct dxil_module *m, int16_t value);

const struct dxil_value *
dxil_module_get_int32_const(struct dxil_module *m, int32_t value);

const struct dxil_value *
dxil_module_get_int64_const(struct dxil_module *m, int64_t value);

const struct dxil_value *
dxil_module_get_int_const(struct dxil_module *m, intmax_t value,
                          unsigned bit_size);

const struct dxil_value *
dxil_module_get_float16_const(struct dxil_module *m, uint16_t);

const struct dxil_value *
dxil_module_get_float_const(struct dxil_module *m, float value);

const struct dxil_value *
dxil_module_get_double_const(struct dxil_module *m, double value);

const struct dxil_value *
dxil_module_get_array_const(struct dxil_module *m, const struct dxil_type *type,
                            const struct dxil_value **values);

const struct dxil_value *
dxil_module_get_vector_const(struct dxil_module *m, const struct dxil_type *type,
                             const struct dxil_value **values);

const struct dxil_value *
dxil_module_get_struct_const(struct dxil_module *m, const struct dxil_type *type,
                             const struct dxil_value **values);

const struct dxil_value *
dxil_module_get_undef(struct dxil_module *m, const struct dxil_type *type);

const struct dxil_value *
dxil_module_get_res_bind_const(struct dxil_module *m,
                               uint32_t lower_bound,
                               uint32_t upper_bound,
                               uint32_t space,
                               uint8_t class);

const struct dxil_value *
dxil_module_get_res_props_const(struct dxil_module *m,
                                enum dxil_resource_class class,
                                const struct dxil_mdnode *mdnode);

const struct dxil_value *
dxil_module_get_srv_res_props_const(struct dxil_module *m,
                                    const nir_tex_instr *tex);

const struct dxil_value *
dxil_module_get_sampler_res_props_const(struct dxil_module *m,
                                        bool is_shadow);

const struct dxil_value *
dxil_module_get_uav_res_props_const(struct dxil_module *m,
                                    nir_intrinsic_instr *intr);

const struct dxil_value *
dxil_module_get_buffer_res_props_const(struct dxil_module *m,
                                       enum dxil_resource_class class,
                                       enum dxil_resource_kind kind);

const struct dxil_mdnode *
dxil_get_metadata_string(struct dxil_module *m, const char *str);

const struct dxil_mdnode *
dxil_get_metadata_value(struct dxil_module *m, const struct dxil_type *type,
                        const struct dxil_value *value);

const struct dxil_mdnode *
dxil_get_metadata_func(struct dxil_module *m, const struct dxil_func *func);

const struct dxil_mdnode *
dxil_get_metadata_int1(struct dxil_module *m, bool value);

const struct dxil_mdnode *
dxil_get_metadata_int8(struct dxil_module *m, int8_t value);

const struct dxil_mdnode *
dxil_get_metadata_int32(struct dxil_module *m, int32_t value);

const struct dxil_mdnode *
dxil_get_metadata_int64(struct dxil_module *m, int64_t value);

const struct dxil_mdnode *
dxil_get_metadata_float32(struct dxil_module *m, float value);

const struct dxil_mdnode *
dxil_get_metadata_node(struct dxil_module *m,
                       const struct dxil_mdnode *subnodes[],
                       size_t num_subnodes);

bool
dxil_add_metadata_named_node(struct dxil_module *m, const char *name,
                             const struct dxil_mdnode *subnodes[],
                             size_t num_subnodes);

const struct dxil_value *
dxil_emit_binop(struct dxil_module *m, enum dxil_bin_opcode opcode,
                const struct dxil_value *op0, const struct dxil_value *op1,
                enum dxil_opt_flags flags);

const struct dxil_value *
dxil_emit_cmp(struct dxil_module *m, enum dxil_cmp_pred pred,
              const struct dxil_value *op0, const struct dxil_value *op1);

const struct dxil_value *
dxil_emit_select(struct dxil_module *m,
                const struct dxil_value *op0,
                const struct dxil_value *op1,
                const struct dxil_value *op2);

const struct dxil_value *
dxil_emit_extractval(struct dxil_module *m, const struct dxil_value *src,
                     const unsigned int index);

const struct dxil_value *
dxil_emit_cast(struct dxil_module *m, enum dxil_cast_opcode opcode,
               const struct dxil_type *type,
               const struct dxil_value *value);

bool
dxil_emit_branch(struct dxil_module *m, const struct dxil_value *cond,
                 unsigned true_block, unsigned false_block);

const struct dxil_value *
dxil_instr_get_return_value(struct dxil_instr *instr);

struct dxil_instr *
dxil_emit_phi(struct dxil_module *m, const struct dxil_type *type);

bool
dxil_phi_add_incoming(struct dxil_instr *instr,
                      const struct dxil_value *incoming_values[],
                      const unsigned incoming_blocks[],
                      size_t num_incoming);

const struct dxil_value *
dxil_emit_call(struct dxil_module *m,
               const struct dxil_func *func,
               const struct dxil_value **args, size_t num_args);

bool
dxil_emit_call_void(struct dxil_module *m,
                    const struct dxil_func *func,
                    const struct dxil_value **args, size_t num_args);

bool
dxil_emit_ret_void(struct dxil_module *m);

const struct dxil_value *
dxil_emit_alloca(struct dxil_module *m, const struct dxil_type *alloc_type,
                 const struct dxil_value *size,
                 unsigned int align);

const struct dxil_value *
dxil_emit_gep_inbounds(struct dxil_module *m,
                       const struct dxil_value **operands,
                       size_t num_operands);

const struct dxil_value *
dxil_emit_load(struct dxil_module *m, const struct dxil_value *ptr,
               unsigned align,
               bool is_volatile);

bool
dxil_emit_store(struct dxil_module *m, const struct dxil_value *value,
                const struct dxil_value *ptr, unsigned align,
                bool is_volatile);

const struct dxil_value *
dxil_emit_cmpxchg(struct dxil_module *m, const struct dxil_value *cmpval,
                  const struct dxil_value *newval,
                  const struct dxil_value *ptr, bool is_volatile,
                  enum dxil_atomic_ordering ordering,
                  enum dxil_sync_scope syncscope);

const struct dxil_value *
dxil_emit_atomicrmw(struct dxil_module *m, const struct dxil_value *value,
                    const struct dxil_value *ptr, enum dxil_rmw_op op,
                    bool is_volatile, enum dxil_atomic_ordering ordering,
                    enum dxil_sync_scope syncscope);

bool
dxil_emit_module(struct dxil_module *m);

#ifdef __cplusplus
}
#endif

#endif
