• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "util/log.h"
36 
37 #include "tgsi_transform.h"
38 
39 /**
40  * Increments the next-token index if the tgsi_build_* succeeded, or extends the
41  * token array and returns true to request a re-emit of the tgsi_build_* by the
42  * caller.
43  */
44 static bool
need_re_emit(struct tgsi_transform_context * ctx,uint32_t emitted,struct tgsi_header orig_header)45 need_re_emit(struct tgsi_transform_context *ctx, uint32_t emitted, struct tgsi_header orig_header)
46 {
47    if (emitted > 0) {
48       ctx->ti += emitted;
49       return false;
50    } else {
51       uint32_t new_len = ctx->max_tokens_out * 2;
52       if (new_len < ctx->max_tokens_out) {
53          ctx->fail = true;
54          return false;
55       }
56 
57       struct tgsi_token *new_tokens = tgsi_alloc_tokens(new_len);
58       if (!new_tokens) {
59          ctx->fail = true;
60          return false;
61       }
62       memcpy(new_tokens, ctx->tokens_out, sizeof(struct tgsi_token) * ctx->ti);
63 
64       tgsi_free_tokens(ctx->tokens_out);
65       ctx->tokens_out = new_tokens;
66       ctx->max_tokens_out = new_len;
67 
68       /* Point the header at the resized tokens. */
69       ctx->header = (struct tgsi_header *)new_tokens;
70       /* The failing emit may have incremented header/body size, reset it to its state before our attempt. */
71       *ctx->header = orig_header;
72 
73       return true;
74    }
75 }
76 
77 static void
emit_instruction(struct tgsi_transform_context * ctx,const struct tgsi_full_instruction * inst)78 emit_instruction(struct tgsi_transform_context *ctx,
79                  const struct tgsi_full_instruction *inst)
80 {
81    uint32_t emitted;
82    struct tgsi_header orig_header = *ctx->header;
83 
84    do {
85       emitted = tgsi_build_full_instruction(inst,
86                                             ctx->tokens_out + ctx->ti,
87                                             ctx->header,
88                                             ctx->max_tokens_out - ctx->ti);
89    } while (need_re_emit(ctx, emitted, orig_header));
90 }
91 
92 
93 static void
emit_declaration(struct tgsi_transform_context * ctx,const struct tgsi_full_declaration * decl)94 emit_declaration(struct tgsi_transform_context *ctx,
95                  const struct tgsi_full_declaration *decl)
96 {
97    uint32_t emitted;
98    struct tgsi_header orig_header = *ctx->header;
99 
100    do {
101       emitted = tgsi_build_full_declaration(decl,
102                                             ctx->tokens_out + ctx->ti,
103                                             ctx->header,
104                                             ctx->max_tokens_out - ctx->ti);
105    } while (need_re_emit(ctx, emitted, orig_header));
106 }
107 
108 
109 static void
emit_immediate(struct tgsi_transform_context * ctx,const struct tgsi_full_immediate * imm)110 emit_immediate(struct tgsi_transform_context *ctx,
111                const struct tgsi_full_immediate *imm)
112 {
113    uint32_t emitted;
114    struct tgsi_header orig_header = *ctx->header;
115 
116    do {
117       emitted = tgsi_build_full_immediate(imm,
118                                           ctx->tokens_out + ctx->ti,
119                                           ctx->header,
120                                           ctx->max_tokens_out - ctx->ti);
121    } while (need_re_emit(ctx, emitted, orig_header));
122 }
123 
124 
125 static void
emit_property(struct tgsi_transform_context * ctx,const struct tgsi_full_property * prop)126 emit_property(struct tgsi_transform_context *ctx,
127               const struct tgsi_full_property *prop)
128 {
129    uint32_t emitted;
130    struct tgsi_header orig_header = *ctx->header;
131 
132    do {
133       emitted = tgsi_build_full_property(prop,
134                                          ctx->tokens_out + ctx->ti,
135                                          ctx->header,
136                                          ctx->max_tokens_out - ctx->ti);
137    } while (need_re_emit(ctx, emitted, orig_header));
138 }
139 
140 
141 /**
142  * Apply user-defined transformations to the input shader to produce
143  * the output shader.
144  * For example, a register search-and-replace operation could be applied
145  * by defining a transform_instruction() callback that examined and changed
146  * the instruction src/dest regs.
147  *
148  * \return new tgsi tokens, or NULL on failure
149  */
150 struct tgsi_token *
tgsi_transform_shader(const struct tgsi_token * tokens_in,uint initial_tokens_len,struct tgsi_transform_context * ctx)151 tgsi_transform_shader(const struct tgsi_token *tokens_in,
152                       uint initial_tokens_len,
153                       struct tgsi_transform_context *ctx)
154 {
155    boolean first_instruction = TRUE;
156    boolean epilog_emitted = FALSE;
157    int cond_stack = 0;
158    int call_stack = 0;
159 
160    /* input shader */
161    struct tgsi_parse_context parse;
162 
163    /* output shader */
164    struct tgsi_processor *processor;
165 
166    /* Always include space for the header. */
167    initial_tokens_len = MAX2(initial_tokens_len, 2);
168 
169    /**
170     ** callback context init
171     **/
172    ctx->emit_instruction = emit_instruction;
173    ctx->emit_declaration = emit_declaration;
174    ctx->emit_immediate = emit_immediate;
175    ctx->emit_property = emit_property;
176    ctx->tokens_out = tgsi_alloc_tokens(initial_tokens_len);
177    ctx->max_tokens_out = initial_tokens_len;
178    ctx->fail = false;
179 
180    if (!ctx->tokens_out) {
181       mesa_loge("failed to allocate %d tokens\n", initial_tokens_len);
182       return NULL;
183    }
184 
185    /**
186     ** Setup to begin parsing input shader
187     **/
188    if (tgsi_parse_init( &parse, tokens_in ) != TGSI_PARSE_OK) {
189       debug_printf("tgsi_parse_init() failed in tgsi_transform_shader()!\n");
190       return NULL;
191    }
192    ctx->processor = parse.FullHeader.Processor.Processor;
193 
194    /**
195     **  Setup output shader
196     **/
197    ctx->header = (struct tgsi_header *)ctx->tokens_out;
198    *ctx->header = tgsi_build_header();
199 
200    processor = (struct tgsi_processor *) (ctx->tokens_out + 1);
201    *processor = tgsi_build_processor( ctx->processor, ctx->header );
202 
203    ctx->ti = 2;
204 
205 
206    /**
207     ** Loop over incoming program tokens/instructions
208     */
209    while( !tgsi_parse_end_of_tokens( &parse ) ) {
210 
211       tgsi_parse_token( &parse );
212 
213       switch( parse.FullToken.Token.Type ) {
214       case TGSI_TOKEN_TYPE_INSTRUCTION:
215          {
216             struct tgsi_full_instruction *fullinst
217                = &parse.FullToken.FullInstruction;
218             enum tgsi_opcode opcode = fullinst->Instruction.Opcode;
219 
220             if (first_instruction && ctx->prolog) {
221                ctx->prolog(ctx);
222             }
223 
224             /*
225              * XXX Note: we handle the case of ret in main.
226              * However, the output redirections done by transform
227              * have their limits with control flow and will generally
228              * not work correctly. e.g.
229              * if (cond) {
230              *    oColor = x;
231              *    ret;
232              * }
233              * oColor = y;
234              * end;
235              * If the color output is redirected to a temp and modified
236              * by a transform, this will not work (the oColor assignment
237              * in the conditional will never make it to the actual output).
238              */
239             if ((opcode == TGSI_OPCODE_END || opcode == TGSI_OPCODE_RET) &&
240                  call_stack == 0 && ctx->epilog && !epilog_emitted) {
241                if (opcode == TGSI_OPCODE_RET && cond_stack != 0) {
242                   assert(!"transform ignoring RET in main");
243                } else {
244                   assert(cond_stack == 0);
245                   /* Emit caller's epilog */
246                   ctx->epilog(ctx);
247                   epilog_emitted = TRUE;
248                }
249                /* Emit END (or RET) */
250                ctx->emit_instruction(ctx, fullinst);
251             }
252             else {
253                switch (opcode) {
254                case TGSI_OPCODE_IF:
255                case TGSI_OPCODE_UIF:
256                case TGSI_OPCODE_SWITCH:
257                case TGSI_OPCODE_BGNLOOP:
258                   cond_stack++;
259                   break;
260                case TGSI_OPCODE_CAL:
261                   call_stack++;
262                   break;
263                case TGSI_OPCODE_ENDIF:
264                case TGSI_OPCODE_ENDSWITCH:
265                case TGSI_OPCODE_ENDLOOP:
266                   assert(cond_stack > 0);
267                   cond_stack--;
268                   break;
269                case TGSI_OPCODE_ENDSUB:
270                   assert(call_stack > 0);
271                   call_stack--;
272                   break;
273                case TGSI_OPCODE_BGNSUB:
274                case TGSI_OPCODE_RET:
275                default:
276                   break;
277                }
278                if (ctx->transform_instruction)
279                   ctx->transform_instruction(ctx, fullinst);
280                else
281                   ctx->emit_instruction(ctx, fullinst);
282             }
283 
284             first_instruction = FALSE;
285          }
286          break;
287 
288       case TGSI_TOKEN_TYPE_DECLARATION:
289          {
290             struct tgsi_full_declaration *fulldecl
291                = &parse.FullToken.FullDeclaration;
292 
293             if (ctx->transform_declaration)
294                ctx->transform_declaration(ctx, fulldecl);
295             else
296                ctx->emit_declaration(ctx, fulldecl);
297          }
298          break;
299 
300       case TGSI_TOKEN_TYPE_IMMEDIATE:
301          {
302             struct tgsi_full_immediate *fullimm
303                = &parse.FullToken.FullImmediate;
304 
305             if (ctx->transform_immediate)
306                ctx->transform_immediate(ctx, fullimm);
307             else
308                ctx->emit_immediate(ctx, fullimm);
309          }
310          break;
311       case TGSI_TOKEN_TYPE_PROPERTY:
312          {
313             struct tgsi_full_property *fullprop
314                = &parse.FullToken.FullProperty;
315 
316             if (ctx->transform_property)
317                ctx->transform_property(ctx, fullprop);
318             else
319                ctx->emit_property(ctx, fullprop);
320          }
321          break;
322 
323       default:
324          assert( 0 );
325       }
326    }
327    assert(call_stack == 0);
328 
329    tgsi_parse_free (&parse);
330 
331    if (ctx->fail) {
332       tgsi_free_tokens(ctx->tokens_out);
333       return NULL;
334    }
335 
336    return ctx->tokens_out;
337 }
338 
339 
340 #include "tgsi_text.h"
341 
342 extern int tgsi_transform_foo( struct tgsi_token *tokens_out,
343                                uint max_tokens_out );
344 
345 /* This function exists only so that tgsi_text_translate() doesn't get
346  * magic-ed out of the libtgsi.a archive by the build system.  Don't
347  * remove unless you know this has been fixed - check on mingw/scons
348  * builds as well.
349  */
350 int
tgsi_transform_foo(struct tgsi_token * tokens_out,uint max_tokens_out)351 tgsi_transform_foo( struct tgsi_token *tokens_out,
352                     uint max_tokens_out )
353 {
354    const char *text =
355       "FRAG\n"
356       "DCL IN[0], COLOR, CONSTANT\n"
357       "DCL OUT[0], COLOR\n"
358       "  0: MOV OUT[0], IN[0]\n"
359       "  1: END";
360 
361    return tgsi_text_translate( text,
362                                tokens_out,
363                                max_tokens_out );
364 }
365