text: /*
   * 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.
   */
  
  #include "dxil_enums.h"
  #include "dxil_module.h"
  #include "dxil_signature.h"
  
  #include "nir_to_dxil.h"
  #include "glsl_types.h"
  #include "util/u_debug.h"
  
  #include <string.h>
  
  
  struct semantic_info {
     enum dxil_semantic_kind kind;
     char name[64];
     int index;
     enum dxil_prog_sig_comp_type comp_type;
     uint8_t sig_comp_type;
     int32_t start_row;
     int32_t rows;
     uint8_t start_col;
     uint8_t cols;
     uint8_t interpolation;
     const char *sysvalue_name;
  };
  
  
  static bool
  is_depth_output(enum dxil_semantic_kind kind)
  {
     return kind == DXIL_SEM_DEPTH ||
           kind == DXIL_SEM_STENCIL_REF;
  }
  
  static uint8_t
  get_interpolation(nir_variable *var)
  {
     if (unlikely(var->data.centroid)) {
        switch (var->data.interpolation) {
        case INTERP_MODE_NONE: return DXIL_INTERP_LINEAR_CENTROID;
        case INTERP_MODE_FLAT: return DXIL_INTERP_CONSTANT;
        case INTERP_MODE_NOPERSPECTIVE: return DXIL_INTERP_LINEAR_NOPERSPECTIVE_CENTROID;
        case INTERP_MODE_SMOOTH: return DXIL_INTERP_LINEAR_CENTROID;
  
        }
     } else {
        switch (var->data.interpolation) {
        case INTERP_MODE_NONE: return DXIL_INTERP_LINEAR;
        case INTERP_MODE_FLAT: return DXIL_INTERP_CONSTANT;
        case INTERP_MODE_NOPERSPECTIVE: return DXIL_INTERP_LINEAR_NOPERSPECTIVE;
        case INTERP_MODE_SMOOTH: return DXIL_INTERP_LINEAR;
        }
     }
  
     return DXIL_INTERP_LINEAR;
  }
  
  static const char *
  in_sysvalue_name(nir_variable *var)
  {
     switch (var->data.location) {
     case VARYING_SLOT_POS:
        return "POS";
     case VARYING_SLOT_FACE:
        return "FACE";
     default:
        return "NONE";
     }
  }
  
  /*
   * The signatures are written into the stream in two pieces:
   * DxilProgramSignatureElement is a fixes size structure that gets dumped
   * to the stream in order of the registers and each contains an offset
   * to the semantic name string. Then these strings are dumped into the stream.
   */
  static unsigned
  get_additional_semantic_info(nir_variable *var, struct semantic_info *info,
                               unsigned next_row, bool is_gs_shader)
  {
     const struct glsl_type *type = var->type;
  
     info->comp_type =
        dxil_get_prog_sig_comp_type(var->type);
  
     bool is_depth = is_depth_output(info->kind);
     info->sig_comp_type = dxil_get_comp_type(var->type);
  
     info->rows = 1;
     if (info->kind == DXIL_SEM_TARGET) {
        info->start_row = info->index;
     } else if (is_depth || (info->kind == DXIL_SEM_PRIMITIVE_ID && is_gs_shader)) {
        info->start_row = -1;
     } else {
        info->start_row = next_row;
        if (glsl_type_is_array(type) && is_gs_shader)
           type = glsl_without_array(type);
  
        if (glsl_type_is_array(type)) {
           info->rows = glsl_get_aoa_size(type);
           type = glsl_get_array_element(type);
           assert(info->rows);
        }
        next_row += info->rows;
     }
     info->start_col = (uint8_t)var->data.location_frac;
     if (glsl_type_is_array(type))
        type = glsl_get_array_element(type);
     info->cols = (uint8_t)glsl_get_components(type);
  
     return next_row;
  }
  
  typedef void (*semantic_info_proc)(nir_variable *var, struct semantic_info *info);
  
  static void
  get_semantic_vs_in_name(nir_variable *var, struct semantic_info *info)
  {
     const char *name = dxil_vs_attr_index_to_name(var->data.driver_location);
     assert(strlen(name) < 63);
     strcpy(info->name, name);
     info->kind = DXIL_SEM_ARBITRARY;
  }
  
  static void
  get_semantic_sv_name(nir_variable *var, struct semantic_info *info)
  {
     switch (var->data.location) {
     case SYSTEM_VALUE_VERTEX_ID_ZERO_BASE:
        info->kind = DXIL_SEM_VERTEX_ID;
        break;
     case SYSTEM_VALUE_FRONT_FACE:
        info->kind = DXIL_SEM_IS_FRONT_FACE;
        break;
     case SYSTEM_VALUE_INSTANCE_ID:
        info->kind = DXIL_SEM_INSTANCE_ID;
        break;
     case SYSTEM_VALUE_PRIMITIVE_ID:
        info->kind = DXIL_SEM_PRIMITIVE_ID;
        break;
     default:
        unreachable("unsupported system value");
     }
     strncpy(info->name, var->name, ARRAY_SIZE(info->name) - 1);
  }
  
  static void
  get_semantic_ps_outname(nir_variable *var, struct semantic_info *info)
  {
     info->kind = DXIL_SEM_INVALID;
     switch (var->data.location) {
     case FRAG_RESULT_COLOR:
        snprintf(info->name, 64, "%s", "SV_Target");
        info->index = var->data.index;
        info->kind = DXIL_SEM_TARGET;
        break;
     case FRAG_RESULT_DATA0:
     case FRAG_RESULT_DATA1:
     case FRAG_RESULT_DATA2:
     case FRAG_RESULT_DATA3:
     case FRAG_RESULT_DATA4:
     case FRAG_RESULT_DATA5:
     case FRAG_RESULT_DATA6:
     case FRAG_RESULT_DATA7:
        snprintf(info->name, 64, "%s", "SV_Target");
        info->index = var->data.location - FRAG_RESULT_DATA0;
        if (var->data.location == FRAG_RESULT_DATA0 &&
            var->data.index > 0)
           info->index = var->data.index;
        info->kind = DXIL_SEM_TARGET;
        break;
     case FRAG_RESULT_DEPTH:
        snprintf(info->name, 64, "%s", "SV_Depth");
        info->kind = DXIL_SEM_DEPTH;
        break;
     case FRAG_RESULT_STENCIL:
        snprintf(info->name, 64, "%s", "SV_StencilRef");
        info->kind = DXIL_SEM_STENCIL_REF; //??
        break;
     case FRAG_RESULT_SAMPLE_MASK:
        snprintf(info->name, 64, "%s", "SV_Coverage");
        info->kind = DXIL_SEM_COVERAGE; //??
        break;
     default:
        snprintf(info->name, 64, "%s", "UNDEFINED");
        break;
     }
  }
  
  static void
  get_semantic_name(nir_variable *var, struct semantic_info *info,
                    const struct glsl_type *type)
  {
     info->kind = DXIL_SEM_INVALID;
     info->interpolation = get_interpolation(var);
     switch (var->data.location) {
  
     case VARYING_SLOT_POS:
        assert(glsl_get_components(type) == 4);
        snprintf(info->name, 64, "%s", "SV_Position");
        info->kind = DXIL_SEM_POSITION;
        break;
  
      case VARYING_SLOT_FACE:
        assert(glsl_get_components(var->type) == 1);
        snprintf(info->name, 64, "%s", "SV_IsFrontFace");
        info->kind = DXIL_SEM_IS_FRONT_FACE;
        break;
  
     case VARYING_SLOT_PRIMITIVE_ID:
       assert(glsl_get_components(var->type) == 1);
       snprintf(info->name, 64, "%s", "SV_PrimitiveID");
       info->kind = DXIL_SEM_PRIMITIVE_ID;
       break;
  
     case VARYING_SLOT_CLIP_DIST1:
        info->index = 1;
        FALLTHROUGH;
     case VARYING_SLOT_CLIP_DIST0:
        assert(var->data.location == VARYING_SLOT_CLIP_DIST1 || info->index == 0);
        snprintf(info->name, 64, "%s", "SV_ClipDistance");
        info->kind = DXIL_SEM_CLIP_DISTANCE;
        break;
  
     default: {
           int index = var->data.location - VARYING_SLOT_POS;
           const char idx1 = 'A' + (char)(index >> 4);
           const char idx2 = 'A' + (char)(index & 0xf);
           snprintf(info->name, 64, "VARYING%c%c", idx1, idx2);
           info->kind = DXIL_SEM_ARBITRARY;
        }
     }
  }
  
  static void
  get_semantic_in_name(nir_variable *var, struct semantic_info *info)
  {
     get_semantic_name(var, info, var->type);
     info->sysvalue_name = in_sysvalue_name(var);
  }
  
  static void
  get_semantic_gs_in_name(nir_variable *var, struct semantic_info *info)
  {
     /* geometry shader input varyings come as arrays, but we want to use
      * the element type */
     const struct glsl_type *type =
           glsl_type_is_array(var->type) ? glsl_without_array(var->type) : var->type;
  
     get_semantic_name(var, info, type);
     info->sysvalue_name = in_sysvalue_name(var);
  }
  
  
  static enum dxil_prog_sig_semantic
  prog_semantic_from_kind(enum dxil_semantic_kind kind)
  {
     switch (kind) {
     case DXIL_SEM_ARBITRARY: return DXIL_PROG_SEM_UNDEFINED;
     case DXIL_SEM_VERTEX_ID: return DXIL_PROG_SEM_VERTEX_ID;
     case DXIL_SEM_INSTANCE_ID: return DXIL_PROG_SEM_INSTANCE_ID;
     case DXIL_SEM_POSITION: return DXIL_PROG_SEM_POSITION;
     case DXIL_SEM_COVERAGE: return DXIL_PROG_SEM_COVERAGE;
     case DXIL_SEM_INNER_COVERAGE: return DXIL_PROG_SEM_INNER_COVERAGE;
     case DXIL_SEM_PRIMITIVE_ID: return DXIL_PROG_SEM_PRIMITIVE_ID;
     case DXIL_SEM_SAMPLE_INDEX: return DXIL_PROG_SEM_SAMPLE_INDEX;
     case DXIL_SEM_IS_FRONT_FACE: return DXIL_PROG_SEM_IS_FRONTFACE;
     case DXIL_SEM_RENDERTARGET_ARRAY_INDEX: return DXIL_PROG_SEM_RENDERTARGET_ARRAY_INDEX;
     case DXIL_SEM_VIEWPORT_ARRAY_INDEX: return DXIL_PROG_SEM_VIEWPORT_ARRAY_INDEX;
     case DXIL_SEM_CLIP_DISTANCE: return DXIL_PROG_SEM_CLIP_DISTANCE;
     case DXIL_SEM_CULL_DISTANCE: return DXIL_PROG_SEM_CULL_DISTANCE;
     case DXIL_SEM_BARYCENTRICS: return DXIL_PROG_SEM_BARYCENTRICS;
     case DXIL_SEM_SHADING_RATE: return DXIL_PROG_SEM_SHADING_RATE;
     case DXIL_SEM_CULL_PRIMITIVE: return DXIL_PROG_SEM_CULL_PRIMITIVE;
     case DXIL_SEM_TARGET: return DXIL_PROG_SEM_TARGET;
     case DXIL_SEM_DEPTH: return DXIL_PROG_SEM_DEPTH;
     case DXIL_SEM_DEPTH_LE: return DXIL_PROG_SEM_DEPTH_LE;
     case DXIL_SEM_DEPTH_GE: return DXIL_PROG_SEM_DEPTH_GE;
     case DXIL_SEM_STENCIL_REF: return DXIL_PROG_SEM_STENCIL_REF;
     default:
         return DXIL_PROG_SEM_UNDEFINED;
     }
  }
  
  static
  uint32_t
  copy_semantic_name_to_string(struct _mesa_string_buffer *string_out, const char *name)
  {
     /*  copy the semantic name */
     uint32_t retval = string_out->length;
     size_t name_len = strlen(name) + 1;
     _mesa_string_buffer_append_len(string_out, name, name_len);
     return retval;
  }
  
  static
  uint32_t
  append_semantic_index_to_table(struct dxil_psv_sem_index_table *table, uint32_t index,
                                 uint32_t num_rows)
  {
     if (num_rows == 1) {
        for (unsigned i = 0; i < table->size; ++i) {
           if (table->data[i] == index)
              return i;
        }
     }
     uint32_t retval = table->size;
     assert(table->size + num_rows <= 80);
     for (unsigned i = 0; i < num_rows; ++i)
        table->data[table->size++] = index + i;
     return retval;
  }
  
  static const struct dxil_mdnode *
  fill_SV_param_nodes(struct dxil_module *mod, unsigned record_id,
                      struct semantic_info *semantic) {
  
     const struct dxil_mdnode *SV_params_nodes[11];
     /* For this to always work we should use vectorize_io, but for FS out and VS in
      * this is not implemented globally */
     const struct dxil_mdnode *flattened_semantics[256];
  
     for (unsigned i = 0; i < semantic->rows; ++i)
        flattened_semantics[i] = dxil_get_metadata_int32(mod, semantic->index + i);
  
     SV_params_nodes[0] = dxil_get_metadata_int32(mod, (int)record_id); // Unique element ID
     SV_params_nodes[1] = dxil_get_metadata_string(mod, semantic->name); // Element name
     SV_params_nodes[2] = dxil_get_metadata_int8(mod, semantic->sig_comp_type); // Element type
     SV_params_nodes[3] = dxil_get_metadata_int8(mod, (int8_t)semantic->kind); // Effective system value
     SV_params_nodes[4] = dxil_get_metadata_node(mod, flattened_semantics,
                                                 semantic->rows); // Semantic index vector
     SV_params_nodes[5] = dxil_get_metadata_int8(mod, semantic->interpolation); // Interpolation mode
     SV_params_nodes[6] = dxil_get_metadata_int32(mod, semantic->rows); // Number of rows
     SV_params_nodes[7] = dxil_get_metadata_int8(mod, semantic->cols); // Number of columns
     SV_params_nodes[8] = dxil_get_metadata_int32(mod, semantic->start_row); // Element packing start row
     SV_params_nodes[9] = dxil_get_metadata_int8(mod, semantic->start_col); // Element packing start column
     SV_params_nodes[10] = 0; // optional Metadata
  
     return dxil_get_metadata_node(mod, SV_params_nodes, ARRAY_SIZE(SV_params_nodes));
  }
  
  static void
  fill_signature_element(struct dxil_signature_element *elm,
                         struct semantic_info *semantic,
                         unsigned row)
  {
     memset(elm, 0, sizeof(struct dxil_signature_element));
     // elm->stream = 0;
     // elm->semantic_name_offset = 0;  // Offset needs to be filled out when writing
     elm->semantic_index = semantic->index + row;
     elm->system_value = (uint32_t) prog_semantic_from_kind(semantic->kind);
     elm->comp_type = (uint32_t) semantic->comp_type;
     elm->reg = semantic->start_row + row;
  
     assert(semantic->cols + semantic->start_col <= 4);
     elm->mask = (uint8_t) (((1 << semantic->cols) - 1) << semantic->start_col);
     // elm->never_writes_mask = 0;
     elm->min_precision = DXIL_MIN_PREC_DEFAULT;
  }
  
  static bool
  fill_psv_signature_element(struct dxil_psv_signature_element *psv_elm,
                             struct semantic_info *semantic, struct dxil_module *mod)
  {
     memset(psv_elm, 0, sizeof(struct dxil_psv_signature_element));
     psv_elm->rows = semantic->rows;
     if (semantic->start_row >= 0) {
        assert(semantic->start_row < 256);
        psv_elm->start_row = semantic->start_row;
        psv_elm->cols_and_start = (1u << 6) | (semantic->start_col << 4) | semantic->cols;
     } else {
        /* The validation expects that the the start row is not egative
         * and apparently the extra bit in the cols_and_start indicates that the
         * row is meant literally, so don't set it in this case.
         * (Source of information: Comparing with the validation structures
         * created by dxcompiler)
         */
        psv_elm->start_row = 0;
        psv_elm->cols_and_start = (semantic->start_col << 4) | semantic->cols;
     }
     psv_elm->semantic_kind = (uint8_t)semantic->kind;
     psv_elm->component_type = semantic->comp_type; //`??
     psv_elm->interpolation_mode = semantic->interpolation;
     /* to be filled later
       psv_elm->dynamic_mask_and_stream = 0;
     */
     if (semantic->kind == DXIL_SEM_ARBITRARY && strlen(semantic->name)) {
        psv_elm->semantic_name_offset =
              copy_semantic_name_to_string(mod->sem_string_table, semantic->name);
  
        /* TODO: clean up memory */
        if (psv_elm->semantic_name_offset == (uint32_t)-1)
           return false;
     }
  
     psv_elm->semantic_indexes_offset =
           append_semantic_index_to_table(&mod->sem_index_table, semantic->index, semantic->rows);
  
     return true;
  }
  
  static bool
  fill_io_signature(struct dxil_module *mod, int id,
                    struct semantic_info *semantic,
                    const struct dxil_mdnode **io,
                    struct dxil_signature_element *elm,
                    struct dxil_psv_signature_element *psv_elm)
  {
  
     *io = fill_SV_param_nodes(mod, id, semantic);
     for (unsigned i = 0; i < semantic->rows; ++i)
        fill_signature_element(&elm[i], semantic, i);
     return fill_psv_signature_element(psv_elm, semantic, mod);
  }
  
  static unsigned
  get_input_signature_group(struct dxil_module *mod, const struct dxil_mdnode **inputs,
                            unsigned num_inputs,
                            nir_shader *s, nir_variable_mode modes,
                            semantic_info_proc get_semantics, unsigned *row_iter,
                            bool is_gs_shader)
  {
     nir_foreach_variable_with_modes(var, s, modes) {
        struct semantic_info semantic = {0};
        get_semantics(var, &semantic);
        mod->inputs[num_inputs].sysvalue = semantic.sysvalue_name;
        *row_iter = get_additional_semantic_info(var, &semantic, *row_iter, is_gs_shader);
  
        mod->inputs[num_inputs].name = ralloc_strdup(mod->ralloc_ctx,
                                                     semantic.name);
        mod->inputs[num_inputs].num_elements = semantic.rows;
        struct dxil_signature_element *elm = mod->inputs[num_inputs].elements;
        struct dxil_psv_signature_element *psv_elm = &mod->psv_inputs[num_inputs];
  
        if (!fill_io_signature(mod, num_inputs, &semantic,
                               &inputs[num_inputs], elm, psv_elm))
           return 0;
  
        ++num_inputs;
        assert(num_inputs < VARYING_SLOT_MAX);
     }
     return num_inputs;
  }
  
  static const struct dxil_mdnode *
  get_input_signature(struct dxil_module *mod, nir_shader *s)
  {
     if (s->info.stage == MESA_SHADER_KERNEL)
        return NULL;
  
     const struct dxil_mdnode *inputs[VARYING_SLOT_MAX];
     unsigned next_row = 0;
     bool is_gs_shader = s->info.stage == MESA_SHADER_GEOMETRY;
  
     mod->num_sig_inputs = get_input_signature_group(mod, inputs, 0,
                                                     s, nir_var_shader_in,
                                                     s->info.stage == MESA_SHADER_VERTEX ?
                                                        get_semantic_vs_in_name :
                                                        (s->info.stage == MESA_SHADER_GEOMETRY ?
                                                           get_semantic_gs_in_name : get_semantic_in_name),
                                                     &next_row, is_gs_shader);
  
     mod->num_sig_inputs = get_input_signature_group(mod, inputs, mod->num_sig_inputs,
                                                     s, nir_var_system_value,
                                                     get_semantic_sv_name,
                                                     &next_row, is_gs_shader);
  
     if (!mod->num_sig_inputs && !mod->num_sig_inputs)
        return NULL;
  
     mod->num_psv_inputs = next_row;
  
     const struct dxil_mdnode *retval = mod->num_sig_inputs ?
           dxil_get_metadata_node(mod, inputs, mod->num_sig_inputs) : NULL;
  
     return retval;
  }
  
  static const char *out_sysvalue_name(nir_variable *var)
  {
     switch (var->data.location) {
     case VARYING_SLOT_FACE:
        return "FACE";
     case VARYING_SLOT_POS:
        return "POS";
     case VARYING_SLOT_CLIP_DIST0:
     case VARYING_SLOT_CLIP_DIST1:
        return "CLIPDST";
     case VARYING_SLOT_PRIMITIVE_ID:
        return "PRIMID";
     default:
        return "NO";
     }
  }
  
  static const struct dxil_mdnode *
  get_output_signature(struct dxil_module *mod, nir_shader *s)
  {
     const struct dxil_mdnode *outputs[VARYING_SLOT_MAX];
     unsigned num_outputs = 0;
     unsigned next_row = 0;
     nir_foreach_variable_with_modes(var, s, nir_var_shader_out) {
        struct semantic_info semantic = {0};
  
        if (s->info.stage == MESA_SHADER_FRAGMENT) {
           get_semantic_ps_outname(var, &semantic);
           mod->outputs[num_outputs].sysvalue = "TARGET";
        } else {
           get_semantic_name(var, &semantic, var->type);
           mod->outputs[num_outputs].sysvalue = out_sysvalue_name(var);
        }
        next_row = get_additional_semantic_info(var, &semantic, next_row, false);
  
        mod->info.has_out_position |= semantic.kind== DXIL_SEM_POSITION;
        mod->info.has_out_depth |= semantic.kind == DXIL_SEM_DEPTH;
  
        mod->outputs[num_outputs].name = ralloc_strdup(mod->ralloc_ctx,
                                                       semantic.name);
        mod->outputs[num_outputs].num_elements = semantic.rows;
        struct dxil_signature_element *elm = mod->outputs[num_outputs].elements;
  
        struct dxil_psv_signature_element *psv_elm = &mod->psv_outputs[num_outputs];
  
        if (!fill_io_signature(mod, num_outputs, &semantic,
                               &outputs[num_outputs], elm, psv_elm))
           return NULL;
  
        /* This is fishy, logic suggests that the LHS should be 0xf, but from the
         * validation it needs to be 0xff */
        elm->never_writes_mask = 0xff & ~elm->mask;
  
        ++num_outputs;
  
        if (!is_depth_output(semantic.kind))
           ++mod->num_psv_outputs;
  
        assert(num_outputs < ARRAY_SIZE(outputs));
     }
  
     if (!num_outputs)
        return NULL;
  
     const struct dxil_mdnode *retval = dxil_get_metadata_node(mod, outputs, num_outputs);
     mod->num_sig_outputs = num_outputs;
     return retval;
  }
  
  const struct dxil_mdnode *
  get_signatures(struct dxil_module *mod, nir_shader *s)
  {
     /* DXC does the same: Add an empty string before everything else */
     mod->sem_string_table = _mesa_string_buffer_create(mod->ralloc_ctx, 1024);
     copy_semantic_name_to_string(mod->sem_string_table, "");
  
     const struct dxil_mdnode *input_signature = get_input_signature(mod, s);
     const struct dxil_mdnode *output_signature = get_output_signature(mod, s);
  
     const struct dxil_mdnode *SV_nodes[3] = {
        input_signature,
        output_signature,
        NULL
     };
     if (output_signature || input_signature)
        return dxil_get_metadata_node(mod, SV_nodes, ARRAY_SIZE(SV_nodes));
     else
        return NULL;
  }
  