1/*
2 * Copyright © 2009 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
24/**
25 * \file prog_parameter_layout.c
26 * \brief Helper functions to layout storage for program parameters
27 *
28 * \author Ian Romanick <ian.d.romanick@intel.com>
29 */
30
31
32#include "main/mtypes.h"
33#include "prog_parameter.h"
34#include "prog_parameter_layout.h"
35#include "prog_instruction.h"
36#include "program_parser.h"
37
38unsigned
39_mesa_combine_swizzles(unsigned base, unsigned applied)
40{
41   unsigned swiz = 0;
42   unsigned i;
43
44   for (i = 0; i < 4; i++) {
45      const unsigned s = GET_SWZ(applied, i);
46
47      swiz |= ((s <= SWIZZLE_W) ? GET_SWZ(base, s) : s) << (i * 3);
48   }
49
50   return swiz;
51}
52
53
54/**
55 * Copy indirect access array from one parameter list to another
56 *
57 * \param src   Parameter array copied from
58 * \param dst   Parameter array copied to
59 * \param first Index of first element in \c src to copy
60 * \param count Number of elements to copy
61 *
62 * \return
63 * The location in \c dst of the first element copied from \c src on
64 * success.  -1 on failure.
65 *
66 * \warning
67 * This function assumes that there is already enough space available in
68 * \c dst to hold all of the elements that will be copied over.
69 */
70static int
71copy_indirect_accessed_array(struct gl_program_parameter_list *src,
72			     struct gl_program_parameter_list *dst,
73			     unsigned first, unsigned count)
74{
75   const int base = dst->NumParameters;
76   unsigned i, j;
77
78   for (i = first; i < (first + count); i++) {
79      struct gl_program_parameter *curr = & src->Parameters[i];
80
81      if (curr->Type == PROGRAM_CONSTANT) {
82	 j = dst->NumParameters;
83      } else {
84	 for (j = 0; j < dst->NumParameters; j++) {
85	    if (memcmp(dst->Parameters[j].StateIndexes, curr->StateIndexes,
86		       sizeof(curr->StateIndexes)) == 0) {
87	       return -1;
88	    }
89	 }
90      }
91
92      assert(j == dst->NumParameters);
93
94      /* copy src parameter [i] to dest parameter [j] */
95      memcpy(&dst->Parameters[j], curr,
96	     sizeof(dst->Parameters[j]));
97
98      dst->Parameters[j].ValueOffset = dst->NumParameterValues;
99
100      gl_constant_value *pv_dst =
101         dst->ParameterValues + dst->Parameters[j].ValueOffset;
102      gl_constant_value *pv_src =
103         src->ParameterValues + src->Parameters[i].ValueOffset;
104
105      memcpy(pv_dst, pv_src, MIN2(src->Parameters[i].Size, 4) *
106             sizeof(GLfloat));
107      dst->NumParameterValues += MIN2(dst->Parameters[j].Size, 4);
108
109
110      /* Pointer to the string name was copied.  Null-out src param name
111       * to prevent double free later.
112       */
113      curr->Name = NULL;
114
115      dst->NumParameters++;
116   }
117
118   return base;
119}
120
121
122static int compare_state_var(const void *a1, const void *a2)
123{
124   const struct gl_program_parameter *p1 =
125      (const struct gl_program_parameter *)a1;
126   const struct gl_program_parameter *p2 =
127      (const struct gl_program_parameter *)a2;
128
129   for (unsigned i = 0; i < STATE_LENGTH; i++) {
130      if (p1->StateIndexes[i] != p2->StateIndexes[i])
131         return p1->StateIndexes[i] - p2->StateIndexes[i];
132   }
133   return 0;
134}
135
136
137/**
138 * Create the final program parameter list in this order:
139 * - constants and state variables with variable indexing are first
140 * - other constants are next
141 * - other state variables are last and sorted
142 *
143 * \return GL_TRUE for success, GL_FALSE for failure
144 */
145GLboolean
146_mesa_layout_parameters(struct asm_parser_state *state)
147{
148   struct gl_program_parameter_list *layout;
149   struct asm_instruction *inst;
150
151   layout =
152      _mesa_new_parameter_list_sized(state->prog->Parameters->NumParameters);
153
154   /* PASS 1:  Move any parameters that are accessed indirectly from the
155    * original parameter list to the new parameter list.
156    */
157   for (inst = state->inst_head; inst != NULL; inst = inst->next) {
158      for (unsigned i = 0; i < 3; i++) {
159         if (inst->SrcReg[i].Base.RelAddr) {
160            /* Only attempt to add the to the new parameter list once.
161             */
162            if (!inst->SrcReg[i].Symbol->pass1_done) {
163               const int new_begin =
164                  copy_indirect_accessed_array(state->prog->Parameters, layout,
165                                               inst->SrcReg[i].Symbol->param_binding_begin,
166                                               inst->SrcReg[i].Symbol->param_binding_length);
167
168               if (new_begin < 0) {
169                  _mesa_free_parameter_list(layout);
170                  return GL_FALSE;
171               }
172
173               inst->SrcReg[i].Symbol->param_binding_begin = new_begin;
174               inst->SrcReg[i].Symbol->pass1_done = 1;
175            }
176
177            /* Previously the Index was just the offset from the parameter
178             * array.  Now that the base of the parameter array is known, the
179             * index can be updated to its actual value.
180             */
181            inst->Base.SrcReg[i] = inst->SrcReg[i].Base;
182            inst->Base.SrcReg[i].Index +=
183               inst->SrcReg[i].Symbol->param_binding_begin;
184         }
185      }
186   }
187
188   /* PASS 2: Move any constants that are not accessed indirectly from the
189    * original parameter list to the new parameter list.
190    */
191   for (inst = state->inst_head; inst != NULL; inst = inst->next) {
192      for (unsigned i = 0; i < 3; i++) {
193         const int idx = inst->SrcReg[i].Base.Index;
194         const struct gl_program_parameter *const p =
195            &state->prog->Parameters->Parameters[idx];
196         unsigned swizzle = SWIZZLE_NOOP;
197
198         if (inst->SrcReg[i].Base.RelAddr ||
199             inst->SrcReg[i].Base.File <= PROGRAM_OUTPUT ||
200             inst->SrcReg[i].Base.File >= PROGRAM_WRITE_ONLY ||
201             p->Type != PROGRAM_CONSTANT)
202            continue;
203
204         inst->Base.SrcReg[i] = inst->SrcReg[i].Base;
205
206         unsigned pvo = state->prog->Parameters->Parameters[idx].ValueOffset;
207         const gl_constant_value *const v =
208            state->prog->Parameters->ParameterValues + pvo;
209
210         inst->Base.SrcReg[i].Index =
211            _mesa_add_unnamed_constant(layout, v, p->Size, &swizzle);
212
213         inst->Base.SrcReg[i].Swizzle =
214            _mesa_combine_swizzles(swizzle, inst->Base.SrcReg[i].Swizzle);
215
216         inst->SrcReg[i].Base.File = p->Type;
217         inst->Base.SrcReg[i].File = p->Type;
218      }
219   }
220
221   /* PASS 3: Add sorted state variables.  NOTE: This pass does **not** modify
222    * the instruction with the updated index.  The sorting step might
223    * invalidate the index that was calculated by _mesa_add_state_reference.
224    * Instead, it relies on PASS 4 to do this.
225    */
226   unsigned first_state_var = layout->NumParameters;
227
228   for (inst = state->inst_head; inst != NULL; inst = inst->next) {
229      for (unsigned i = 0; i < 3; i++) {
230         const struct gl_program_parameter *p;
231         const int idx = inst->SrcReg[i].Base.Index;
232
233         p = &state->prog->Parameters->Parameters[idx];
234
235         if (inst->SrcReg[i].Base.RelAddr ||
236             inst->SrcReg[i].Base.File <= PROGRAM_OUTPUT ||
237             inst->SrcReg[i].Base.File >= PROGRAM_WRITE_ONLY ||
238             p->Type != PROGRAM_STATE_VAR)
239            continue;
240
241         _mesa_add_state_reference(layout, p->StateIndexes);
242      }
243   }
244
245   /* Sort if we have added at least 2 state vars. */
246   if (first_state_var + 2 <= layout->NumParameters) {
247      /* All state vars should be vec4s. */
248      for (unsigned i = first_state_var; i < layout->NumParameters; i++) {
249         assert(layout->Parameters[i].Size == 4);
250         assert(layout->Parameters[i].ValueOffset == i * 4);
251      }
252
253      qsort(layout->Parameters + first_state_var,
254            layout->NumParameters - first_state_var,
255            sizeof(layout->Parameters[0]), compare_state_var);
256
257      /* Fix offsets. */
258      for (unsigned i = first_state_var; i < layout->NumParameters; i++) {
259         layout->Parameters[i].ValueOffset = i * 4;
260      }
261   }
262
263   /* PASS 4: Fix up the index and file information for instructions whose
264    * parameters were added to the parameter list in PASS 3.
265    */
266   for (inst = state->inst_head; inst != NULL; inst = inst->next) {
267      for (unsigned i = 0; i < 3; i++) {
268         const int idx = inst->SrcReg[i].Base.Index;
269         const struct gl_program_parameter *const p =
270            &state->prog->Parameters->Parameters[idx];
271
272         if (inst->SrcReg[i].Base.RelAddr ||
273             inst->SrcReg[i].Base.File <= PROGRAM_OUTPUT ||
274             inst->SrcReg[i].Base.File >= PROGRAM_WRITE_ONLY ||
275             p->Type != PROGRAM_STATE_VAR)
276            continue;
277
278         inst->Base.SrcReg[i] = inst->SrcReg[i].Base;
279
280         inst->Base.SrcReg[i].Index =
281            _mesa_add_state_reference(layout, p->StateIndexes);
282
283         inst->SrcReg[i].Base.File = p->Type;
284         inst->Base.SrcReg[i].File = p->Type;
285      }
286   }
287
288   assert(layout->NumParameters <= state->prog->Parameters->NumParameters);
289   _mesa_recompute_parameter_bounds(layout);
290
291   layout->StateFlags = state->prog->Parameters->StateFlags;
292   _mesa_free_parameter_list(state->prog->Parameters);
293   state->prog->Parameters = layout;
294
295   return GL_TRUE;
296}
297