1/**************************************************************************
2 *
3 * Copyright 2008 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 above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * TGSI program transformation utility.
30 *
31 * Authors:  Brian Paul
32 */
33
34#include "util/u_debug.h"
35
36#include "tgsi_transform.h"
37
38
39
40static void
41emit_instruction(struct tgsi_transform_context *ctx,
42                 const struct tgsi_full_instruction *inst)
43{
44   uint ti = ctx->ti;
45
46   ti += tgsi_build_full_instruction(inst,
47                                     ctx->tokens_out + ti,
48                                     ctx->header,
49                                     ctx->max_tokens_out - ti);
50   ctx->ti = ti;
51}
52
53
54static void
55emit_declaration(struct tgsi_transform_context *ctx,
56                 const struct tgsi_full_declaration *decl)
57{
58   uint ti = ctx->ti;
59
60   ti += tgsi_build_full_declaration(decl,
61                                     ctx->tokens_out + ti,
62                                     ctx->header,
63                                     ctx->max_tokens_out - ti);
64   ctx->ti = ti;
65}
66
67
68static void
69emit_immediate(struct tgsi_transform_context *ctx,
70               const struct tgsi_full_immediate *imm)
71{
72   uint ti = ctx->ti;
73
74   ti += tgsi_build_full_immediate(imm,
75                                   ctx->tokens_out + ti,
76                                   ctx->header,
77                                   ctx->max_tokens_out - ti);
78   ctx->ti = ti;
79}
80
81
82static void
83emit_property(struct tgsi_transform_context *ctx,
84              const struct tgsi_full_property *prop)
85{
86   uint ti = ctx->ti;
87
88   ti += tgsi_build_full_property(prop,
89                                  ctx->tokens_out + ti,
90                                  ctx->header,
91                                  ctx->max_tokens_out - ti);
92   ctx->ti = ti;
93}
94
95
96/**
97 * Apply user-defined transformations to the input shader to produce
98 * the output shader.
99 * For example, a register search-and-replace operation could be applied
100 * by defining a transform_instruction() callback that examined and changed
101 * the instruction src/dest regs.
102 *
103 * \return number of tokens emitted
104 */
105int
106tgsi_transform_shader(const struct tgsi_token *tokens_in,
107                      struct tgsi_token *tokens_out,
108                      uint max_tokens_out,
109                      struct tgsi_transform_context *ctx)
110{
111   uint procType;
112   boolean first_instruction = TRUE;
113   boolean epilog_emitted = FALSE;
114   int cond_stack = 0;
115   int call_stack = 0;
116
117   /* input shader */
118   struct tgsi_parse_context parse;
119
120   /* output shader */
121   struct tgsi_processor *processor;
122
123
124   /**
125    ** callback context init
126    **/
127   ctx->emit_instruction = emit_instruction;
128   ctx->emit_declaration = emit_declaration;
129   ctx->emit_immediate = emit_immediate;
130   ctx->emit_property = emit_property;
131   ctx->tokens_out = tokens_out;
132   ctx->max_tokens_out = max_tokens_out;
133
134
135   /**
136    ** Setup to begin parsing input shader
137    **/
138   if (tgsi_parse_init( &parse, tokens_in ) != TGSI_PARSE_OK) {
139      debug_printf("tgsi_parse_init() failed in tgsi_transform_shader()!\n");
140      return -1;
141   }
142   procType = parse.FullHeader.Processor.Processor;
143
144   /**
145    **  Setup output shader
146    **/
147   ctx->header = (struct tgsi_header *)tokens_out;
148   *ctx->header = tgsi_build_header();
149
150   processor = (struct tgsi_processor *) (tokens_out + 1);
151   *processor = tgsi_build_processor( procType, ctx->header );
152
153   ctx->ti = 2;
154
155
156   /**
157    ** Loop over incoming program tokens/instructions
158    */
159   while( !tgsi_parse_end_of_tokens( &parse ) ) {
160
161      tgsi_parse_token( &parse );
162
163      switch( parse.FullToken.Token.Type ) {
164      case TGSI_TOKEN_TYPE_INSTRUCTION:
165         {
166            struct tgsi_full_instruction *fullinst
167               = &parse.FullToken.FullInstruction;
168            enum tgsi_opcode opcode = fullinst->Instruction.Opcode;
169
170            if (first_instruction && ctx->prolog) {
171               ctx->prolog(ctx);
172            }
173
174            /*
175             * XXX Note: we handle the case of ret in main.
176             * However, the output redirections done by transform
177             * have their limits with control flow and will generally
178             * not work correctly. e.g.
179             * if (cond) {
180             *    oColor = x;
181             *    ret;
182             * }
183             * oColor = y;
184             * end;
185             * If the color output is redirected to a temp and modified
186             * by a transform, this will not work (the oColor assignment
187             * in the conditional will never make it to the actual output).
188             */
189            if ((opcode == TGSI_OPCODE_END || opcode == TGSI_OPCODE_RET) &&
190                 call_stack == 0 && ctx->epilog && !epilog_emitted) {
191               if (opcode == TGSI_OPCODE_RET && cond_stack != 0) {
192                  assert(!"transform ignoring RET in main");
193               } else {
194                  assert(cond_stack == 0);
195                  /* Emit caller's epilog */
196                  ctx->epilog(ctx);
197                  epilog_emitted = TRUE;
198               }
199               /* Emit END (or RET) */
200               ctx->emit_instruction(ctx, fullinst);
201            }
202            else {
203               switch (opcode) {
204               case TGSI_OPCODE_IF:
205               case TGSI_OPCODE_UIF:
206               case TGSI_OPCODE_SWITCH:
207               case TGSI_OPCODE_BGNLOOP:
208                  cond_stack++;
209                  break;
210               case TGSI_OPCODE_CAL:
211                  call_stack++;
212                  break;
213               case TGSI_OPCODE_ENDIF:
214               case TGSI_OPCODE_ENDSWITCH:
215               case TGSI_OPCODE_ENDLOOP:
216                  assert(cond_stack > 0);
217                  cond_stack--;
218                  break;
219               case TGSI_OPCODE_ENDSUB:
220                  assert(call_stack > 0);
221                  call_stack--;
222                  break;
223               case TGSI_OPCODE_BGNSUB:
224               case TGSI_OPCODE_RET:
225               default:
226                  break;
227               }
228               if (ctx->transform_instruction)
229                  ctx->transform_instruction(ctx, fullinst);
230               else
231                  ctx->emit_instruction(ctx, fullinst);
232            }
233
234            first_instruction = FALSE;
235         }
236         break;
237
238      case TGSI_TOKEN_TYPE_DECLARATION:
239         {
240            struct tgsi_full_declaration *fulldecl
241               = &parse.FullToken.FullDeclaration;
242
243            if (ctx->transform_declaration)
244               ctx->transform_declaration(ctx, fulldecl);
245            else
246               ctx->emit_declaration(ctx, fulldecl);
247         }
248         break;
249
250      case TGSI_TOKEN_TYPE_IMMEDIATE:
251         {
252            struct tgsi_full_immediate *fullimm
253               = &parse.FullToken.FullImmediate;
254
255            if (ctx->transform_immediate)
256               ctx->transform_immediate(ctx, fullimm);
257            else
258               ctx->emit_immediate(ctx, fullimm);
259         }
260         break;
261      case TGSI_TOKEN_TYPE_PROPERTY:
262         {
263            struct tgsi_full_property *fullprop
264               = &parse.FullToken.FullProperty;
265
266            if (ctx->transform_property)
267               ctx->transform_property(ctx, fullprop);
268            else
269               ctx->emit_property(ctx, fullprop);
270         }
271         break;
272
273      default:
274         assert( 0 );
275      }
276   }
277   assert(call_stack == 0);
278
279   tgsi_parse_free (&parse);
280
281   return ctx->ti;
282}
283
284
285#include "tgsi_text.h"
286
287extern int tgsi_transform_foo( struct tgsi_token *tokens_out,
288                               uint max_tokens_out );
289
290/* This function exists only so that tgsi_text_translate() doesn't get
291 * magic-ed out of the libtgsi.a archive by the build system.  Don't
292 * remove unless you know this has been fixed - check on mingw/scons
293 * builds as well.
294 */
295int
296tgsi_transform_foo( struct tgsi_token *tokens_out,
297                    uint max_tokens_out )
298{
299   const char *text =
300      "FRAG\n"
301      "DCL IN[0], COLOR, CONSTANT\n"
302      "DCL OUT[0], COLOR\n"
303      "  0: MOV OUT[0], IN[0]\n"
304      "  1: END";
305
306   return tgsi_text_translate( text,
307                               tokens_out,
308                               max_tokens_out );
309}
310