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 */ 44static bool 45need_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 77static void 78emit_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 93static void 94emit_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 109static void 110emit_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 125static void 126emit_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 */ 150struct tgsi_token * 151tgsi_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 342extern 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 */ 350int 351tgsi_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