1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2018 Intel Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci * IN THE SOFTWARE.
22bf215546Sopenharmony_ci *
23bf215546Sopenharmony_ci */
24bf215546Sopenharmony_ci#include <ctype.h>
25bf215546Sopenharmony_ci
26bf215546Sopenharmony_ci#include "glsl_types.h"
27bf215546Sopenharmony_ci#include "linker_util.h"
28bf215546Sopenharmony_ci#include "util/bitscan.h"
29bf215546Sopenharmony_ci#include "util/set.h"
30bf215546Sopenharmony_ci#include "ir_uniform.h" /* for gl_uniform_storage */
31bf215546Sopenharmony_ci#include "main/shader_types.h"
32bf215546Sopenharmony_ci#include "main/consts_exts.h"
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_ci/**
35bf215546Sopenharmony_ci * Given a string identifying a program resource, break it into a base name
36bf215546Sopenharmony_ci * and an optional array index in square brackets.
37bf215546Sopenharmony_ci *
38bf215546Sopenharmony_ci * If an array index is present, \c out_base_name_end is set to point to the
39bf215546Sopenharmony_ci * "[" that precedes the array index, and the array index itself is returned
40bf215546Sopenharmony_ci * as a long.
41bf215546Sopenharmony_ci *
42bf215546Sopenharmony_ci * If no array index is present (or if the array index is negative or
43bf215546Sopenharmony_ci * mal-formed), \c out_base_name_end, is set to point to the null terminator
44bf215546Sopenharmony_ci * at the end of the input string, and -1 is returned.
45bf215546Sopenharmony_ci *
46bf215546Sopenharmony_ci * Only the final array index is parsed; if the string contains other array
47bf215546Sopenharmony_ci * indices (or structure field accesses), they are left in the base name.
48bf215546Sopenharmony_ci *
49bf215546Sopenharmony_ci * No attempt is made to check that the base name is properly formed;
50bf215546Sopenharmony_ci * typically the caller will look up the base name in a hash table, so
51bf215546Sopenharmony_ci * ill-formed base names simply turn into hash table lookup failures.
52bf215546Sopenharmony_ci */
53bf215546Sopenharmony_cilong
54bf215546Sopenharmony_cilink_util_parse_program_resource_name(const GLchar *name, const size_t len,
55bf215546Sopenharmony_ci                                      const GLchar **out_base_name_end)
56bf215546Sopenharmony_ci{
57bf215546Sopenharmony_ci   /* Section 7.3.1 ("Program Interfaces") of the OpenGL 4.3 spec says:
58bf215546Sopenharmony_ci    *
59bf215546Sopenharmony_ci    *     "When an integer array element or block instance number is part of
60bf215546Sopenharmony_ci    *     the name string, it will be specified in decimal form without a "+"
61bf215546Sopenharmony_ci    *     or "-" sign or any extra leading zeroes. Additionally, the name
62bf215546Sopenharmony_ci    *     string will not include white space anywhere in the string."
63bf215546Sopenharmony_ci    */
64bf215546Sopenharmony_ci
65bf215546Sopenharmony_ci   *out_base_name_end = name + len;
66bf215546Sopenharmony_ci
67bf215546Sopenharmony_ci   if (len == 0 || name[len-1] != ']')
68bf215546Sopenharmony_ci      return -1;
69bf215546Sopenharmony_ci
70bf215546Sopenharmony_ci   /* Walk backwards over the string looking for a non-digit character.  This
71bf215546Sopenharmony_ci    * had better be the opening bracket for an array index.
72bf215546Sopenharmony_ci    *
73bf215546Sopenharmony_ci    * Initially, i specifies the location of the ']'.  Since the string may
74bf215546Sopenharmony_ci    * contain only the ']' charcater, walk backwards very carefully.
75bf215546Sopenharmony_ci    */
76bf215546Sopenharmony_ci   unsigned i;
77bf215546Sopenharmony_ci   for (i = len - 1; (i > 0) && isdigit(name[i-1]); --i)
78bf215546Sopenharmony_ci      /* empty */ ;
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_ci   if ((i == 0) || name[i-1] != '[')
81bf215546Sopenharmony_ci      return -1;
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci   long array_index = strtol(&name[i], NULL, 10);
84bf215546Sopenharmony_ci   if (array_index < 0)
85bf215546Sopenharmony_ci      return -1;
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci   /* Check for leading zero */
88bf215546Sopenharmony_ci   if (name[i] == '0' && name[i+1] != ']')
89bf215546Sopenharmony_ci      return -1;
90bf215546Sopenharmony_ci
91bf215546Sopenharmony_ci   *out_base_name_end = name + (i - 1);
92bf215546Sopenharmony_ci   return array_index;
93bf215546Sopenharmony_ci}
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci/* Utility methods shared between the GLSL IR and the NIR */
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_ci/* From the OpenGL 4.6 specification, 7.3.1.1 Naming Active Resources:
98bf215546Sopenharmony_ci *
99bf215546Sopenharmony_ci *    "For an active shader storage block member declared as an array of an
100bf215546Sopenharmony_ci *     aggregate type, an entry will be generated only for the first array
101bf215546Sopenharmony_ci *     element, regardless of its type. Such block members are referred to as
102bf215546Sopenharmony_ci *     top-level arrays. If the block member is an aggregate type, the
103bf215546Sopenharmony_ci *     enumeration rules are then applied recursively."
104bf215546Sopenharmony_ci */
105bf215546Sopenharmony_cibool
106bf215546Sopenharmony_cilink_util_should_add_buffer_variable(struct gl_shader_program *prog,
107bf215546Sopenharmony_ci                                     struct gl_uniform_storage *uniform,
108bf215546Sopenharmony_ci                                     int top_level_array_base_offset,
109bf215546Sopenharmony_ci                                     int top_level_array_size_in_bytes,
110bf215546Sopenharmony_ci                                     int second_element_offset,
111bf215546Sopenharmony_ci                                     int block_index)
112bf215546Sopenharmony_ci{
113bf215546Sopenharmony_ci   /* If the uniform is not a shader storage buffer or is not an array return
114bf215546Sopenharmony_ci    * true.
115bf215546Sopenharmony_ci    */
116bf215546Sopenharmony_ci   if (!uniform->is_shader_storage || top_level_array_size_in_bytes == 0)
117bf215546Sopenharmony_ci      return true;
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci   int after_top_level_array = top_level_array_base_offset +
120bf215546Sopenharmony_ci      top_level_array_size_in_bytes;
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_ci   /* Check for a new block, or that we are not dealing with array elements of
123bf215546Sopenharmony_ci    * a top member array other than the first element.
124bf215546Sopenharmony_ci    */
125bf215546Sopenharmony_ci   if (block_index != uniform->block_index ||
126bf215546Sopenharmony_ci       uniform->offset >= after_top_level_array ||
127bf215546Sopenharmony_ci       uniform->offset < second_element_offset) {
128bf215546Sopenharmony_ci      return true;
129bf215546Sopenharmony_ci   }
130bf215546Sopenharmony_ci
131bf215546Sopenharmony_ci   return false;
132bf215546Sopenharmony_ci}
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_cibool
135bf215546Sopenharmony_cilink_util_add_program_resource(struct gl_shader_program *prog,
136bf215546Sopenharmony_ci                               struct set *resource_set,
137bf215546Sopenharmony_ci                               GLenum type, const void *data, uint8_t stages)
138bf215546Sopenharmony_ci{
139bf215546Sopenharmony_ci   assert(data);
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci   /* If resource already exists, do not add it again. */
142bf215546Sopenharmony_ci   if (_mesa_set_search(resource_set, data))
143bf215546Sopenharmony_ci      return true;
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci   prog->data->ProgramResourceList =
146bf215546Sopenharmony_ci      reralloc(prog->data,
147bf215546Sopenharmony_ci               prog->data->ProgramResourceList,
148bf215546Sopenharmony_ci               gl_program_resource,
149bf215546Sopenharmony_ci               prog->data->NumProgramResourceList + 1);
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_ci   if (!prog->data->ProgramResourceList) {
152bf215546Sopenharmony_ci      linker_error(prog, "Out of memory during linking.\n");
153bf215546Sopenharmony_ci      return false;
154bf215546Sopenharmony_ci   }
155bf215546Sopenharmony_ci
156bf215546Sopenharmony_ci   struct gl_program_resource *res =
157bf215546Sopenharmony_ci      &prog->data->ProgramResourceList[prog->data->NumProgramResourceList];
158bf215546Sopenharmony_ci
159bf215546Sopenharmony_ci   res->Type = type;
160bf215546Sopenharmony_ci   res->Data = data;
161bf215546Sopenharmony_ci   res->StageReferences = stages;
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci   prog->data->NumProgramResourceList++;
164bf215546Sopenharmony_ci
165bf215546Sopenharmony_ci   _mesa_set_add(resource_set, data);
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci   return true;
168bf215546Sopenharmony_ci}
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci/**
171bf215546Sopenharmony_ci * Search through the list of empty blocks to find one that fits the current
172bf215546Sopenharmony_ci * uniform.
173bf215546Sopenharmony_ci */
174bf215546Sopenharmony_ciint
175bf215546Sopenharmony_cilink_util_find_empty_block(struct gl_shader_program *prog,
176bf215546Sopenharmony_ci                           struct gl_uniform_storage *uniform)
177bf215546Sopenharmony_ci{
178bf215546Sopenharmony_ci   const unsigned entries = MAX2(1, uniform->array_elements);
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci   foreach_list_typed(struct empty_uniform_block, block, link,
181bf215546Sopenharmony_ci                      &prog->EmptyUniformLocations) {
182bf215546Sopenharmony_ci      /* Found a block with enough slots to fit the uniform */
183bf215546Sopenharmony_ci      if (block->slots == entries) {
184bf215546Sopenharmony_ci         unsigned start = block->start;
185bf215546Sopenharmony_ci         exec_node_remove(&block->link);
186bf215546Sopenharmony_ci         ralloc_free(block);
187bf215546Sopenharmony_ci
188bf215546Sopenharmony_ci         return start;
189bf215546Sopenharmony_ci      /* Found a block with more slots than needed. It can still be used. */
190bf215546Sopenharmony_ci      } else if (block->slots > entries) {
191bf215546Sopenharmony_ci         unsigned start = block->start;
192bf215546Sopenharmony_ci         block->start += entries;
193bf215546Sopenharmony_ci         block->slots -= entries;
194bf215546Sopenharmony_ci
195bf215546Sopenharmony_ci         return start;
196bf215546Sopenharmony_ci      }
197bf215546Sopenharmony_ci   }
198bf215546Sopenharmony_ci
199bf215546Sopenharmony_ci   return -1;
200bf215546Sopenharmony_ci}
201bf215546Sopenharmony_ci
202bf215546Sopenharmony_civoid
203bf215546Sopenharmony_cilink_util_update_empty_uniform_locations(struct gl_shader_program *prog)
204bf215546Sopenharmony_ci{
205bf215546Sopenharmony_ci   struct empty_uniform_block *current_block = NULL;
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_ci   for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) {
208bf215546Sopenharmony_ci      /* We found empty space in UniformRemapTable. */
209bf215546Sopenharmony_ci      if (prog->UniformRemapTable[i] == NULL) {
210bf215546Sopenharmony_ci         /* We've found the beginning of a new continous block of empty slots */
211bf215546Sopenharmony_ci         if (!current_block || current_block->start + current_block->slots != i) {
212bf215546Sopenharmony_ci            current_block = rzalloc(prog, struct empty_uniform_block);
213bf215546Sopenharmony_ci            current_block->start = i;
214bf215546Sopenharmony_ci            exec_list_push_tail(&prog->EmptyUniformLocations,
215bf215546Sopenharmony_ci                                &current_block->link);
216bf215546Sopenharmony_ci         }
217bf215546Sopenharmony_ci
218bf215546Sopenharmony_ci         /* The current block continues, so we simply increment its slots */
219bf215546Sopenharmony_ci         current_block->slots++;
220bf215546Sopenharmony_ci      }
221bf215546Sopenharmony_ci   }
222bf215546Sopenharmony_ci}
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_civoid
225bf215546Sopenharmony_cilink_util_check_subroutine_resources(struct gl_shader_program *prog)
226bf215546Sopenharmony_ci{
227bf215546Sopenharmony_ci   unsigned mask = prog->data->linked_stages;
228bf215546Sopenharmony_ci   while (mask) {
229bf215546Sopenharmony_ci      const int i = u_bit_scan(&mask);
230bf215546Sopenharmony_ci      struct gl_program *p = prog->_LinkedShaders[i]->Program;
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci      if (p->sh.NumSubroutineUniformRemapTable > MAX_SUBROUTINE_UNIFORM_LOCATIONS) {
233bf215546Sopenharmony_ci         linker_error(prog, "Too many %s shader subroutine uniforms\n",
234bf215546Sopenharmony_ci                      _mesa_shader_stage_to_string(i));
235bf215546Sopenharmony_ci      }
236bf215546Sopenharmony_ci   }
237bf215546Sopenharmony_ci}
238bf215546Sopenharmony_ci
239bf215546Sopenharmony_ci/**
240bf215546Sopenharmony_ci * Validate uniform resources used by a program versus the implementation limits
241bf215546Sopenharmony_ci */
242bf215546Sopenharmony_civoid
243bf215546Sopenharmony_cilink_util_check_uniform_resources(const struct gl_constants *consts,
244bf215546Sopenharmony_ci                                  struct gl_shader_program *prog)
245bf215546Sopenharmony_ci{
246bf215546Sopenharmony_ci   unsigned total_uniform_blocks = 0;
247bf215546Sopenharmony_ci   unsigned total_shader_storage_blocks = 0;
248bf215546Sopenharmony_ci
249bf215546Sopenharmony_ci   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
250bf215546Sopenharmony_ci      struct gl_linked_shader *sh = prog->_LinkedShaders[i];
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci      if (sh == NULL)
253bf215546Sopenharmony_ci         continue;
254bf215546Sopenharmony_ci
255bf215546Sopenharmony_ci      if (sh->num_uniform_components >
256bf215546Sopenharmony_ci          consts->Program[i].MaxUniformComponents) {
257bf215546Sopenharmony_ci         if (consts->GLSLSkipStrictMaxUniformLimitCheck) {
258bf215546Sopenharmony_ci            linker_warning(prog, "Too many %s shader default uniform block "
259bf215546Sopenharmony_ci                           "components, but the driver will try to optimize "
260bf215546Sopenharmony_ci                           "them out; this is non-portable out-of-spec "
261bf215546Sopenharmony_ci                           "behavior\n",
262bf215546Sopenharmony_ci                           _mesa_shader_stage_to_string(i));
263bf215546Sopenharmony_ci         } else {
264bf215546Sopenharmony_ci            linker_error(prog, "Too many %s shader default uniform block "
265bf215546Sopenharmony_ci                         "components\n",
266bf215546Sopenharmony_ci                         _mesa_shader_stage_to_string(i));
267bf215546Sopenharmony_ci         }
268bf215546Sopenharmony_ci      }
269bf215546Sopenharmony_ci
270bf215546Sopenharmony_ci      if (sh->num_combined_uniform_components >
271bf215546Sopenharmony_ci          consts->Program[i].MaxCombinedUniformComponents) {
272bf215546Sopenharmony_ci         if (consts->GLSLSkipStrictMaxUniformLimitCheck) {
273bf215546Sopenharmony_ci            linker_warning(prog, "Too many %s shader uniform components, "
274bf215546Sopenharmony_ci                           "but the driver will try to optimize them out; "
275bf215546Sopenharmony_ci                           "this is non-portable out-of-spec behavior\n",
276bf215546Sopenharmony_ci                           _mesa_shader_stage_to_string(i));
277bf215546Sopenharmony_ci         } else {
278bf215546Sopenharmony_ci            linker_error(prog, "Too many %s shader uniform components\n",
279bf215546Sopenharmony_ci                         _mesa_shader_stage_to_string(i));
280bf215546Sopenharmony_ci         }
281bf215546Sopenharmony_ci      }
282bf215546Sopenharmony_ci
283bf215546Sopenharmony_ci      total_shader_storage_blocks += sh->Program->info.num_ssbos;
284bf215546Sopenharmony_ci      total_uniform_blocks += sh->Program->info.num_ubos;
285bf215546Sopenharmony_ci   }
286bf215546Sopenharmony_ci
287bf215546Sopenharmony_ci   if (total_uniform_blocks > consts->MaxCombinedUniformBlocks) {
288bf215546Sopenharmony_ci      linker_error(prog, "Too many combined uniform blocks (%d/%d)\n",
289bf215546Sopenharmony_ci                   total_uniform_blocks, consts->MaxCombinedUniformBlocks);
290bf215546Sopenharmony_ci   }
291bf215546Sopenharmony_ci
292bf215546Sopenharmony_ci   if (total_shader_storage_blocks > consts->MaxCombinedShaderStorageBlocks) {
293bf215546Sopenharmony_ci      linker_error(prog, "Too many combined shader storage blocks (%d/%d)\n",
294bf215546Sopenharmony_ci                   total_shader_storage_blocks,
295bf215546Sopenharmony_ci                   consts->MaxCombinedShaderStorageBlocks);
296bf215546Sopenharmony_ci   }
297bf215546Sopenharmony_ci
298bf215546Sopenharmony_ci   for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) {
299bf215546Sopenharmony_ci      if (prog->data->UniformBlocks[i].UniformBufferSize >
300bf215546Sopenharmony_ci          consts->MaxUniformBlockSize) {
301bf215546Sopenharmony_ci         linker_error(prog, "Uniform block %s too big (%d/%d)\n",
302bf215546Sopenharmony_ci                      prog->data->UniformBlocks[i].name.string,
303bf215546Sopenharmony_ci                      prog->data->UniformBlocks[i].UniformBufferSize,
304bf215546Sopenharmony_ci                      consts->MaxUniformBlockSize);
305bf215546Sopenharmony_ci      }
306bf215546Sopenharmony_ci   }
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci   for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) {
309bf215546Sopenharmony_ci      if (prog->data->ShaderStorageBlocks[i].UniformBufferSize >
310bf215546Sopenharmony_ci          consts->MaxShaderStorageBlockSize) {
311bf215546Sopenharmony_ci         linker_error(prog, "Shader storage block %s too big (%d/%d)\n",
312bf215546Sopenharmony_ci                      prog->data->ShaderStorageBlocks[i].name.string,
313bf215546Sopenharmony_ci                      prog->data->ShaderStorageBlocks[i].UniformBufferSize,
314bf215546Sopenharmony_ci                      consts->MaxShaderStorageBlockSize);
315bf215546Sopenharmony_ci      }
316bf215546Sopenharmony_ci   }
317bf215546Sopenharmony_ci}
318bf215546Sopenharmony_ci
319bf215546Sopenharmony_civoid
320bf215546Sopenharmony_cilink_util_calculate_subroutine_compat(struct gl_shader_program *prog)
321bf215546Sopenharmony_ci{
322bf215546Sopenharmony_ci   unsigned mask = prog->data->linked_stages;
323bf215546Sopenharmony_ci   while (mask) {
324bf215546Sopenharmony_ci      const int i = u_bit_scan(&mask);
325bf215546Sopenharmony_ci      struct gl_program *p = prog->_LinkedShaders[i]->Program;
326bf215546Sopenharmony_ci
327bf215546Sopenharmony_ci      for (unsigned j = 0; j < p->sh.NumSubroutineUniformRemapTable; j++) {
328bf215546Sopenharmony_ci         if (p->sh.SubroutineUniformRemapTable[j] == INACTIVE_UNIFORM_EXPLICIT_LOCATION)
329bf215546Sopenharmony_ci            continue;
330bf215546Sopenharmony_ci
331bf215546Sopenharmony_ci         struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[j];
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci         if (!uni)
334bf215546Sopenharmony_ci            continue;
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci         int count = 0;
337bf215546Sopenharmony_ci         if (p->sh.NumSubroutineFunctions == 0) {
338bf215546Sopenharmony_ci            linker_error(prog, "subroutine uniform %s defined but no valid functions found\n", uni->type->name);
339bf215546Sopenharmony_ci            continue;
340bf215546Sopenharmony_ci         }
341bf215546Sopenharmony_ci         for (unsigned f = 0; f < p->sh.NumSubroutineFunctions; f++) {
342bf215546Sopenharmony_ci            struct gl_subroutine_function *fn = &p->sh.SubroutineFunctions[f];
343bf215546Sopenharmony_ci            for (int k = 0; k < fn->num_compat_types; k++) {
344bf215546Sopenharmony_ci               if (fn->types[k] == uni->type) {
345bf215546Sopenharmony_ci                  count++;
346bf215546Sopenharmony_ci                  break;
347bf215546Sopenharmony_ci               }
348bf215546Sopenharmony_ci            }
349bf215546Sopenharmony_ci         }
350bf215546Sopenharmony_ci         uni->num_compatible_subroutines = count;
351bf215546Sopenharmony_ci      }
352bf215546Sopenharmony_ci   }
353bf215546Sopenharmony_ci}
354bf215546Sopenharmony_ci
355bf215546Sopenharmony_ci/**
356bf215546Sopenharmony_ci * Recursive part of the public mark_array_elements_referenced function.
357bf215546Sopenharmony_ci *
358bf215546Sopenharmony_ci * The recursion occurs when an entire array-of- is accessed.  See the
359bf215546Sopenharmony_ci * implementation for more details.
360bf215546Sopenharmony_ci *
361bf215546Sopenharmony_ci * \param dr                List of array_deref_range elements to be
362bf215546Sopenharmony_ci *                          processed.
363bf215546Sopenharmony_ci * \param count             Number of array_deref_range elements to be
364bf215546Sopenharmony_ci *                          processed.
365bf215546Sopenharmony_ci * \param scale             Current offset scale.
366bf215546Sopenharmony_ci * \param linearized_index  Current accumulated linearized array index.
367bf215546Sopenharmony_ci */
368bf215546Sopenharmony_civoid
369bf215546Sopenharmony_ci_mark_array_elements_referenced(const struct array_deref_range *dr,
370bf215546Sopenharmony_ci                                unsigned count, unsigned scale,
371bf215546Sopenharmony_ci                                unsigned linearized_index,
372bf215546Sopenharmony_ci                                BITSET_WORD *bits)
373bf215546Sopenharmony_ci{
374bf215546Sopenharmony_ci   /* Walk through the list of array dereferences in least- to
375bf215546Sopenharmony_ci    * most-significant order.  Along the way, accumulate the current
376bf215546Sopenharmony_ci    * linearized offset and the scale factor for each array-of-.
377bf215546Sopenharmony_ci    */
378bf215546Sopenharmony_ci   for (unsigned i = 0; i < count; i++) {
379bf215546Sopenharmony_ci      if (dr[i].index < dr[i].size) {
380bf215546Sopenharmony_ci         linearized_index += dr[i].index * scale;
381bf215546Sopenharmony_ci         scale *= dr[i].size;
382bf215546Sopenharmony_ci      } else {
383bf215546Sopenharmony_ci         /* For each element in the current array, update the count and
384bf215546Sopenharmony_ci          * offset, then recurse to process the remaining arrays.
385bf215546Sopenharmony_ci          *
386bf215546Sopenharmony_ci          * There is some inefficency here if the last eBITSET_WORD *bitslement in the
387bf215546Sopenharmony_ci          * array_deref_range list specifies the entire array.  In that case,
388bf215546Sopenharmony_ci          * the loop will make recursive calls with count == 0.  In the call,
389bf215546Sopenharmony_ci          * all that will happen is the bit will be set.
390bf215546Sopenharmony_ci          */
391bf215546Sopenharmony_ci         for (unsigned j = 0; j < dr[i].size; j++) {
392bf215546Sopenharmony_ci            _mark_array_elements_referenced(&dr[i + 1],
393bf215546Sopenharmony_ci                                            count - (i + 1),
394bf215546Sopenharmony_ci                                            scale * dr[i].size,
395bf215546Sopenharmony_ci                                            linearized_index + (j * scale),
396bf215546Sopenharmony_ci                                            bits);
397bf215546Sopenharmony_ci         }
398bf215546Sopenharmony_ci
399bf215546Sopenharmony_ci         return;
400bf215546Sopenharmony_ci      }
401bf215546Sopenharmony_ci   }
402bf215546Sopenharmony_ci
403bf215546Sopenharmony_ci   BITSET_SET(bits, linearized_index);
404bf215546Sopenharmony_ci}
405bf215546Sopenharmony_ci
406bf215546Sopenharmony_ci/**
407bf215546Sopenharmony_ci * Mark a set of array elements as accessed.
408bf215546Sopenharmony_ci *
409bf215546Sopenharmony_ci * If every \c array_deref_range is for a single index, only a single
410bf215546Sopenharmony_ci * element will be marked.  If any \c array_deref_range is for an entire
411bf215546Sopenharmony_ci * array-of-, then multiple elements will be marked.
412bf215546Sopenharmony_ci *
413bf215546Sopenharmony_ci * Items in the \c array_deref_range list appear in least- to
414bf215546Sopenharmony_ci * most-significant order.  This is the \b opposite order the indices
415bf215546Sopenharmony_ci * appear in the GLSL shader text.  An array access like
416bf215546Sopenharmony_ci *
417bf215546Sopenharmony_ci *     x = y[1][i][3];
418bf215546Sopenharmony_ci *
419bf215546Sopenharmony_ci * would appear as
420bf215546Sopenharmony_ci *
421bf215546Sopenharmony_ci *     { { 3, n }, { m, m }, { 1, p } }
422bf215546Sopenharmony_ci *
423bf215546Sopenharmony_ci * where n, m, and p are the sizes of the arrays-of-arrays.
424bf215546Sopenharmony_ci *
425bf215546Sopenharmony_ci * The set of marked array elements can later be queried by
426bf215546Sopenharmony_ci * \c ::is_linearized_index_referenced.
427bf215546Sopenharmony_ci *
428bf215546Sopenharmony_ci * \param dr     List of array_deref_range elements to be processed.
429bf215546Sopenharmony_ci * \param count  Number of array_deref_range elements to be processed.
430bf215546Sopenharmony_ci */
431bf215546Sopenharmony_civoid
432bf215546Sopenharmony_cilink_util_mark_array_elements_referenced(const struct array_deref_range *dr,
433bf215546Sopenharmony_ci                                         unsigned count, unsigned array_depth,
434bf215546Sopenharmony_ci                                         BITSET_WORD *bits)
435bf215546Sopenharmony_ci{
436bf215546Sopenharmony_ci   if (count != array_depth)
437bf215546Sopenharmony_ci      return;
438bf215546Sopenharmony_ci
439bf215546Sopenharmony_ci   _mark_array_elements_referenced(dr, count, 1, 0, bits);
440bf215546Sopenharmony_ci}
441