/* -*- mesa-c++  -*-
 *
 * Copyright (c) 2021 Collabora LTD
 *
 * Author: Gert Wollny <gert.wollny@collabora.com>
 *
 * 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
 * on 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
 * THE AUTHOR(S) AND/OR THEIR 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.
 */

#include "sfn_valuefactory.h"

#include "gallium/drivers/r600/r600_shader.h"
#include "sfn_debug.h"
#include "sfn_instr.h"

#include <algorithm>
#include <iostream>
#include <queue>
#include <sstream>

namespace r600 {

using std::istringstream;
using std::string;

ValueFactory::ValueFactory():
    m_next_register_index(VirtualValue::virtual_register_base),
    m_nowrite_idx(0)
{
}

void
ValueFactory::set_virtual_register_base(int base)
{
   m_next_register_index = base;
}

bool
ValueFactory::allocate_registers(const exec_list *registers)
{
   bool has_arrays = false;
   struct array_entry {
      unsigned index;
      unsigned length;
      unsigned ncomponents;

      bool operator()(const array_entry& a, const array_entry& b) const
      {
         return a.length < b.length ||
                (a.length == b.length && a.ncomponents > b.ncomponents);
      }
   };

   using array_list =
      std::priority_queue<array_entry, std::vector<array_entry>, array_entry>;

   array_list arrays;

   foreach_list_typed(nir_register, reg, node, registers)
   {
      if (reg->num_array_elems) {
         array_entry ae = {
            reg->index, reg->num_array_elems, reg->bit_size / 32 * reg->num_components};
         arrays.push(ae);
         has_arrays = true;
      }
   }

   int ncomponents = 0;
   int sel = m_next_register_index;
   unsigned length = 0;

   while (!arrays.empty()) {
      auto a = arrays.top();
      arrays.pop();

      /* This is a bit hackish, return an id that encodes the array merge. To
       * make sure that the mapping doesn't go wrong we have to make sure the
       * arrays is longer than the number of instances in this arrays slot */
      if (a.ncomponents + ncomponents > 4 || a.length > length) {
         sel = m_next_register_index;
         ncomponents = 0;
         length = 0;
      }

      if (ncomponents == 0)
         m_next_register_index += a.length;

      uint32_t frac = ncomponents;
      auto array = new LocalArray(sel, a.ncomponents, a.length, frac);

      for (unsigned i = 0; i < a.ncomponents; ++i) {
         RegisterKey key(a.index, i, vp_array);
         m_channel_counts.inc_count(i);
         m_registers[key] = array;
         sfn_log << SfnLog::reg << __func__ << ": Allocate array " << key << ":" << *array
                 << "\n";
      }

      ncomponents += a.ncomponents;
      length = a.length;
   }

   foreach_list_typed(nir_register, reg, node, registers)
   {
      if (!reg->num_array_elems) {
         uint32_t sel = m_next_register_index++;
         unsigned num_components = reg->num_components * reg->bit_size / 32;
         for (auto chan = 0u; chan < num_components; ++chan) {
            RegisterKey key(reg->index, chan, vp_register);
            m_channel_counts.inc_count(chan);
            m_registers[key] =
               new Register(sel, chan, num_components > 1 ? pin_none : pin_free);
            sfn_log << SfnLog::reg << "allocate register " << key << ":"
                    << *m_registers[key] << "\n";
         }
      }
   }
   return has_arrays;
}

PRegister
ValueFactory::allocate_pinned_register(int sel, int chan)
{
   if (m_next_register_index <= sel)
      m_next_register_index = sel + 1;

   auto reg = new Register(sel, chan, pin_fully);
   m_pinned_registers.push_back(reg);
   return reg;
}

RegisterVec4
ValueFactory::allocate_pinned_vec4(int sel, bool is_ssa)
{
   if (m_next_register_index <= sel)
      m_next_register_index = sel + 1;

   RegisterVec4 retval(sel, is_ssa, {0, 1, 2, 3}, pin_fully);
   for (int i = 0; i < 4; ++i)
      m_pinned_registers.push_back(retval[i]);
   return retval;
}

void
ValueFactory::inject_value(const nir_dest& dest, int chan, PVirtualValue value)
{
   assert(dest.is_ssa);
   RegisterKey key(dest.ssa.index, chan, vp_ssa);
   sfn_log << SfnLog::reg << "Inject value with key " << key << "\n";
   assert(m_values.find(key) == m_values.end());
   m_values[key] = value;
}

PRegister
ValueFactory::dest(const nir_alu_dest& dst, int chan, Pin pin_channel, uint8_t chan_mask)
{
   sfn_log << SfnLog::reg << "Search (ref) " << &dst << "\n";
   return dest(dst.dest, chan, pin_channel, chan_mask);
}

class TranslateRegister : public RegisterVisitor {
public:
   void visit(VirtualValue& value) { (void)value; }
   void visit(Register& value) { (void)value; };
   void visit(LocalArray& value) { m_value = value.element(m_offset, m_addr, m_chan); }
   void visit(LocalArrayValue& value) { (void)value; }
   void visit(UniformValue& value) { (void)value; }
   void visit(LiteralConstant& value) { (void)value; }
   void visit(InlineConstant& value) { (void)value; }

