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