1/**************************************************************************
2 *
3 * Copyright 2010 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 * USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * The above copyright notice and this permission notice (including the
23 * next paragraph) shall be included in all copies or substantial portions
24 * of the Software.
25 *
26 **************************************************************************/
27
28
29#include "util/u_memory.h"
30#include "util/u_math.h"
31#include "tgsi/tgsi_parse.h"
32#include "tgsi/tgsi_util.h"
33#include "tgsi/tgsi_dump.h"
34#include "tgsi/tgsi_strings.h"
35#include "lp_bld_debug.h"
36#include "lp_bld_tgsi.h"
37
38
39/**
40 * Analysis context.
41 *
42 * This is where we keep store the value of each channel of the IMM/TEMP/OUT
43 * register values, as we walk the shader.
44 */
45struct analysis_context
46{
47   struct lp_tgsi_info *info;
48
49   unsigned num_imms;
50   float imm[128][4];
51
52   struct lp_tgsi_channel_info temp[32][4];
53};
54
55
56/**
57 * Describe the specified channel of the src register.
58 */
59static void
60analyse_src(struct analysis_context *ctx,
61            struct lp_tgsi_channel_info *chan_info,
62            const struct tgsi_src_register *src,
63            unsigned chan)
64{
65   chan_info->file = TGSI_FILE_NULL;
66   if (!src->Indirect && !src->Absolute && !src->Negate) {
67      unsigned swizzle = tgsi_util_get_src_register_swizzle(src, chan);
68      if (src->File == TGSI_FILE_TEMPORARY) {
69         if (src->Index < Elements(ctx->temp)) {
70            *chan_info = ctx->temp[src->Index][swizzle];
71         }
72      } else {
73         chan_info->file = src->File;
74         if (src->File == TGSI_FILE_IMMEDIATE) {
75            assert(src->Index < Elements(ctx->imm));
76            if (src->Index < Elements(ctx->imm)) {
77               chan_info->u.value = ctx->imm[src->Index][swizzle];
78            }
79         } else {
80            chan_info->u.index = src->Index;
81            chan_info->swizzle = swizzle;
82         }
83      }
84   }
85}
86
87
88/**
89 * Whether this register channel refers to a specific immediate value.
90 */
91static boolean
92is_immediate(const struct lp_tgsi_channel_info *chan_info, float value)
93{
94   return chan_info->file == TGSI_FILE_IMMEDIATE &&
95          chan_info->u.value == value;
96}
97
98
99static void
100analyse_tex(struct analysis_context *ctx,
101            const struct tgsi_full_instruction *inst,
102            enum lp_build_tex_modifier modifier)
103{
104   struct lp_tgsi_info *info = ctx->info;
105   unsigned chan;
106
107   if (info->num_texs < Elements(info->tex)) {
108      struct lp_tgsi_texture_info *tex_info = &info->tex[info->num_texs];
109      boolean indirect = FALSE;
110      unsigned readmask = 0;
111
112      tex_info->target = inst->Texture.Texture;
113      switch (inst->Texture.Texture) {
114      case TGSI_TEXTURE_1D:
115         readmask = TGSI_WRITEMASK_X;
116         break;
117      case TGSI_TEXTURE_1D_ARRAY:
118      case TGSI_TEXTURE_2D:
119      case TGSI_TEXTURE_RECT:
120         readmask = TGSI_WRITEMASK_XY;
121         break;
122      case TGSI_TEXTURE_SHADOW1D:
123      case TGSI_TEXTURE_SHADOW1D_ARRAY:
124      case TGSI_TEXTURE_SHADOW2D:
125      case TGSI_TEXTURE_SHADOWRECT:
126      case TGSI_TEXTURE_2D_ARRAY:
127      case TGSI_TEXTURE_3D:
128      case TGSI_TEXTURE_CUBE:
129         readmask = TGSI_WRITEMASK_XYZ;
130         break;
131      case TGSI_TEXTURE_SHADOW2D_ARRAY:
132         readmask = TGSI_WRITEMASK_XYZW;
133         break;
134      default:
135         assert(0);
136         return;
137      }
138
139      if (modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV) {
140         /* We don't track explicit derivatives, although we could */
141         indirect = TRUE;
142         tex_info->unit = inst->Src[3].Register.Index;
143      }  else {
144         if (modifier == LP_BLD_TEX_MODIFIER_PROJECTED ||
145             modifier == LP_BLD_TEX_MODIFIER_LOD_BIAS ||
146             modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_LOD) {
147            readmask |= TGSI_WRITEMASK_W;
148         }
149         tex_info->unit = inst->Src[1].Register.Index;
150      }
151
152      for (chan = 0; chan < 4; ++chan) {
153         struct lp_tgsi_channel_info *chan_info = &tex_info->coord[chan];
154         if (readmask & (1 << chan)) {
155            analyse_src(ctx, chan_info, &inst->Src[0].Register, chan);
156            if (chan_info->file != TGSI_FILE_INPUT) {
157               indirect = TRUE;
158            }
159         } else {
160            memset(chan_info, 0, sizeof *chan_info);
161         }
162      }
163
164      if (indirect) {
165         info->indirect_textures = TRUE;
166      }
167
168      ++info->num_texs;
169   } else {
170      info->indirect_textures = TRUE;
171   }
172}
173
174
175/**
176 * Process an instruction, and update the register values accordingly.
177 */
178static void
179analyse_instruction(struct analysis_context *ctx,
180                    struct tgsi_full_instruction *inst)
181{
182   struct lp_tgsi_info *info = ctx->info;
183   struct lp_tgsi_channel_info (*regs)[4];
184   unsigned max_regs;
185   unsigned i;
186   unsigned index;
187   unsigned chan;
188
189   for (i = 0; i < inst->Instruction.NumDstRegs; ++i) {
190      const struct tgsi_dst_register *dst = &inst->Dst[i].Register;
191
192      /*
193       * Get the lp_tgsi_channel_info array corresponding to the destination
194       * register file.
195       */
196
197      if (dst->File == TGSI_FILE_TEMPORARY) {
198         regs = ctx->temp;
199         max_regs = Elements(ctx->temp);
200      } else if (dst->File == TGSI_FILE_OUTPUT) {
201         regs = info->output;
202         max_regs = Elements(info->output);
203      } else if (dst->File == TGSI_FILE_ADDRESS ||
204                 dst->File == TGSI_FILE_PREDICATE) {
205         continue;
206      } else {
207         assert(0);
208         continue;
209      }
210
211      /*
212       * Detect direct TEX instructions
213       */
214
215      switch (inst->Instruction.Opcode) {
216      case TGSI_OPCODE_TEX:
217         analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_NONE);
218         break;
219      case TGSI_OPCODE_TXD:
220         analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV);
221         break;
222      case TGSI_OPCODE_TXB:
223         analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS);
224         break;
225      case TGSI_OPCODE_TXL:
226         analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD);
227         break;
228      case TGSI_OPCODE_TXP:
229         analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_PROJECTED);
230         break;
231      default:
232         break;
233      }
234
235      /*
236       * Keep track of assignments and writes
237       */
238
239      if (dst->Indirect) {
240         /*
241          * It could be any register index so clear all register indices.
242          */
243
244         for (chan = 0; chan < 4; ++chan) {
245            if (dst->WriteMask & (1 << chan)) {
246               for (index = 0; index < max_regs; ++index) {
247                  regs[index][chan].file = TGSI_FILE_NULL;
248               }
249            }
250         }
251      } else if (dst->Index < max_regs) {
252         /*
253          * Update this destination register value.
254          */
255
256         struct lp_tgsi_channel_info res[4];
257
258         memset(res, 0, sizeof res);
259
260         if (!inst->Instruction.Predicate &&
261             !inst->Instruction.Saturate) {
262            for (chan = 0; chan < 4; ++chan) {
263               if (dst->WriteMask & (1 << chan)) {
264                  if (inst->Instruction.Opcode == TGSI_OPCODE_MOV) {
265                     analyse_src(ctx, &res[chan],
266                                 &inst->Src[0].Register, chan);
267                  } else if (inst->Instruction.Opcode == TGSI_OPCODE_MUL) {
268                     /*
269                      * Propagate values across 1.0 and 0.0 multiplications.
270                      */
271
272                     struct lp_tgsi_channel_info src0;
273                     struct lp_tgsi_channel_info src1;
274
275                     analyse_src(ctx, &src0, &inst->Src[0].Register, chan);
276                     analyse_src(ctx, &src1, &inst->Src[1].Register, chan);
277
278                     if (is_immediate(&src0, 0.0f)) {
279                        res[chan] = src0;
280                     } else if (is_immediate(&src1, 0.0f)) {
281                        res[chan] = src1;
282                     } else if (is_immediate(&src0, 1.0f)) {
283                        res[chan] = src1;
284                     } else if (is_immediate(&src1, 1.0f)) {
285                        res[chan] = src0;
286                     }
287                  }
288               }
289            }
290         }
291
292         for (chan = 0; chan < 4; ++chan) {
293            if (dst->WriteMask & (1 << chan)) {
294               regs[dst->Index][chan] = res[chan];
295            }
296         }
297      }
298   }
299
300   /*
301    * Clear all temporaries information in presence of a control flow opcode.
302    */
303
304   switch (inst->Instruction.Opcode) {
305   case TGSI_OPCODE_IF:
306   case TGSI_OPCODE_IFC:
307   case TGSI_OPCODE_ELSE:
308   case TGSI_OPCODE_ENDIF:
309   case TGSI_OPCODE_BGNLOOP:
310   case TGSI_OPCODE_BRK:
311   case TGSI_OPCODE_BREAKC:
312   case TGSI_OPCODE_CONT:
313   case TGSI_OPCODE_ENDLOOP:
314   case TGSI_OPCODE_CALLNZ:
315   case TGSI_OPCODE_CAL:
316   case TGSI_OPCODE_BGNSUB:
317   case TGSI_OPCODE_ENDSUB:
318   case TGSI_OPCODE_SWITCH:
319   case TGSI_OPCODE_CASE:
320   case TGSI_OPCODE_DEFAULT:
321   case TGSI_OPCODE_ENDSWITCH:
322   case TGSI_OPCODE_RET:
323   case TGSI_OPCODE_END:
324      /* XXX: Are there more cases? */
325      memset(&ctx->temp, 0, sizeof ctx->temp);
326      memset(&info->output, 0, sizeof info->output);
327   default:
328      break;
329   }
330}
331
332
333static INLINE void
334dump_info(const struct tgsi_token *tokens,
335          struct lp_tgsi_info *info)
336{
337   unsigned index;
338   unsigned chan;
339
340   tgsi_dump(tokens, 0);
341
342   for (index = 0; index < info->num_texs; ++index) {
343      const struct lp_tgsi_texture_info *tex_info = &info->tex[index];
344      debug_printf("TEX[%u] =", index);
345      for (chan = 0; chan < 4; ++chan) {
346         const struct lp_tgsi_channel_info *chan_info =
347               &tex_info->coord[chan];
348         if (chan_info->file != TGSI_FILE_NULL) {
349            debug_printf(" %s[%u].%c",
350                         tgsi_file_names[chan_info->file],
351                         chan_info->u.index,
352                         "xyzw01"[chan_info->swizzle]);
353         } else {
354            debug_printf(" _");
355         }
356      }
357      debug_printf(", SAMP[%u], %s\n",
358                   tex_info->unit,
359                   tgsi_texture_names[tex_info->target]);
360   }
361
362   for (index = 0; index < PIPE_MAX_SHADER_OUTPUTS; ++index) {
363      for (chan = 0; chan < 4; ++chan) {
364         const struct lp_tgsi_channel_info *chan_info =
365               &info->output[index][chan];
366         if (chan_info->file != TGSI_FILE_NULL) {
367            debug_printf("OUT[%u].%c = ", index, "xyzw"[chan]);
368            if (chan_info->file == TGSI_FILE_IMMEDIATE) {
369               debug_printf("%f", chan_info->u.value);
370            } else {
371               const char *file_name;
372               switch (chan_info->file) {
373               case TGSI_FILE_CONSTANT:
374                  file_name = "CONST";
375                  break;
376               case TGSI_FILE_INPUT:
377                  file_name = "IN";
378                  break;
379               default:
380                  file_name = "???";
381                  break;
382               }
383               debug_printf("%s[%u].%c",
384                            file_name,
385                            chan_info->u.index,
386                            "xyzw01"[chan_info->swizzle]);
387            }
388            debug_printf("\n");
389         }
390      }
391   }
392}
393
394
395/**
396 * Detect any direct relationship between the output color
397 */
398void
399lp_build_tgsi_info(const struct tgsi_token *tokens,
400                   struct lp_tgsi_info *info)
401{
402   struct tgsi_parse_context parse;
403   struct analysis_context ctx;
404   unsigned index;
405   unsigned chan;
406
407   memset(info, 0, sizeof *info);
408
409   tgsi_scan_shader(tokens, &info->base);
410
411   memset(&ctx, 0, sizeof ctx);
412   ctx.info = info;
413
414   tgsi_parse_init(&parse, tokens);
415
416   while (!tgsi_parse_end_of_tokens(&parse)) {
417      tgsi_parse_token(&parse);
418
419      switch (parse.FullToken.Token.Type) {
420      case TGSI_TOKEN_TYPE_DECLARATION:
421         break;
422
423      case TGSI_TOKEN_TYPE_INSTRUCTION:
424         {
425            struct tgsi_full_instruction *inst =
426                  &parse.FullToken.FullInstruction;
427
428            if (inst->Instruction.Opcode == TGSI_OPCODE_END ||
429                inst->Instruction.Opcode == TGSI_OPCODE_BGNSUB) {
430               /* We reached the end of main function body. */
431               goto finished;
432            }
433
434            analyse_instruction(&ctx, inst);
435         }
436         break;
437
438      case TGSI_TOKEN_TYPE_IMMEDIATE:
439         {
440            const unsigned size =
441                  parse.FullToken.FullImmediate.Immediate.NrTokens - 1;
442            assert(size <= 4);
443            if (ctx.num_imms < Elements(ctx.imm)) {
444               for (chan = 0; chan < size; ++chan) {
445                  float value = parse.FullToken.FullImmediate.u[chan].Float;
446                  ctx.imm[ctx.num_imms][chan] = value;
447
448                  if (value < 0.0f || value > 1.0f) {
449                     info->unclamped_immediates = TRUE;
450                  }
451               }
452               ++ctx.num_imms;
453            }
454         }
455         break;
456
457      case TGSI_TOKEN_TYPE_PROPERTY:
458         break;
459
460      default:
461         assert(0);
462      }
463   }
464finished:
465
466   tgsi_parse_free(&parse);
467
468
469   /*
470    * Link the output color values.
471    */
472
473   for (index = 0; index < PIPE_MAX_COLOR_BUFS; ++index) {
474      const struct lp_tgsi_channel_info null_output[4];
475      info->cbuf[index] = null_output;
476   }
477
478   for (index = 0; index < info->base.num_outputs; ++index) {
479      unsigned semantic_name = info->base.output_semantic_name[index];
480      unsigned semantic_index = info->base.output_semantic_index[index];
481      if (semantic_name == TGSI_SEMANTIC_COLOR &&
482          semantic_index < PIPE_MAX_COLOR_BUFS) {
483         info->cbuf[semantic_index] = info->output[index];
484      }
485   }
486
487   if (gallivm_debug & GALLIVM_DEBUG_TGSI) {
488      dump_info(tokens, info);
489   }
490}
491