   TranslateRegister(int offset, PVirtualValue addr, int chan):
       m_addr(addr),
       m_value(nullptr),
       m_offset(offset),
       m_chan(chan)
   {
   }

   PVirtualValue m_addr;
   PRegister m_value;
   int m_offset;
   int m_chan;
};

PRegister
ValueFactory::resolve_array(nir_register *reg,
                            nir_src *indirect,
                            int base_offset,
                            int chan)
{
   PVirtualValue addr = nullptr;
   auto type = reg->num_array_elems ? vp_array : vp_register;
   RegisterKey key(reg->index, chan, type);
   auto ireg = m_registers.find(key);
   if (ireg == m_registers.end()) {
      std::cerr << "Key " << key << " not found\n";
      assert(0);
   }

   if (reg->num_array_elems) {

      if (indirect)
         addr = src(*indirect, 0);

      TranslateRegister array_resolution(base_offset, addr, chan);

      ireg->second->accept(array_resolution);
      assert(array_resolution.m_value);
      return array_resolution.m_value;
   } else {
      return ireg->second;
   }
}

PRegister
ValueFactory::dest(const nir_dest& dst, int chan, Pin pin_channel, uint8_t chan_mask)
{
   if (dst.is_ssa) {
      return dest(dst.ssa, chan, pin_channel, chan_mask);
   } else {
      return resolve_array(dst.reg.reg, dst.reg.indirect, dst.reg.base_offset, chan);
   }
}

void
ValueFactory::allocate_const(nir_load_const_instr *load_const)
{
   assert(load_const->def.bit_size == 32);
   for (int i = 0; i < load_const->def.num_components; ++i) {
      RegisterKey key(load_const->def.index, i, vp_ssa);
      m_values[key] = literal(load_const->value[i].i32);
      sfn_log << SfnLog::reg << "Add const with key " << key << " as " << m_values[key]
              << "\n";
   }
}

PVirtualValue
ValueFactory::uniform(nir_intrinsic_instr *load_uniform, int chan)
{
   auto literal = nir_src_as_const_value(load_uniform->src[0]);
   assert(literal);

   int index = nir_intrinsic_base(load_uniform) + +literal->u32 + 512;

   return uniform(index, chan, 0);
}

PVirtualValue
ValueFactory::uniform(uint32_t index, int chan, int kcache)
{
   return new UniformValue(index, chan, kcache);
}

PRegister
ValueFactory::temp_register(int pinned_channel, bool is_ssa)
{
   int sel = m_next_register_index++;
   int chan = (pinned_channel >= 0) ? pinned_channel : m_channel_counts.least_used(0xf);

   auto reg = new Register(sel, chan, pinned_channel >= 0 ? pin_chan : pin_free);
   m_channel_counts.inc_count(chan);

   reg->set_is_ssa(is_ssa);
   m_registers[RegisterKey(sel, chan, vp_temp)] = reg;
   return reg;
}

RegisterVec4
ValueFactory::temp_vec4(Pin pin, const RegisterVec4::Swizzle& swizzle)
{
   int sel = m_next_register_index++;

   if (pin == pin_free)
      pin = pin_chan;

   PRegister vec4[4];

   for (int i = 0; i < 4; ++i) {
      vec4[i] = new Register(sel, swizzle[i], pin);
      vec4[i]->set_is_ssa(true);
      m_registers[RegisterKey(sel, swizzle[i], vp_temp)] = vec4[i];
   }
   return RegisterVec4(vec4[0], vec4[1], vec4[2], vec4[3], pin);
}

RegisterVec4
ValueFactory::dest_vec4(const nir_dest& dst, Pin pin)
{
   if (pin != pin_group && pin != pin_chgr)
      pin = pin_chan;
   if (dst.is_ssa) {
      PRegister x = dest(dst, 0, pin);
      PRegister y = dest(dst, 1, pin);
      PRegister z = dest(dst, 2, pin);
      PRegister w = dest(dst, 3, pin);
      return RegisterVec4(x, y, z, w, pin);
   } else {
      assert(!dst.reg.indirect);
      PRegister v[4];
      int sel = -1;
      for (int i = 0; i < 4; ++i) {
         RegisterKey key(dst.reg.reg->index, i, vp_register);
         v[i] = m_registers[key];
         assert(sel >= 0 || v[i]);
         if (sel < 0)
            sel = v[i]->sel();

         if (!v[i]) {
            v[i] = m_registers[key] = new Register(sel, i, pin_group);
         }
      }
      return RegisterVec4(v[0], v[1], v[2], v[3], pin);
   }
   unreachable("unsupported");
}

PVirtualValue
ValueFactory::src(const nir_alu_src& alu_src, int chan)
{
   return src(alu_src.src, alu_src.swizzle[chan]);
}

PVirtualValue
ValueFactory::src64(const nir_alu_src& alu_src, int chan, int comp)
{
   return src(alu_src.src, 2 * alu_src.swizzle[chan] + comp);
}

PVirtualValue
ValueFactory::src(const nir_src& src, int chan)
{
   sfn_log << SfnLog::reg << "search (ref) " << (void *)&src << "\n";

   if (src.is_ssa) {
      sfn_log << SfnLog::reg << "search ssa " << src.ssa->index << " c:" << chan
              << " got ";
      auto val = ssa_src(*src.ssa, chan);
      sfn_log << *val << "\n";
      return val;
   } else {
      sfn_log << SfnLog::reg << "search reg " << src.reg.reg->index << "\n";
      return local_register(src.reg, chan);
   }
}

PVirtualValue
ValueFactory::src(const nir_tex_src& tex_src, int chan)
{
   return src(tex_src.src, chan);
}

PRegister
ValueFactory::dummy_dest(unsigned chan)
{
   assert(chan < 4);
   return m_dummy_dest_pinned[chan];
}

PRegister
ValueFactory::dest(const nir_ssa_def& ssa, int chan, Pin pin_channel, uint8_t chan_mask)
{
   RegisterKey key(ssa.index, chan, vp_ssa);

   /* dirty workaround for Cayman trans ops, because we may request
    * the same sa reg more than once, but only write to it once.  */
   auto ireg = m_registers.find(key);
   if (ireg != m_registers.end())
      return ireg->second;

   auto isel = m_ssa_index_to_sel.find(ssa.index);
   int sel;
   if (isel != m_ssa_index_to_sel.end())
      sel = isel->second;
   else {
      sel = m_next_register_index++;
      sfn_log << SfnLog::reg << "Assign " << sel << " to index " << ssa.index << " in "
              << &m_ssa_index_to_sel << "\n";
      m_ssa_index_to_sel[ssa.index] = sel;
   }

   if (pin_channel == pin_free)
      chan = m_channel_counts.least_used(chan_mask);

   auto vreg = new Register(sel, chan, pin_channel);
   m_channel_counts.inc_count(chan);
   vreg->set_is_ssa(true);
   m_registers[key] = vreg;
   sfn_log << SfnLog::reg << "allocate Ssa " << key << ":" << *vreg << "\n";
   return vreg;
}

PVirtualValue
ValueFactory::zero()
{
   return inline_const(ALU_SRC_0, 0);
}

PVirtualValue
ValueFactory::one()
{
   return inline_const(ALU_SRC_1, 0);
}

PVirtualValue
ValueFactory::one_i()
{
   return inline_const(ALU_SRC_1_INT, 0);
}

PRegister
ValueFactory::undef(int index, int chan)
{
   RegisterKey key(index, chan, vp_ssa);
   PRegister reg = new Register(m_next_register_index++, 0, pin_free);
   reg->set_is_ssa(true);
   m_registers[key] = reg;
   return reg;
}

PVirtualValue
ValueFactory::ssa_src(const nir_ssa_def& ssa, int chan)
{
   RegisterKey key(ssa.index, chan, vp_ssa);
   sfn_log << SfnLog::reg << "search src with key" << key << "\n";

   auto ireg = m_registers.find(key);
   if (ireg != m_registers.end())
      return ireg->second;

   auto ival = m_values.find(key);
   if (ival != m_values.end())
      return ival->second;

   std::cerr << "Didn't find source with key " << key << "\n";
   unreachable("Source values should always exist");
}

PRegister
ValueFactory::local_register(const nir_reg_dest& dst, int chan)
{
   return resolve_array(dst.reg, dst.indirect, dst.base_offset, chan);
}

PRegister
ValueFactory::local_register(const nir_reg_src& src, int chan)
{
   return resolve_array(src.reg, src.indirect, src.base_offset, chan);
}

PVirtualValue
ValueFactory::literal(uint32_t value)
{
   auto iv = m_literal_values.find(value);
   if (iv != m_literal_values.end())
      return iv->second;

   auto v = new LiteralConstant(value);
   m_literal_values[value] = v;
   return v;
}

PInlineConstant
ValueFactory::inline_const(AluInlineConstants sel, int chan)
{
   int hash = (sel << 3) | chan;
   auto iv = m_inline_constants.find(hash);
   if (iv != m_inline_constants.end())
      return iv->second;
   auto v = new InlineConstant(sel, chan);
   m_inline_constants[hash] = v;
   return v;
}

std::vector<PVirtualValue, Allocator<PVirtualValue>>
ValueFactory::src_vec(const nir_src& source, int components)
{
   std::vector<PVirtualValue, Allocator<PVirtualValue>> retval;
   retval.reserve(components);
   for (int i = 0; i < components; ++i)
      retval.push_back(src(source, i));
   return retval;
}

std::vector<PRegister, Allocator<PRegister>>
ValueFactory::dest_vec(const nir_dest& dst, int num_components)
{
   std::vector<PRegister, Allocator<PRegister>> retval;
   retval.reserve(num_components);
   for (int i = 0; i < num_components; ++i)
      retval.push_back(dest(dst, i, num_components > 1 ? pin_chan : pin_free));
   return retval;
}

RegisterVec4
ValueFactory::src_vec4(const nir_src& source, Pin pin, const RegisterVec4::Swizzle& swz)
{
   auto sx = swz[0] < 4 ? src(source, swz[0])->as_register() : nullptr;
   auto sy = swz[1] < 4 ? src(source, swz[1])->as_register() : nullptr;
   auto sz = swz[2] < 4 ? src(source, swz[2])->as_register() : nullptr;
   auto sw = swz[3] < 4 ? src(source, swz[3])->as_register() : nullptr;

   assert(sx || sy || sz || sw);

   int sel = sx ? sx->sel() : (sy ? sy->sel() : (sz ? sz->sel() : sw ? sw->sel() : -1));
   if (sel < 0)
      unreachable("source vector without valid components");

   if (!sx)
      sx = new Register(sel, 7, pin);
   if (!sy)
      sy = new Register(sel, 7, pin);
   if (!sz)
      sz = new Register(sel, 7, pin);
   if (!sw)
      sw = new Register(sel, 7, pin);

   return RegisterVec4(sx, sy, sz, sw, pin);
}

static Pin
pin_from_string(const std::string& pinstr)
{
   if (pinstr == "chan")
      return pin_chan;
   if (pinstr == "array")
      return pin_array;
   if (pinstr == "fully")
      return pin_fully;
   if (pinstr == "group")
      return pin_group;
   if (pinstr == "chgr")
      return pin_chgr;
   if (pinstr == "free")
      return pin_free;
   return pin_none;
}

static int
chan_from_char(char chan)
{
   switch (chan) {
   case 'x':
      return 0;
   case 'y':
      return 1;
   case 'z':
      return 2;
   case 'w':
      return 3;
   case '0':
      return 4;
   case '1':
      return 5;
   case '_':
      return 7;
   }
   unreachable("Unknown swizzle char");
}

static int
str_to_int(const string& s)
{
   istringstream ss(s);
   int retval;
   ss >> retval;
   return retval;
}

static bool
split_register_string(const string& s,
                      string& index_str,
                      string& size_str,
                      string& swizzle_str,
                      string& pin_str)
{
   int type = 0;
   for (unsigned i = 1; i < s.length(); ++i) {
      if (s[i] == '.' && type != 3) {
         type = 1;
         continue;
      } else if (s[i] == '@' && type != 3) {
         type = 2;
         continue;
      } else if (s[i] == '[') {
         type = 3;
         continue;
      } else if (s[i] == ']') {
         if (type != 3)
            std::cerr << "s=" << s << ": type=" << type << ": i=" << i << "\n";
         assert(type == 3);

         type = 4;
         continue;
      }

      switch (type) {
      case 0:
         index_str.append(1, s[i]);
         break;
      case 1:
         swizzle_str.append(1, s[i]);
         break;
      case 2:
         pin_str.append(1, s[i]);
         break;
      case 3:
         size_str.append(1, s[i]);
         break;
      default:
         unreachable("Malformed Array allocation string");
      }
   }
   return true;
}

PRegister
ValueFactory::dest_from_string(const std::string& s)
{
   assert(s.length() >= 4);

   assert(strchr("ARS_", s[0]));

   string index_str;
   string size_str;
   string swizzle_str;
   string pin_str;

   split_register_string(s, index_str, size_str, swizzle_str, pin_str);

   int sel = 0;
   if (s[0] == '_') {
      /* Since these instructions still may use or switch to a different
       * channel we have to create a new instance for each occurance */
      sel = std::numeric_limits<int>::max() - m_nowrite_idx++;
   } else {
      std::istringstream n(index_str);
      n >> sel;
   }

   auto p = pin_from_string(pin_str);
   char chan = chan_from_char(swizzle_str[0]);

   EValuePool pool = vp_temp;
   switch (s[0]) {
   case 'A':
      pool = vp_array;
      break;
   case 'R':
      pool = vp_register;
      break;
   case '_':
      pool = vp_ignore;
      break;
   case 'S':
      pool = vp_ssa;
      break;
   default:
      unreachable("Unknown value type");
   }

   bool is_ssa = s[0] == 'S';

   RegisterKey key(sel, chan, pool);

   sfn_log << SfnLog::reg << "Search register with key " << key << "\n";

   auto ireg = m_registers.find(key);
   if (ireg == m_registers.end()) {
      auto reg = new Register(sel, chan, p);
      reg->set_is_ssa(is_ssa);
      if (p == pin_fully)
         reg->pin_live_range(true);
      m_registers[key] = reg;
      return reg;
   } else if (pool == vp_ignore) {
      assert(ireg->second->sel() == std::numeric_limits<int>::max());
      return ireg->second;
   } else {
      assert(!is_ssa || s[0] == '_');

      if (size_str.length()) {
         auto array = static_cast<LocalArray *>(ireg->second);
         PVirtualValue addr = nullptr;
         int offset = 0;
         if (size_str[0] == 'S' || size_str[0] == 'R') {
            addr = src_from_string(size_str);
         } else {
            istringstream num_str(size_str);
            num_str >> offset;
         }

         return array->element(offset, addr, chan - array->frac());
      } else
         return ireg->second;
   }
}

PVirtualValue
ValueFactory::src_from_string(const std::string& s)
{
   switch (s[0]) {
   case 'A':
   case 'S':
   case 'R':
      break;
   case 'L':
      return LiteralConstant::from_string(s);
   case 'K':
      return UniformValue::from_string(s);
   case 'P':
      return InlineConstant::param_from_string(s);
   case 'I':
      return InlineConstant::from_string(s);

   default:
      std::cerr << "'" << s << "'";
      unreachable("Unknown register type");
   }

   assert(strchr("ARS_", s[0]));

   string index_str;
   string size_str;
   string swizzle_str;
   string pin_str;

   split_register_string(s, index_str, size_str, swizzle_str, pin_str);

   int sel = 0;
   if (s[0] == '_') {
      sel = std::numeric_limits<int>::max();
   } else {
      std::istringstream n(index_str);
      n >> sel;
   }

   auto p = pin_from_string(pin_str);
   char chan = chan_from_char(swizzle_str[0]);

   EValuePool pool = vp_temp;
   switch (s[0]) {
   case 'A':
      pool = vp_array;
      break;
   case 'R':
      pool = vp_register;
      break;
   case '_':
      pool = vp_ignore;
      break;
   case 'S':
      pool = vp_ssa;
      break;
   default:
      unreachable("Unknown value type");
   }

   RegisterKey key(sel, chan, pool);

   auto ireg = m_registers.find(key);
   if (ireg != m_registers.end()) {
      if (pool != vp_ssa && size_str.length()) {
         auto array = static_cast<LocalArray *>(ireg->second);
         PVirtualValue addr = nullptr;
         int offset = 0;
         if (size_str[0] == 'S' || size_str[0] == 'R') {
            addr = src_from_string(size_str);
         } else {
            istringstream num_str(size_str);
            num_str >> offset;
         }
         return array->element(offset, addr, chan - array->frac());
      } else {
         return ireg->second;
      }
   } else {
      if (sel != std::numeric_limits<int>::max()) {
         std::cerr << "register " << key << "not found \n";
         unreachable("Source register should exist");
      } else {
         auto reg = new Register(sel, chan, p);
         m_registers[key] = reg;
         return reg;
      }
   }
}

RegisterVec4
ValueFactory::dest_vec4_from_string(const std::string& s,
                                    RegisterVec4::Swizzle& swz,
                                    Pin pin)
{
   bool is_ssa = false;
   int sel = sel_and_szw_from_string(s, swz, is_ssa);

   PRegister v[4];

   for (int i = 0; i < 4; ++i) {
      auto pool = is_ssa ? vp_ssa : vp_register;
      if (swz[i] > 3)
         pool = vp_ignore;

      RegisterKey key(sel, i, pool);
      auto ireg = m_registers.find(key);
      if (ireg != m_registers.end()) {
         v[i] = ireg->second;
         assert(!is_ssa || pool == vp_ignore);
      } else {
         v[i] = new Register(sel, i, pin);
         v[i]->set_is_ssa(is_ssa);
         m_registers[key] = v[i];
      }
   }
   return RegisterVec4(v[0], v[1], v[2], v[3], pin);
}

RegisterVec4
ValueFactory::src_vec4_from_string(const std::string& s)
{
   RegisterVec4::Swizzle swz;
   bool is_ssa = false;
   int sel = sel_and_szw_from_string(s, swz, is_ssa);

   PRegister v[4];

   PRegister used_reg = nullptr;
   for (int i = 0; i < 4; ++i) {
      if (swz[i] < 4) {
         RegisterKey key(sel, swz[i], is_ssa ? vp_ssa : vp_register);
         auto ireg = m_registers.find(key);
         if (ireg == m_registers.end()) {
            std::cerr << s << ": Register with key " << key << " not found\n";
            assert(0);
         }
         used_reg = v[i] = ireg->second;
      } else {
         v[i] = nullptr;
      }
   }
   sel = used_reg ? used_reg->sel() : 0;
   Pin pin = used_reg ? used_reg->pin() : pin_group;

   for (int i = 0; i < 4; ++i) {
      if (!v[i]) {
         v[i] = new Register(sel, swz[i], pin);
         v[i]->set_is_ssa(is_ssa);
      } else {
         if (v[i]->pin() == pin_none)
            v[i]->set_pin(pin_group);
      }
   }
   return RegisterVec4(v[0], v[1], v[2], v[3], pin);
}

LocalArray *
ValueFactory::array_from_string(const std::string& s)
{
   assert(s[0] == 'A');
   string index_str;
   string size_str;
   string swizzle_str;
   string pin_str;

   int type = 0;
   for (unsigned i = 1; i < s.length(); ++i) {
      if (s[i] == '.') {
         type = 1;
         continue;
      } else if (s[i] == '@') {
         type = 2;
         continue;
      } else if (s[i] == '[') {
         type = 3;
         continue;
      } else if (s[i] == ']') {
         assert(type == 3);
         type = 4;
         continue;
      }

      switch (type) {
      case 0:
         index_str.append(1, s[i]);
         break;
      case 1:
         swizzle_str.append(1, s[i]);
         break;
      case 2:
         pin_str.append(1, s[i]);
         break;
      case 3:
         size_str.append(1, s[i]);
         break;
      default:
         unreachable("Malformed Array allocation string");
      }
   }

   int sel = str_to_int(index_str);
   int size = str_to_int(size_str);
   int ncomp = swizzle_str.length();

   if (ncomp > 4 || ncomp <= 0) {
      std::cerr << "Error reading array from '" << s << ": ";
      std::cerr << "index:'" << index_str << "' -> '" << sel << "' size:'" << size_str
                << "' -> '" << size << " swizzle:'" << swizzle_str << "' -> '" << ncomp
                << "'\n";
      assert(0);
   }

   const char *swz = "xyzw";
   const char *first_swz = strchr(swz, swizzle_str[0]);
   long frac = first_swz - swz;
   assert(frac >= 0 && frac <= 4 - ncomp);

   auto array = new LocalArray(sel, ncomp, size, frac);

   for (int i = 0; i < ncomp; ++i) {
      RegisterKey key(sel, i + frac, vp_array);
      m_registers[key] = array;
   }
   return array;
}

void
LiveRangeMap::append_register(Register *reg)
{
   sfn_log << SfnLog::merge << __func__ << ": " << *reg << "\n";

   auto chan = reg->chan();
   auto& ranges = m_life_ranges[chan];

   LiveRangeEntry entry(reg);
   ranges.emplace_back(entry);
}

std::array<size_t, 4>
LiveRangeMap::sizes() const
{
   std::array<size_t, 4> result;
   std::transform(m_life_ranges.begin(),
                  m_life_ranges.end(),
                  result.begin(),
                  [](auto lr) { return lr.size(); });
   return result;
}

LiveRangeMap
ValueFactory::prepare_live_range_map()
{
   LiveRangeMap result;

   for (auto [key, reg] : m_registers) {
      if (key.value.pool == vp_ignore)
         continue;

      if (key.value.pool == vp_array) {
         if (key.value.chan == 0) {
            auto array = static_cast<LocalArray *>(reg);
            for (auto& a : *array) {
               result.append_register(a);
            }
         }
      } else {
         if (reg->chan() < 4)
            result.append_register(reg);
      }
   }

   for (auto r : m_pinned_registers) {
      result.append_register(r);
   }

   for (int i = 0; i < 4; ++i) {
      auto& comp = result.component(i);
      std::sort(comp.begin(),
                comp.end(),
                [](const LiveRangeEntry& lhs, const LiveRangeEntry& rhs) {
                   return lhs.m_register->sel() < rhs.m_register->sel();
                });
      for (size_t j = 0; j < comp.size(); ++j)
         comp[j].m_register->set_index(j);
   }

   return result;
}

void
ValueFactory::clear_pins()
{
   for (auto [key, reg] : m_registers)
      reg->set_pin(pin_none);

   for (auto reg : m_pinned_registers)
      reg->set_pin(pin_none);
}

void
ValueFactory::clear()
{
   m_registers.clear();
   m_values.clear();
   m_literal_values.clear();
   m_inline_constants.clear();
   m_ssa_index_to_sel.clear();
}

void
ValueFactory::get_shader_info(r600_shader *sh_info)
{
   std::set<LocalArray *> arrays;

   for (auto& [key, reg] : m_registers) {
      if (key.value.pool == vp_array)
         arrays.insert(static_cast<LocalArray *>(reg));
   }

   if (!arrays.empty()) {

      sh_info->num_arrays = arrays.size();
      sh_info->arrays = new r600_shader_array[arrays.size()];

      for (auto& arr : arrays) {
         sh_info->arrays->gpr_start = arr->sel();
         sh_info->arrays->gpr_count = arr->size();
         sh_info->arrays->comp_mask = ((1 << arr->nchannels()) - 1) << arr->frac();
      }
      sh_info->indirect_files |= 1 << TGSI_FILE_TEMPORARY;
   }
}

} // namespace r600
