1/* -*- mesa-c++  -*-
2 *
3 * Copyright (c) 2021 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27#include "sfn_virtualvalues.h"
28#include "sfn_alu_defines.h"
29#include "sfn_valuefactory.h"
30#include "sfn_instr.h"
31#include "sfn_debug.h"
32
33#include "util/macros.h"
34
35#include <ostream>
36#include <iostream>
37#include <iomanip>
38#include <limits>
39#include <sstream>
40
41namespace r600 {
42
43std::ostream& operator << (std::ostream& os, Pin pin)
44{
45#define PRINT_PIN(X) case pin_ ## X : os << #X; break
46   switch (pin) {
47   PRINT_PIN(chan);
48   PRINT_PIN(array);
49   PRINT_PIN(fully);
50   PRINT_PIN(group);
51   PRINT_PIN(chgr);
52   PRINT_PIN(free);
53   case pin_none:
54   default:
55      ;
56   }
57#undef PRINT_PIN
58   return os;
59}
60
61VirtualValue::VirtualValue(int sel, int chan, Pin pin):
62   m_sel(sel), m_chan(chan), m_pins(pin)
63{
64#if __cpp_exceptions >= 199711L
65   ASSERT_OR_THROW(m_sel < virtual_register_base || pin != pin_fully, "Register is virtual but pinned to sel");
66#endif
67}
68
69bool VirtualValue::ready(int block, int index) const
70{
71   (void)block;
72   (void)index;
73   return true;
74}
75
76bool VirtualValue::is_virtual() const
77{
78   return m_sel >= virtual_register_base;
79}
80
81class ValueComparer: public ConstRegisterVisitor {
82public:
83   ValueComparer();
84   ValueComparer(const Register *value);
85   ValueComparer(const LocalArray *value);
86   ValueComparer(const LocalArrayValue *value);
87   ValueComparer(const UniformValue *value);
88   ValueComparer(const LiteralConstant *value);
89   ValueComparer(const InlineConstant *value);
90
91   void visit(const Register& other) override;
92   void visit(const LocalArray& other) override;
93   void visit(const LocalArrayValue& other) override;
94   void visit(const UniformValue& value) override;
95   void visit(const LiteralConstant& other) override;
96   void visit(const InlineConstant& other) override;
97
98   bool m_result;
99private:
100   const Register *m_register;
101   const LocalArray *m_array;
102   const LocalArrayValue *m_array_value;
103   const UniformValue *m_uniform_value;
104   const LiteralConstant *m_literal_value;
105   const InlineConstant *m_inline_constant;
106};
107
108class ValueCompareCreater: public ConstRegisterVisitor {
109public:
110   void visit(const Register& value) { compare = ValueComparer(&value);}
111   void visit(const LocalArray& value) {compare = ValueComparer(&value);}
112   void visit(const LocalArrayValue& value) {compare = ValueComparer(&value);}
113   void visit(const UniformValue& value) {compare = ValueComparer(&value);}
114   void visit(const LiteralConstant& value) {compare = ValueComparer(&value);}
115   void visit(const InlineConstant& value) {compare = ValueComparer(&value);}
116
117   ValueComparer compare;
118};
119
120VirtualValue::Pointer VirtualValue::from_string(const std::string& s)
121{
122   switch (s[0]) {
123   case 'S':
124   case 'R': return Register::from_string(s);
125   case 'L': return LiteralConstant::from_string(s);
126   case 'K': return UniformValue::from_string(s);
127   case 'P': return InlineConstant::param_from_string(s);
128   case 'I': return InlineConstant::from_string(s);
129
130   default:
131      std::cerr << "'" << s << "'";
132      unreachable("Unknown register type");
133   }
134}
135
136bool VirtualValue::equal_to(const VirtualValue& other) const
137{
138   bool result = m_sel == other.m_sel &&
139         m_chan == other.m_chan &&
140         m_pins == other.m_pins;
141
142   if (result) {
143      ValueCompareCreater comp_creater;
144      accept(comp_creater);
145      other.accept(comp_creater.compare);
146      result &= comp_creater.compare.m_result;
147   }
148
149   return result;
150}
151
152VirtualValue::Pointer VirtualValue::get_addr() const
153{
154   class GetAddressRegister: public ConstRegisterVisitor {
155   public:
156      void visit(const VirtualValue& value) {(void)value;}
157      void visit(const Register& value) {(void)value;};
158      void visit(const LocalArray& value) {(void)value;}
159      void visit(const LocalArrayValue& value) {m_result = value.addr();}
160      void visit(const UniformValue& value) {(void)value;}
161      void visit(const LiteralConstant& value) {(void)value;}
162      void visit(const InlineConstant& value) {(void)value;}
163
164      GetAddressRegister() : m_result(nullptr) {}
165
166      PVirtualValue m_result;
167   };
168   GetAddressRegister get_addr;
169   accept(get_addr);
170   return get_addr.m_result;
171}
172
173Register::Register(int sel, int chan, Pin pin):
174   VirtualValue(sel, chan, pin)
175{
176}
177
178void Register::add_parent(Instr *instr)
179{
180   m_parents.insert(instr);
181   instr->add_use();
182   add_parent_to_array(instr);
183}
184
185void Register::add_parent_to_array(Instr *instr)
186{
187   (void)instr;
188}
189
190void Register::del_parent(Instr *instr)
191{
192   m_parents.erase(instr);
193   instr->dec_use();
194   del_parent_from_array(instr);
195}
196
197void Register::del_parent_from_array(Instr *instr)
198{
199   (void)instr;
200}
201
202
203void Register::add_use(Instr *instr)
204{
205   const auto& [itr, inserted] = m_uses.insert(instr);  {}
206
207   if (inserted) {
208      for (auto& p: m_parents)
209         p->add_use();
210   }
211}
212
213void Register::del_use(Instr *instr)
214{
215   sfn_log << SfnLog::opt << "Del use of " << *this << " in " << *instr << "\n";
216   if (m_uses.find(instr) != m_uses.end()) {
217      m_uses.erase(instr);
218      if (is_ssa())
219         for (auto& p: m_parents)
220            p->dec_use();
221   }
222}
223
224bool Register::ready(int block, int index) const
225{
226   for (auto p : m_parents) {
227      if (p->block_id() <= block) {
228         if (p->index() < index && !p->is_scheduled()) {
229            return false;
230         }
231      }
232   }
233   return true;
234}
235
236void Register::accept(RegisterVisitor& visitor)
237{
238   visitor.visit(*this);
239}
240
241void Register::accept(ConstRegisterVisitor& visitor) const
242{
243   visitor.visit(*this);
244}
245
246
247void Register::pin_live_range(bool start, bool end)
248{
249   m_pin_start = start;
250   m_pin_end = end;
251}
252
253void Register::set_is_ssa(bool value)
254{
255   m_is_ssa = value;
256}
257
258void Register::print(std::ostream& os) const
259{
260   os << (m_is_ssa ? "S" : "R") << sel() << "." << chanchar[chan()];
261
262   if (pin() !=  pin_none)
263      os << "@" << pin();
264}
265
266Register::Pointer Register::from_string(const std::string &s)
267{
268   std::string numstr;
269   char chan = 0;
270   std::string pinstr;
271
272   assert(s[0] == 'R' || s[0] == '_' || s[0] == 'S' );
273
274   int type = 0;
275   for (unsigned i = 1; i < s.length(); ++i) {
276      if (s[i] == '.') {
277         type = 1;
278         continue;
279      } else if (s[i] == '@') {
280         type = 2;
281         continue;
282      }
283
284      switch (type) {
285      case 0: numstr.append(1, s[i]); break;
286      case 1: chan = s[i]; break;
287      case 2: pinstr.append(1, s[i]); break;
288      default:
289         unreachable("Malformed register string");
290      }
291   }
292
293   int sel;
294   if (s[0] != '_') {
295      std::istringstream n(numstr);
296      n >> sel;
297   } else {
298      sel = std::numeric_limits<int>::max();
299   }
300
301   auto p = pin_none;
302   if (pinstr == "chan")
303      p = pin_chan;
304   else if (pinstr == "array")
305      p = pin_array;
306   else if (pinstr == "fully")
307      p = pin_fully;
308   else if (pinstr == "group")
309      p = pin_group;
310   else if (pinstr == "chgr")
311      p = pin_chgr;
312   else if (pinstr == "free")
313      p = pin_free;
314
315   switch (chan) {
316   case 'x' : chan = 0; break;
317   case 'y' : chan = 1; break;
318   case 'z' : chan = 2; break;
319   case 'w' : chan = 3; break;
320   case '0' : chan = 4; break;
321   case '1' : chan = 5; break;
322   case '_' : chan = 7; break;
323   }
324
325   auto reg = new Register( sel, chan, p);
326   reg->set_is_ssa(s[0] == 'S');
327   if (p == pin_fully || p == pin_array)
328      reg->pin_live_range(true);
329   return reg;
330}
331
332RegisterVec4::RegisterVec4():
333   m_sel(-1),
334   m_swz({7,7,7,7}),
335   m_values({nullptr, nullptr, nullptr, nullptr})
336{
337}
338
339RegisterVec4::RegisterVec4(int sel, bool is_ssa, const Swizzle& swz, Pin pin):
340   m_sel(sel),
341   m_swz(swz)
342{
343   for (int i = 0; i < 4; ++i) {
344      m_values[i] = new Element( *this, new Register(m_sel, swz[i], pin));
345      m_values[i]->value()->set_is_ssa(is_ssa);
346   }
347}
348
349RegisterVec4::RegisterVec4(const RegisterVec4& orig):
350   m_sel(orig.m_sel),
351   m_swz(orig.m_swz)
352{
353   for (int i = 0; i < 4; ++i)
354      m_values[i] = new Element(*this, orig.m_values[i]->value());
355}
356
357RegisterVec4::RegisterVec4(PRegister x, PRegister y, PRegister z, PRegister w, Pin pin)
358{
359   PRegister dummy = nullptr;
360
361   if (x) {
362      m_sel = x->sel();
363   } else if (y) {
364      m_sel = y->sel();
365   } else if (z) {
366      m_sel = z->sel();
367   } else if (w) {
368      m_sel = w->sel();
369   } else
370      m_sel = 0;
371
372   if (!(x && y && z && w))
373      dummy = new Register (m_sel, 7, pin_none);
374
375   m_values[0] = new Element(*this,  x ? x : dummy);
376   m_values[1] = new Element(*this,  y ? y : dummy);
377   m_values[2] = new Element(*this,  z ? z : dummy);
378   m_values[3] = new Element(*this,  w ? w : dummy);
379
380   for (int i = 0; i < 4; ++i) {
381      if (m_values[0]->value()->pin() == pin_fully) {
382         pin = pin_fully;
383         break;
384      }
385   }
386
387   for (int i = 0; i < 4; ++i) {
388      switch (m_values[i]->value()->pin()) {
389      case pin_none:
390      case pin_free:
391         m_values[i]->value()->set_pin(pin);
392      break;
393      case pin_chan:
394         if (pin == pin_group)
395            m_values[i]->value()->set_pin(pin_chgr);
396      break;
397      default:
398         ;
399      }
400
401      m_swz[i] = m_values[i]->value()->chan();
402      assert(m_values[i]->value()->sel() == m_sel);
403   }
404}
405
406void RegisterVec4::add_use(Instr *instr)
407{
408   for (auto& r: m_values) {
409      if (r->value()->chan() < 4)
410          r->value()->add_use(instr);
411   }
412}
413
414void RegisterVec4::del_use(Instr *instr)
415{
416   for (auto& r: m_values) {
417      r->value()->del_use(instr);
418   }
419}
420
421bool RegisterVec4::has_uses() const
422{
423   for (auto& r: m_values) {
424      if (r->value()->has_uses())
425         return true;
426   }
427   return false;
428}
429
430
431int RegisterVec4::sel() const
432{
433   int comp = 0;
434   while (comp < 4 && m_values[comp]->value()->chan() > 3)
435      ++comp;
436   return comp < 4 ? m_values[comp]->value()->sel() : 0;
437}
438
439bool RegisterVec4::ready(int block_id, int index) const
440{
441   for (int i = 0; i < 4; ++i) {
442      if (m_values[i]->value()->chan() < 4) {
443         if (!m_values[i]->value()->ready(block_id, index))
444            return false;
445      }
446   }
447   return true;
448}
449
450void RegisterVec4::print(std::ostream& os) const
451{
452   os << (m_values[0]->value()->is_ssa() ? 'S' : 'R') << sel() << ".";
453   for (int i = 0; i < 4; ++i)
454      os << VirtualValue::chanchar[m_swz[i]];
455}
456
457bool operator == (const RegisterVec4& lhs, const RegisterVec4& rhs)
458{
459   for (int i = 0; i < 4; ++i) {
460      assert(lhs[i]);
461      assert(rhs[i]);
462      if (!lhs[i]->equal_to(*rhs[i])) {
463         return false;
464      }
465   }
466   return true;
467}
468
469RegisterVec4::Element::Element(const RegisterVec4& parent, int chan):
470   m_parent(parent),
471   m_value(new Register(parent.m_sel, chan, pin_none))
472{
473}
474
475RegisterVec4::Element::Element(const RegisterVec4& parent, PRegister value):
476   m_parent(parent),
477   m_value(value)
478{
479}
480
481LiteralConstant::LiteralConstant(uint32_t value):
482   VirtualValue(ALU_SRC_LITERAL, -1, pin_none),
483   m_value(value)
484{
485}
486
487void LiteralConstant::accept(RegisterVisitor& vistor)
488{
489   vistor.visit(*this);
490}
491
492void LiteralConstant::accept(ConstRegisterVisitor& vistor) const
493{
494   vistor.visit(*this);
495}
496
497void LiteralConstant::print(std::ostream& os) const
498{
499   os << "L[0x" << std::hex << m_value << std::dec << "]";
500}
501
502LiteralConstant::Pointer LiteralConstant::from_string(const std::string& s)
503{
504   if (s[1] != '[')
505      return nullptr;
506
507   std::string numstr;
508   for (unsigned i = 2; i < s.length(); ++i) {
509      if (s[i] == ']')
510         break;
511
512      if (isxdigit(s[i]))
513         numstr.append(1, s[i]);
514      if (s[i] == 'x')
515         continue;
516   }
517
518   std::istringstream n(numstr);
519
520   uint32_t num;
521   n >> std::hex >> num;
522   return new LiteralConstant( num);
523}
524
525
526// Inline constants usually don't care about the channel but
527// ALU_SRC_PV should be pinned, but we only emit these constants
528// very late, and based on the real register they replace
529InlineConstant::InlineConstant(int sel, int chan):
530   VirtualValue(sel, chan, pin_none)
531{
532}
533
534void InlineConstant::accept(RegisterVisitor& vistor)
535{
536   vistor.visit(*this);
537}
538
539void InlineConstant::accept(ConstRegisterVisitor& vistor) const
540{
541   vistor.visit(*this);
542}
543
544void InlineConstant::print(std::ostream& os) const
545{
546   auto ivalue = alu_src_const.find(static_cast<AluInlineConstants>(sel()));
547   if (ivalue != alu_src_const.end()) {
548      os << "I[" << ivalue->second.descr<< "]";
549      if (ivalue->second.use_chan)
550         os << "." << chanchar[chan()];
551   } else if (sel() >= ALU_SRC_PARAM_BASE &&
552              sel() <  ALU_SRC_PARAM_BASE + 32 ) {
553      os << "Param"
554          << sel() - ALU_SRC_PARAM_BASE
555          << "." << chanchar[chan()];
556   } else {
557      unreachable("Unknown inline constant");
558   }
559}
560
561std::map<std::string, std::pair<AluInlineConstants, bool>> InlineConstant::s_opmap;
562
563InlineConstant::Pointer InlineConstant::from_string(const std::string& s)
564{
565   std::string namestr;
566   char chan = 0;
567
568   ASSERT_OR_THROW(s[1] == '[', "inline const not started with '['");
569
570   unsigned i = 2;
571   while (i < s.length()) {
572      if (s[i] == ']')
573         break;
574      namestr.append(1, s[i]);
575      ++i;
576   }
577
578   ASSERT_OR_THROW(s[i] == ']', "inline const not closed with ']'");
579
580   auto entry = s_opmap.find(namestr);
581   AluInlineConstants value = ALU_SRC_UNKNOWN;
582   bool use_chan = false;
583
584   if (entry == s_opmap.end())  {
585      for (auto& [opcode, descr] : alu_src_const) {
586         if (namestr == descr.descr) {
587            value = opcode;
588            use_chan = descr.use_chan;
589            s_opmap[namestr] = std::make_pair(opcode, use_chan);
590
591            break;
592         }
593      }
594   } else {
595      value = entry->second.first;
596      use_chan = entry->second.second;
597   }
598
599   ASSERT_OR_THROW(value != ALU_SRC_UNKNOWN, "Unknwon inline constant was given");
600
601   if (use_chan) {
602      ASSERT_OR_THROW(s[i + 1] == '.', "inline const channel not started with '.'");
603      switch (s[i + 2]) {
604      case 'x': chan = 0; break;
605      case 'y': chan = 1; break;
606      case 'z': chan = 2; break;
607      case 'w': chan = 3; break;
608      case '0': chan = 4; break;
609      case '1': chan = 5; break;
610      case '_': chan = 7; break;
611      default:
612         ASSERT_OR_THROW(0, "invalied inline const channel ");
613      }
614   }
615   return new InlineConstant( value, chan);
616}
617
618InlineConstant::Pointer InlineConstant::param_from_string(const std::string& s)
619{
620   assert(s.substr(0, 5) == "Param");
621
622   int param = 0;
623   int i = 5;
624   while (isdigit(s[i])) {
625      param *= 10;
626      param +=  s[i] - '0';
627      ++i;
628   }
629
630   int chan = 7;
631   assert(s[i] == '.');
632   switch (s[i+1]) {
633   case 'x': chan = 0; break;
634   case 'y': chan = 1; break;
635   case 'z': chan = 2; break;
636   case 'w': chan = 3; break;
637   default:
638      unreachable("unsupported channel char");
639   }
640
641   return new InlineConstant( ALU_SRC_PARAM_BASE + param, chan);
642}
643
644UniformValue::UniformValue(int sel, int chan, int kcache_bank):
645   VirtualValue(sel, chan, pin_none),
646   m_kcache_bank(kcache_bank),
647   m_buf_addr(nullptr)
648{
649}
650
651UniformValue::UniformValue(int sel, int chan, PVirtualValue buf_addr):
652   VirtualValue(sel, chan, pin_none),
653   m_kcache_bank(0),
654   m_buf_addr(buf_addr)
655{
656}
657
658void UniformValue::accept(RegisterVisitor& vistor)
659{
660   vistor.visit(*this);
661}
662
663void UniformValue::accept(ConstRegisterVisitor& vistor) const
664{
665   vistor.visit(*this);
666}
667
668PVirtualValue UniformValue::buf_addr() const
669{
670   return m_buf_addr;
671}
672
673void UniformValue::print(std::ostream& os) const
674{
675   os << "KC" << m_kcache_bank;
676   if (m_buf_addr) {
677      os << "[" << *m_buf_addr
678         << "]";
679   }
680   os << "[" << (sel() - 512) << "]." << chanchar[chan()];
681}
682
683bool UniformValue::equal_buf_and_cache(const UniformValue& other) const
684{
685   bool result = m_kcache_bank == other.m_kcache_bank;
686   if (result) {
687      if (m_buf_addr && other.m_buf_addr) {
688         result = m_buf_addr->equal_to(other);
689      } else {
690         result = !m_buf_addr && !other.m_buf_addr;
691      }
692   }
693   return result;
694}
695
696
697UniformValue::Pointer UniformValue::from_string(const std::string& s)
698{
699   assert(s[1] == 'C');
700   std::istringstream is(s.substr(2));
701   int bank;
702   char c;
703   is >> bank;
704   is >> c;
705
706   assert(c == '[');
707
708   int index;
709   is >> index;
710
711   is >> c;
712   assert(c == ']');
713   is >> c;
714   assert(c == '.');
715
716   is >> c;
717   int chan = 0;
718   switch (c) {
719   case 'x': chan = 0; break;
720   case 'y': chan = 1; break;
721   case 'z': chan = 2; break;
722   case 'w': chan = 3; break;
723   default:
724      unreachable("Unknown channle when reading uniform");
725   }
726   return new UniformValue(index + 512, chan, bank);
727}
728
729LocalArray::LocalArray(int base_sel, int nchannels, int size, int frac):
730   Register(base_sel, nchannels, pin_array),
731   m_base_sel(base_sel),
732   m_nchannels(nchannels),
733   m_size(size),
734   m_values(size * nchannels),
735   m_frac(frac)
736{
737   assert(nchannels <= 4);
738   assert(nchannels + frac <= 4);
739
740   sfn_log << SfnLog::reg << "Allocate array A" <<  base_sel << "("
741           << size << ", " << frac << ", " << nchannels << ")\n";
742
743   for (int c = 0; c < nchannels; ++c) {
744      for (unsigned i = 0; i < m_size; ++i) {
745         PRegister reg = new Register( base_sel + i, c + frac, pin_array);
746         m_values[m_size * c + i] = new LocalArrayValue(reg, *this);
747
748         /* Pin the array register on the start, because currently we don't
749          * don't track the first write to an array element as write to all
750          * array elements, and it seems that the one can not just use registers
751          * that are not written to in an array for other purpouses */
752         m_values[m_size * c + i]->pin_live_range(true);
753      }
754   }
755}
756
757void LocalArray::accept(RegisterVisitor& vistor)
758{
759   vistor.visit(*this);
760}
761
762void LocalArray::accept(ConstRegisterVisitor& vistor) const
763{
764   vistor.visit(*this);
765}
766
767void LocalArray::print(std::ostream& os) const
768{
769   os << "A" << m_base_sel << "[0 " << ":" << m_values.size() << "].";
770   for (unsigned i = 0; i < m_nchannels; ++i) {
771      os << chanchar[i];
772   }
773}
774
775
776size_t LocalArray::size() const
777{
778   return m_size;
779}
780
781uint32_t LocalArray::nchannels() const
782{
783   return m_nchannels;
784}
785
786PRegister LocalArray::element(size_t offset, PVirtualValue indirect, uint32_t chan)
787{
788   ASSERT_OR_THROW(offset < m_size, "Array: index out of range");
789   ASSERT_OR_THROW(chan < m_nchannels, "Array: channel out of range");
790
791   sfn_log << SfnLog::reg << "Request element A" << m_base_sel << "["  << offset;
792   if (indirect)
793      sfn_log   << "+" << *indirect;
794   sfn_log << SfnLog::reg << "]\n";
795
796   if (indirect) {
797      class ResolveDirectArrayElement: public ConstRegisterVisitor {
798      public:
799         void visit(const Register& value) {(void) value;};
800         void visit(const LocalArray& value) {(void)value; unreachable("An array can't be used as address");}
801         void visit(const LocalArrayValue& value) {(void) value;}
802         void visit(const UniformValue& value) {(void)value;}
803         void visit(const LiteralConstant& value) {offset = value.value(); is_contant = true;}
804         void visit(const InlineConstant& value) {(void)value;}
805
806         ResolveDirectArrayElement(): offset(0), is_contant(false) {}
807
808         int offset;
809         bool is_contant;
810      } addr;
811
812      // If the address os a literal constant then update the offset
813      // and don't access the value indirectly
814      indirect->accept(addr);
815      if (addr.is_contant) {
816         offset += addr.offset;
817         indirect = nullptr;
818         ASSERT_OR_THROW(offset < m_size, "Array: indirect constant index out of range");
819      }
820   }
821
822   LocalArrayValue *reg = m_values[m_size * chan + offset];
823   if (indirect) {
824      reg = new LocalArrayValue( reg, indirect, *this);
825      m_values_indirect.push_back(reg);
826   }
827
828   sfn_log << SfnLog::reg << "  got " << *reg << "\n";
829   return reg;
830}
831
832bool LocalArray::ready_for_direct(int block, int index, int chan) const
833{
834   if (!Register::ready(block, index))
835      return false;
836
837   /* For direct access to an array value we also have to take indirect
838    * writes on the same channels into account */
839   for (LocalArrayValue *e : m_values_indirect) {
840      if (e->chan() == chan && !e->Register::ready(block, index)) {
841         return false;
842      }
843   }
844
845   return true;
846}
847
848bool LocalArray::ready_for_indirect(int block, int index, int chan) const
849{
850   int offset = (chan - m_frac) * m_size;
851   for (unsigned i = 0; i < m_size; ++i) {
852      if (!m_values[offset + i]->Register::ready(block, index))
853         return false;
854   }
855
856   return ready_for_direct(block, index, chan);
857}
858
859
860LocalArrayValue::LocalArrayValue(PRegister reg, PVirtualValue index,
861                                 LocalArray& array):
862   Register(reg->sel(), reg->chan(), pin_array),
863   m_addr(index),
864   m_array(array)
865{
866}
867
868const Register& LocalArray::operator ()(size_t idx, size_t chan) const
869{
870   return *m_values[m_size  * (chan - m_frac) + idx];
871}
872
873LocalArrayValue::LocalArrayValue(PRegister reg, LocalArray& array):
874   LocalArrayValue(reg, nullptr, array)
875{
876
877}
878
879
880PVirtualValue LocalArrayValue::addr() const
881{
882   return m_addr;
883}
884
885const LocalArray& LocalArrayValue::array() const
886{
887   return m_array;
888}
889
890
891void LocalArrayValue::forward_del_use(Instr *instr)
892{
893   if (m_addr && m_addr->as_register())
894      m_addr->as_register()->del_use(instr);
895}
896
897void LocalArrayValue::forward_add_use(Instr *instr)
898{
899   if (m_addr && m_addr->as_register())
900      m_addr->as_register()->add_use(instr);
901}
902
903void LocalArrayValue::accept(RegisterVisitor& vistor)
904{
905   vistor.visit(*this);
906}
907
908void LocalArrayValue::accept(ConstRegisterVisitor& vistor) const
909{
910   vistor.visit(*this);
911}
912
913void LocalArrayValue::add_parent_to_array(Instr *instr)
914{
915   m_array.add_parent(instr);
916}
917
918void LocalArrayValue::del_parent_from_array(Instr *instr)
919{
920   m_array.del_parent(instr);
921}
922
923void LocalArrayValue::print(std::ostream& os) const
924{
925   int offset = sel() - m_array.sel();
926   os << "A" << m_array.sel() << "[";
927   if ( offset > 0 && m_addr)
928      os << offset << "+" << *m_addr;
929   else if (m_addr)
930      os << *m_addr;
931   else
932      os << offset;
933   os << "]." << chanchar[chan()];
934}
935
936bool LocalArrayValue::ready(int block, int index) const
937{
938   return m_addr ?
939         (m_array.ready_for_indirect(block, index, chan()) && m_addr->ready(block, index)):
940            m_array.ready_for_direct(block, index, chan());
941}
942
943ValueComparer::ValueComparer() :
944   m_result(false),
945   m_register(nullptr),
946   m_array(nullptr),
947   m_array_value(nullptr),
948   m_uniform_value(nullptr),
949   m_literal_value(nullptr),
950   m_inline_constant(nullptr)
951{}
952
953ValueComparer::ValueComparer(const Register *value):
954   m_result(false),
955   m_register(value),
956   m_array(nullptr),
957   m_array_value(nullptr),
958   m_uniform_value(nullptr),
959   m_literal_value(nullptr),
960   m_inline_constant(nullptr)
961{}
962
963ValueComparer::ValueComparer(const LocalArray *value):
964   m_result(false),
965   m_register(nullptr),
966   m_array(value),
967   m_array_value(nullptr),
968   m_uniform_value(nullptr),
969   m_literal_value(nullptr),
970   m_inline_constant(nullptr)
971{}
972
973ValueComparer::ValueComparer(const LocalArrayValue *value):
974   m_result(false),
975   m_register(nullptr),
976   m_array(nullptr),
977   m_array_value(value),
978   m_uniform_value(nullptr),
979   m_literal_value(nullptr),
980   m_inline_constant(nullptr)
981{}
982
983ValueComparer::ValueComparer(const UniformValue *value):
984   m_result(false),
985   m_register(nullptr),
986   m_array(nullptr),
987   m_array_value(nullptr),
988   m_uniform_value(value),
989   m_literal_value(nullptr),
990   m_inline_constant(nullptr)
991{}
992
993ValueComparer::ValueComparer(const LiteralConstant *value):
994   m_result(false),
995   m_register(nullptr),
996   m_array(nullptr),
997   m_array_value(nullptr),
998   m_uniform_value(nullptr),
999   m_literal_value(value),
1000   m_inline_constant(nullptr)
1001{}
1002
1003ValueComparer::ValueComparer(const InlineConstant *value):
1004   m_result(false),
1005   m_register(nullptr),
1006   m_array(nullptr),
1007   m_array_value(nullptr),
1008   m_uniform_value(nullptr),
1009   m_literal_value(nullptr),
1010   m_inline_constant(value)
1011{}
1012
1013void ValueComparer::visit(const Register& other)
1014{
1015   (void)other;
1016   m_result = !!m_register;
1017};
1018
1019void ValueComparer::visit(const LocalArray& other)
1020{
1021   m_result = false;
1022   if (m_array) {
1023      m_result = m_array->size() == other.size() &&
1024            m_array->nchannels() == other.nchannels();
1025   }
1026};
1027
1028void ValueComparer::visit(const LocalArrayValue& other)
1029{
1030   m_result = false;
1031   if (m_array_value) {
1032      m_result = m_array_value->array().equal_to(other.array());
1033      if (m_result) {
1034         auto my_addr = m_array_value->addr();
1035         auto other_addr = other.addr();
1036         if (my_addr && other_addr) {
1037            m_result = my_addr->equal_to(*other_addr);
1038         } else {
1039            m_result = !my_addr && !other_addr;
1040         }
1041      }
1042   }
1043};
1044
1045void ValueComparer::visit(const UniformValue& value)
1046{
1047   m_result = false;
1048   if (m_uniform_value) {
1049      m_result = m_uniform_value->kcache_bank() == value.kcache_bank();
1050      if (m_result) {
1051         auto my_buf_addr = m_uniform_value->buf_addr();
1052         auto other_buf_addr = value.buf_addr();
1053         if (my_buf_addr && other_buf_addr) {
1054            m_result = my_buf_addr->equal_to(*other_buf_addr);
1055         } else {
1056            m_result = !my_buf_addr && !other_buf_addr;
1057         }
1058      }
1059   }
1060};
1061
1062void ValueComparer::visit(const LiteralConstant& other)
1063{
1064   m_result = m_literal_value && (m_literal_value->value() == other.value());
1065};
1066
1067void ValueComparer::visit(const InlineConstant& other)
1068{
1069   (void)other;
1070   m_result = !!m_inline_constant;
1071};
1072
1073
1074} // namespace r600
1075