1/*
2 * Copyright © 2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23#include <gtest/gtest.h>
24#include "ir.h"
25#include "ir_array_refcount.h"
26#include "ir_builder.h"
27#include "util/hash_table.h"
28
29using namespace ir_builder;
30
31class array_refcount_test : public ::testing::Test {
32public:
33   virtual void SetUp();
34   virtual void TearDown();
35
36   exec_list instructions;
37   ir_factory *body;
38   void *mem_ctx;
39
40   /**
41    * glsl_type for a vec4[3][4][5].
42    *
43    * The exceptionally verbose name is picked because it matches the syntax
44    * of http://cdecl.org/.
45    */
46   const glsl_type *array_3_of_array_4_of_array_5_of_vec4;
47
48   /**
49    * glsl_type for a int[3].
50    *
51    * The exceptionally verbose name is picked because it matches the syntax
52    * of http://cdecl.org/.
53    */
54   const glsl_type *array_3_of_int;
55
56   /**
57    * Wrapper to access private member "bits" of ir_array_refcount_entry
58    *
59    * The test class is a friend to ir_array_refcount_entry, but the
60    * individual tests are not part of the class.  Since the friendliness of
61    * the test class does not extend to the tests, provide a wrapper.
62    */
63   const BITSET_WORD *get_bits(const ir_array_refcount_entry &entry)
64   {
65      return entry.bits;
66   }
67
68   /**
69    * Wrapper to access private member "num_bits" of ir_array_refcount_entry
70    *
71    * The test class is a friend to ir_array_refcount_entry, but the
72    * individual tests are not part of the class.  Since the friendliness of
73    * the test class does not extend to the tests, provide a wrapper.
74    */
75   unsigned get_num_bits(const ir_array_refcount_entry &entry)
76   {
77      return entry.num_bits;
78   }
79
80   /**
81    * Wrapper to access private member "array_depth" of ir_array_refcount_entry
82    *
83    * The test class is a friend to ir_array_refcount_entry, but the
84    * individual tests are not part of the class.  Since the friendliness of
85    * the test class does not extend to the tests, provide a wrapper.
86    */
87   unsigned get_array_depth(const ir_array_refcount_entry &entry)
88   {
89      return entry.array_depth;
90   }
91};
92
93void
94array_refcount_test::SetUp()
95{
96   mem_ctx = ralloc_context(NULL);
97
98   instructions.make_empty();
99   body = new ir_factory(&instructions, mem_ctx);
100
101   /* The type of vec4 x[3][4][5]; */
102   const glsl_type *const array_5_of_vec4 =
103      glsl_type::get_array_instance(glsl_type::vec4_type, 5);
104   const glsl_type *const array_4_of_array_5_of_vec4 =
105      glsl_type::get_array_instance(array_5_of_vec4, 4);
106   array_3_of_array_4_of_array_5_of_vec4 =
107      glsl_type::get_array_instance(array_4_of_array_5_of_vec4, 3);
108
109   array_3_of_int = glsl_type::get_array_instance(glsl_type::int_type, 3);
110}
111
112void
113array_refcount_test::TearDown()
114{
115   delete body;
116   body = NULL;
117
118   ralloc_free(mem_ctx);
119   mem_ctx = NULL;
120}
121
122static operand
123deref_array(operand array, operand index)
124{
125   void *mem_ctx = ralloc_parent(array.val);
126
127   ir_rvalue *val = new(mem_ctx) ir_dereference_array(array.val, index.val);
128
129   return operand(val);
130}
131
132static operand
133deref_struct(operand s, const char *field)
134{
135   void *mem_ctx = ralloc_parent(s.val);
136
137   ir_rvalue *val = new(mem_ctx) ir_dereference_record(s.val, field);
138
139   return operand(val);
140}
141
142/**
143 * Verify that only the specified set of ir_variables exists in the hash table
144 */
145static void
146validate_variables_in_hash_table(struct hash_table *ht,
147                                 unsigned count,
148                                 ...)
149{
150   ir_variable **vars = new ir_variable *[count];
151   va_list args;
152
153   /* Make a copy of the list of expected ir_variables.  The copied list can
154    * be modified during the checking.
155    */
156   va_start(args, count);
157
158   for (unsigned i = 0; i < count; i++)
159      vars[i] = va_arg(args, ir_variable *);
160
161   va_end(args);
162
163   struct hash_entry *entry;
164   hash_table_foreach(ht, entry) {
165      const ir_instruction *const ir = (ir_instruction *) entry->key;
166      const ir_variable *const v = ir->as_variable();
167
168      if (v == NULL) {
169         ADD_FAILURE() << "Invalid junk in hash table: ir_type = "
170                       << ir->ir_type << ", address = "
171                       << (void *) ir;
172         continue;
173      }
174
175      unsigned i;
176      for (i = 0; i < count; i++) {
177         if (vars[i] == NULL)
178            continue;
179
180         if (vars[i] == v)
181            break;
182      }
183
184      if (i == count) {
185            ADD_FAILURE() << "Invalid variable in hash table: \""
186                          << v->name << "\"";
187      } else {
188         /* As each variable is encountered, remove it from the set.  Don't
189          * bother compacting the set because we don't care about
190          * performance here.
191          */
192         vars[i] = NULL;
193      }
194   }
195
196   /* Check that there's nothing left in the set. */
197   for (unsigned i = 0; i < count; i++) {
198      if (vars[i] != NULL) {
199         ADD_FAILURE() << "Variable was not in the hash table: \""
200                          << vars[i]->name << "\"";
201      }
202   }
203
204   delete [] vars;
205}
206
207TEST_F(array_refcount_test, ir_array_refcount_entry_initial_state_for_scalar)
208{
209   ir_variable *const var =
210      new(mem_ctx) ir_variable(glsl_type::int_type, "a", ir_var_auto);
211
212   ir_array_refcount_entry entry(var);
213
214   ASSERT_NE((void *)0, get_bits(entry));
215   EXPECT_FALSE(entry.is_referenced);
216   EXPECT_EQ(1, get_num_bits(entry));
217   EXPECT_EQ(0, get_array_depth(entry));
218   EXPECT_FALSE(entry.is_linearized_index_referenced(0));
219}
220
221TEST_F(array_refcount_test, ir_array_refcount_entry_initial_state_for_vector)
222{
223   ir_variable *const var =
224      new(mem_ctx) ir_variable(glsl_type::vec4_type, "a", ir_var_auto);
225
226   ir_array_refcount_entry entry(var);
227
228   ASSERT_NE((void *)0, get_bits(entry));
229   EXPECT_FALSE(entry.is_referenced);
230   EXPECT_EQ(1, get_num_bits(entry));
231   EXPECT_EQ(0, get_array_depth(entry));
232   EXPECT_FALSE(entry.is_linearized_index_referenced(0));
233}
234
235TEST_F(array_refcount_test, ir_array_refcount_entry_initial_state_for_matrix)
236{
237   ir_variable *const var =
238      new(mem_ctx) ir_variable(glsl_type::mat4_type, "a", ir_var_auto);
239
240   ir_array_refcount_entry entry(var);
241
242   ASSERT_NE((void *)0, get_bits(entry));
243   EXPECT_FALSE(entry.is_referenced);
244   EXPECT_EQ(1, get_num_bits(entry));
245   EXPECT_EQ(0, get_array_depth(entry));
246   EXPECT_FALSE(entry.is_linearized_index_referenced(0));
247}
248
249TEST_F(array_refcount_test, ir_array_refcount_entry_initial_state_for_array)
250{
251   ir_variable *const var =
252      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
253                               "a",
254                               ir_var_auto);
255   const unsigned total_elements = var->type->arrays_of_arrays_size();
256
257   ir_array_refcount_entry entry(var);
258
259   ASSERT_NE((void *)0, get_bits(entry));
260   EXPECT_FALSE(entry.is_referenced);
261   EXPECT_EQ(total_elements, get_num_bits(entry));
262   EXPECT_EQ(3, get_array_depth(entry));
263
264   for (unsigned i = 0; i < total_elements; i++)
265      EXPECT_FALSE(entry.is_linearized_index_referenced(i)) << "index = " << i;
266}
267
268TEST_F(array_refcount_test, mark_array_elements_referenced_simple)
269{
270   ir_variable *const var =
271      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
272                               "a",
273                               ir_var_auto);
274   const unsigned total_elements = var->type->arrays_of_arrays_size();
275
276   ir_array_refcount_entry entry(var);
277
278   static const array_deref_range dr[] = {
279      { 0, 5 }, { 1, 4 }, { 2, 3 }
280   };
281   const unsigned accessed_element = 0 + (1 * 5) + (2 * 4 * 5);
282
283   entry.mark_array_elements_referenced(dr, 3);
284
285   for (unsigned i = 0; i < total_elements; i++)
286      EXPECT_EQ(i == accessed_element, entry.is_linearized_index_referenced(i));
287}
288
289TEST_F(array_refcount_test, mark_array_elements_referenced_whole_first_array)
290{
291   ir_variable *const var =
292      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
293                               "a",
294                               ir_var_auto);
295
296   ir_array_refcount_entry entry(var);
297
298   static const array_deref_range dr[] = {
299      { 0, 5 }, { 1, 4 }, { 3, 3 }
300   };
301
302   entry.mark_array_elements_referenced(dr, 3);
303
304   for (unsigned i = 0; i < 3; i++) {
305      for (unsigned j = 0; j < 4; j++) {
306         for (unsigned k = 0; k < 5; k++) {
307            const bool accessed = (j == 1) && (k == 0);
308            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
309
310            EXPECT_EQ(accessed,
311                      entry.is_linearized_index_referenced(linearized_index));
312         }
313      }
314   }
315}
316
317TEST_F(array_refcount_test, mark_array_elements_referenced_whole_second_array)
318{
319   ir_variable *const var =
320      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
321                               "a",
322                               ir_var_auto);
323
324   ir_array_refcount_entry entry(var);
325
326   static const array_deref_range dr[] = {
327      { 0, 5 }, { 4, 4 }, { 1, 3 }
328   };
329
330   entry.mark_array_elements_referenced(dr, 3);
331
332   for (unsigned i = 0; i < 3; i++) {
333      for (unsigned j = 0; j < 4; j++) {
334         for (unsigned k = 0; k < 5; k++) {
335            const bool accessed = (i == 1) && (k == 0);
336            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
337
338            EXPECT_EQ(accessed,
339                      entry.is_linearized_index_referenced(linearized_index));
340         }
341      }
342   }
343}
344
345TEST_F(array_refcount_test, mark_array_elements_referenced_whole_third_array)
346{
347   ir_variable *const var =
348      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
349                               "a",
350                               ir_var_auto);
351
352   ir_array_refcount_entry entry(var);
353
354   static const array_deref_range dr[] = {
355      { 5, 5 }, { 2, 4 }, { 1, 3 }
356   };
357
358   entry.mark_array_elements_referenced(dr, 3);
359
360   for (unsigned i = 0; i < 3; i++) {
361      for (unsigned j = 0; j < 4; j++) {
362         for (unsigned k = 0; k < 5; k++) {
363            const bool accessed = (i == 1) && (j == 2);
364            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
365
366            EXPECT_EQ(accessed,
367                      entry.is_linearized_index_referenced(linearized_index));
368         }
369      }
370   }
371}
372
373TEST_F(array_refcount_test, mark_array_elements_referenced_whole_first_and_third_arrays)
374{
375   ir_variable *const var =
376      new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
377                               "a",
378                               ir_var_auto);
379
380   ir_array_refcount_entry entry(var);
381
382   static const array_deref_range dr[] = {
383      { 5, 5 }, { 3, 4 }, { 3, 3 }
384   };
385
386   entry.mark_array_elements_referenced(dr, 3);
387
388   for (unsigned i = 0; i < 3; i++) {
389      for (unsigned j = 0; j < 4; j++) {
390         for (unsigned k = 0; k < 5; k++) {
391            const bool accessed = (j == 3);
392            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
393
394            EXPECT_EQ(accessed,
395                      entry.is_linearized_index_referenced(linearized_index));
396         }
397      }
398   }
399}
400
401TEST_F(array_refcount_test, do_not_process_vector_indexing)
402{
403   /* Vectors and matrices can also be indexed in much the same manner as
404    * arrays.  The visitor should not try to track per-element accesses to
405    * these types.
406    */
407   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::float_type,
408                                                 "a",
409                                                 ir_var_auto);
410   ir_variable *var_b = new(mem_ctx) ir_variable(glsl_type::int_type,
411                                                 "b",
412                                                 ir_var_auto);
413   ir_variable *var_c = new(mem_ctx) ir_variable(glsl_type::vec4_type,
414                                                 "c",
415                                                 ir_var_auto);
416
417   body->emit(assign(var_a, deref_array(var_c, var_b)));
418
419   ir_array_refcount_visitor v;
420
421   visit_list_elements(&v, &instructions);
422
423   ir_array_refcount_entry *entry_a = v.get_variable_entry(var_a);
424   ir_array_refcount_entry *entry_b = v.get_variable_entry(var_b);
425   ir_array_refcount_entry *entry_c = v.get_variable_entry(var_c);
426
427   EXPECT_TRUE(entry_a->is_referenced);
428   EXPECT_TRUE(entry_b->is_referenced);
429   EXPECT_TRUE(entry_c->is_referenced);
430
431   /* As validated by previous tests, for non-array types, num_bits is 1. */
432   ASSERT_EQ(1, get_num_bits(*entry_c));
433   EXPECT_FALSE(entry_c->is_linearized_index_referenced(0));
434}
435
436TEST_F(array_refcount_test, do_not_process_matrix_indexing)
437{
438   /* Vectors and matrices can also be indexed in much the same manner as
439    * arrays.  The visitor should not try to track per-element accesses to
440    * these types.
441    */
442   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::vec4_type,
443                                                 "a",
444                                                 ir_var_auto);
445   ir_variable *var_b = new(mem_ctx) ir_variable(glsl_type::int_type,
446                                                 "b",
447                                                 ir_var_auto);
448   ir_variable *var_c = new(mem_ctx) ir_variable(glsl_type::mat4_type,
449                                                 "c",
450                                                 ir_var_auto);
451
452   body->emit(assign(var_a, deref_array(var_c, var_b)));
453
454   ir_array_refcount_visitor v;
455
456   visit_list_elements(&v, &instructions);
457
458   ir_array_refcount_entry *entry_a = v.get_variable_entry(var_a);
459   ir_array_refcount_entry *entry_b = v.get_variable_entry(var_b);
460   ir_array_refcount_entry *entry_c = v.get_variable_entry(var_c);
461
462   EXPECT_TRUE(entry_a->is_referenced);
463   EXPECT_TRUE(entry_b->is_referenced);
464   EXPECT_TRUE(entry_c->is_referenced);
465
466   /* As validated by previous tests, for non-array types, num_bits is 1. */
467   ASSERT_EQ(1, get_num_bits(*entry_c));
468   EXPECT_FALSE(entry_c->is_linearized_index_referenced(0));
469}
470
471TEST_F(array_refcount_test, do_not_process_array_inside_structure)
472{
473   /* Structures can contain arrays.  The visitor should not try to track
474    * per-element accesses to arrays contained inside structures.
475    */
476   const glsl_struct_field fields[] = {
477      glsl_struct_field(array_3_of_int, "i"),
478   };
479
480   const glsl_type *const record_of_array_3_of_int =
481      glsl_type::get_record_instance(fields, ARRAY_SIZE(fields), "S");
482
483   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::int_type,
484                                                 "a",
485                                                 ir_var_auto);
486
487   ir_variable *var_b = new(mem_ctx) ir_variable(record_of_array_3_of_int,
488                                                 "b",
489                                                 ir_var_auto);
490
491   /* a = b.i[2] */
492   body->emit(assign(var_a,
493                     deref_array(
494                        deref_struct(var_b, "i"),
495                        body->constant(int(2)))));
496
497   ir_array_refcount_visitor v;
498
499   visit_list_elements(&v, &instructions);
500
501   ir_array_refcount_entry *entry_a = v.get_variable_entry(var_a);
502   ir_array_refcount_entry *entry_b = v.get_variable_entry(var_b);
503
504   EXPECT_TRUE(entry_a->is_referenced);
505   EXPECT_TRUE(entry_b->is_referenced);
506
507   ASSERT_EQ(1, get_num_bits(*entry_b));
508   EXPECT_FALSE(entry_b->is_linearized_index_referenced(0));
509
510   validate_variables_in_hash_table(v.ht, 2, var_a, var_b);
511}
512
513TEST_F(array_refcount_test, visit_simple_indexing)
514{
515   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::vec4_type,
516                                                 "a",
517                                                 ir_var_auto);
518   ir_variable *var_b = new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
519                                                 "b",
520                                                 ir_var_auto);
521
522   /* a = b[2][1][0] */
523   body->emit(assign(var_a,
524                     deref_array(
525                        deref_array(
526                           deref_array(var_b, body->constant(int(2))),
527                           body->constant(int(1))),
528                        body->constant(int(0)))));
529
530   ir_array_refcount_visitor v;
531
532   visit_list_elements(&v, &instructions);
533
534   const unsigned accessed_element = 0 + (1 * 5) + (2 * 4 * 5);
535   ir_array_refcount_entry *entry_b = v.get_variable_entry(var_b);
536   const unsigned total_elements = var_b->type->arrays_of_arrays_size();
537
538   for (unsigned i = 0; i < total_elements; i++)
539      EXPECT_EQ(i == accessed_element, entry_b->is_linearized_index_referenced(i)) <<
540         "i = " << i;
541
542   validate_variables_in_hash_table(v.ht, 2, var_a, var_b);
543}
544
545TEST_F(array_refcount_test, visit_whole_second_array_indexing)
546{
547   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::vec4_type,
548                                                 "a",
549                                                 ir_var_auto);
550   ir_variable *var_b = new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
551                                                 "b",
552                                                 ir_var_auto);
553   ir_variable *var_i = new(mem_ctx) ir_variable(glsl_type::int_type,
554                                                 "i",
555                                                 ir_var_auto);
556
557   /* a = b[2][i][1] */
558   body->emit(assign(var_a,
559                     deref_array(
560                        deref_array(
561                           deref_array(var_b, body->constant(int(2))),
562                           var_i),
563                        body->constant(int(1)))));
564
565   ir_array_refcount_visitor v;
566
567   visit_list_elements(&v, &instructions);
568
569   ir_array_refcount_entry *const entry_b = v.get_variable_entry(var_b);
570   for (unsigned i = 0; i < 3; i++) {
571      for (unsigned j = 0; j < 4; j++) {
572         for (unsigned k = 0; k < 5; k++) {
573            const bool accessed = (i == 2) && (k == 1);
574            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
575
576            EXPECT_EQ(accessed,
577                      entry_b->is_linearized_index_referenced(linearized_index)) <<
578               "i = " << i;
579         }
580      }
581   }
582
583   validate_variables_in_hash_table(v.ht, 3, var_a, var_b, var_i);
584}
585
586TEST_F(array_refcount_test, visit_array_indexing_an_array)
587{
588   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::vec4_type,
589                                                 "a",
590                                                 ir_var_auto);
591   ir_variable *var_b = new(mem_ctx) ir_variable(array_3_of_array_4_of_array_5_of_vec4,
592                                                 "b",
593                                                 ir_var_auto);
594   ir_variable *var_c = new(mem_ctx) ir_variable(array_3_of_int,
595                                                 "c",
596                                                 ir_var_auto);
597   ir_variable *var_i = new(mem_ctx) ir_variable(glsl_type::int_type,
598                                                 "i",
599                                                 ir_var_auto);
600
601   /* a = b[2][3][c[i]] */
602   body->emit(assign(var_a,
603                     deref_array(
604                        deref_array(
605                           deref_array(var_b, body->constant(int(2))),
606                           body->constant(int(3))),
607                        deref_array(var_c, var_i))));
608
609   ir_array_refcount_visitor v;
610
611   visit_list_elements(&v, &instructions);
612
613   ir_array_refcount_entry *const entry_b = v.get_variable_entry(var_b);
614
615   for (unsigned i = 0; i < 3; i++) {
616      for (unsigned j = 0; j < 4; j++) {
617         for (unsigned k = 0; k < 5; k++) {
618            const bool accessed = (i == 2) && (j == 3);
619            const unsigned linearized_index = k + (j * 5) + (i * 4 * 5);
620
621            EXPECT_EQ(accessed,
622                      entry_b->is_linearized_index_referenced(linearized_index)) <<
623               "array b[" << i << "][" << j << "][" << k << "], " <<
624               "linear index = " << linearized_index;
625         }
626      }
627   }
628
629   ir_array_refcount_entry *const entry_c = v.get_variable_entry(var_c);
630
631   for (unsigned i = 0; i < var_c->type->array_size(); i++) {
632      EXPECT_EQ(true, entry_c->is_linearized_index_referenced(i)) <<
633         "array c, i = " << i;
634   }
635
636   validate_variables_in_hash_table(v.ht, 4, var_a, var_b, var_c, var_i);
637}
638
639TEST_F(array_refcount_test, visit_array_indexing_with_itself)
640{
641   const glsl_type *const array_2_of_array_3_of_int =
642      glsl_type::get_array_instance(array_3_of_int, 2);
643
644   const glsl_type *const array_2_of_array_2_of_array_3_of_int =
645      glsl_type::get_array_instance(array_2_of_array_3_of_int, 2);
646
647   ir_variable *var_a = new(mem_ctx) ir_variable(glsl_type::int_type,
648                                                 "a",
649                                                 ir_var_auto);
650   ir_variable *var_b = new(mem_ctx) ir_variable(array_2_of_array_2_of_array_3_of_int,
651                                                 "b",
652                                                 ir_var_auto);
653
654   /* Given GLSL code:
655    *
656    *    int b[2][2][3];
657    *    a = b[ b[0][0][0] ][ b[ b[0][1][0] ][ b[1][0][0] ][1] ][2]
658    *
659    * b[0][0][0], b[0][1][0], and b[1][0][0] are trivially accessed.
660    *
661    * b[*][*][1] and b[*][*][2] are accessed.
662    *
663    * Only b[1][1][0] is not accessed.
664    */
665   operand b000 = deref_array(
666      deref_array(
667         deref_array(var_b, body->constant(int(0))),
668         body->constant(int(0))),
669      body->constant(int(0)));
670
671   operand b010 = deref_array(
672      deref_array(
673         deref_array(var_b, body->constant(int(0))),
674         body->constant(int(1))),
675      body->constant(int(0)));
676
677   operand b100 = deref_array(
678      deref_array(
679         deref_array(var_b, body->constant(int(1))),
680         body->constant(int(0))),
681      body->constant(int(0)));
682
683   operand b_b010_b100_1 = deref_array(
684      deref_array(
685         deref_array(var_b, b010),
686         b100),
687      body->constant(int(1)));
688
689   body->emit(assign(var_a,
690                     deref_array(
691                        deref_array(
692                           deref_array(var_b, b000),
693                           b_b010_b100_1),
694                        body->constant(int(2)))));
695
696   ir_array_refcount_visitor v;
697
698   visit_list_elements(&v, &instructions);
699
700   ir_array_refcount_entry *const entry_b = v.get_variable_entry(var_b);
701
702   for (unsigned i = 0; i < 2; i++) {
703      for (unsigned j = 0; j < 2; j++) {
704         for (unsigned k = 0; k < 3; k++) {
705            const bool accessed = !(i == 1 && j == 1 && k == 0);
706            const unsigned linearized_index = k + (j * 3) + (i * 2 * 3);
707
708            EXPECT_EQ(accessed,
709                      entry_b->is_linearized_index_referenced(linearized_index)) <<
710               "array b[" << i << "][" << j << "][" << k << "], " <<
711               "linear index = " << linearized_index;
712         }
713      }
714   }
715
716   validate_variables_in_hash_table(v.ht, 2, var_a, var_b);
717}
718