tgsi_sanity.c revision ff56a12051a91c5c69db9afb85e4a3ebdb17ef96
1/************************************************************************** 2 * 3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 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 TUNGSTEN GRAPHICS 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#include "util/u_debug.h" 29#include "util/u_memory.h" 30#include "util/u_prim.h" 31#include "cso_cache/cso_hash.h" 32#include "tgsi_sanity.h" 33#include "tgsi_info.h" 34#include "tgsi_iterate.h" 35 36typedef struct { 37 uint file : 28; 38 /* max 2 dimensions */ 39 uint dimensions : 4; 40 uint indices[2]; 41} scan_register; 42 43struct sanity_check_ctx 44{ 45 struct tgsi_iterate_context iter; 46 struct cso_hash *regs_decl; 47 struct cso_hash *regs_used; 48 struct cso_hash *regs_ind_used; 49 50 uint num_imms; 51 uint num_instructions; 52 uint index_of_END; 53 54 uint errors; 55 uint warnings; 56 uint implied_array_size; 57}; 58 59static INLINE unsigned 60scan_register_key(const scan_register *reg) 61{ 62 unsigned key = reg->file; 63 key |= (reg->indices[0] << 4); 64 key |= (reg->indices[1] << 18); 65 66 return key; 67} 68 69static void 70fill_scan_register1d(scan_register *reg, 71 uint file, uint index) 72{ 73 reg->file = file; 74 reg->dimensions = 1; 75 reg->indices[0] = index; 76 reg->indices[1] = 0; 77} 78 79static void 80fill_scan_register2d(scan_register *reg, 81 uint file, uint index1, uint index2) 82{ 83 reg->file = file; 84 reg->dimensions = 2; 85 reg->indices[0] = index1; 86 reg->indices[1] = index2; 87} 88 89static void 90scan_register_dst(scan_register *reg, 91 struct tgsi_full_dst_register *dst) 92{ 93 fill_scan_register1d(reg, 94 dst->Register.File, 95 dst->Register.Index); 96} 97 98static void 99scan_register_src(scan_register *reg, 100 struct tgsi_full_src_register *src) 101{ 102 if (src->Register.Dimension) { 103 /*FIXME: right now we don't support indirect 104 * multidimensional addressing */ 105 debug_assert(!src->Dimension.Indirect); 106 fill_scan_register2d(reg, 107 src->Register.File, 108 src->Register.Index, 109 src->Dimension.Index); 110 } else { 111 fill_scan_register1d(reg, 112 src->Register.File, 113 src->Register.Index); 114 } 115} 116 117static scan_register * 118create_scan_register_src(struct tgsi_full_src_register *src) 119{ 120 scan_register *reg = MALLOC(sizeof(scan_register)); 121 scan_register_src(reg, src); 122 123 return reg; 124} 125 126static scan_register * 127create_scan_register_dst(struct tgsi_full_dst_register *dst) 128{ 129 scan_register *reg = MALLOC(sizeof(scan_register)); 130 scan_register_dst(reg, dst); 131 132 return reg; 133} 134 135static void 136report_error( 137 struct sanity_check_ctx *ctx, 138 const char *format, 139 ... ) 140{ 141 va_list args; 142 143 debug_printf( "Error : " ); 144 va_start( args, format ); 145 _debug_vprintf( format, args ); 146 va_end( args ); 147 debug_printf( "\n" ); 148 ctx->errors++; 149} 150 151static void 152report_warning( 153 struct sanity_check_ctx *ctx, 154 const char *format, 155 ... ) 156{ 157 va_list args; 158 159 debug_printf( "Warning: " ); 160 va_start( args, format ); 161 _debug_vprintf( format, args ); 162 va_end( args ); 163 debug_printf( "\n" ); 164 ctx->warnings++; 165} 166 167static boolean 168check_file_name( 169 struct sanity_check_ctx *ctx, 170 uint file ) 171{ 172 if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) { 173 report_error( ctx, "(%u): Invalid register file name", file ); 174 return FALSE; 175 } 176 return TRUE; 177} 178 179static boolean 180is_register_declared( 181 struct sanity_check_ctx *ctx, 182 const scan_register *reg) 183{ 184 void *data = cso_hash_find_data_from_template( 185 ctx->regs_decl, scan_register_key(reg), 186 (void*)reg, sizeof(scan_register)); 187 return data ? TRUE : FALSE; 188} 189 190static boolean 191is_any_register_declared( 192 struct sanity_check_ctx *ctx, 193 uint file ) 194{ 195 struct cso_hash_iter iter = 196 cso_hash_first_node(ctx->regs_decl); 197 198 while (cso_hash_iter_is_null(iter)) { 199 scan_register *reg = (scan_register *)cso_hash_iter_data(iter); 200 if (reg->file == file) 201 return TRUE; 202 iter = cso_hash_iter_next(iter); 203 } 204 205 return FALSE; 206} 207 208static boolean 209is_register_used( 210 struct sanity_check_ctx *ctx, 211 scan_register *reg) 212{ 213 void *data = cso_hash_find_data_from_template( 214 ctx->regs_used, scan_register_key(reg), 215 reg, sizeof(scan_register)); 216 return data ? TRUE : FALSE; 217} 218 219 220static boolean 221is_ind_register_used( 222 struct sanity_check_ctx *ctx, 223 scan_register *reg) 224{ 225 return cso_hash_contains(ctx->regs_ind_used, reg->file); 226} 227 228static const char *file_names[TGSI_FILE_COUNT] = 229{ 230 "NULL", 231 "CONST", 232 "IN", 233 "OUT", 234 "TEMP", 235 "SAMP", 236 "ADDR", 237 "IMM", 238 "LOOP", 239 "PRED" 240}; 241 242static boolean 243check_register_usage( 244 struct sanity_check_ctx *ctx, 245 scan_register *reg, 246 const char *name, 247 boolean indirect_access ) 248{ 249 if (!check_file_name( ctx, reg->file )) { 250 free(reg); 251 return FALSE; 252 } 253 254 if (indirect_access) { 255 /* Note that 'index' is an offset relative to the value of the 256 * address register. No range checking done here.*/ 257 reg->indices[0] = 0; 258 reg->indices[1] = 0; 259 if (!is_any_register_declared( ctx, reg->file )) 260 report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name ); 261 if (!is_ind_register_used(ctx, reg)) 262 cso_hash_insert(ctx->regs_ind_used, reg->file, reg); 263 else 264 free(reg); 265 } 266 else { 267 if (!is_register_declared( ctx, reg )) { 268 if (reg->dimensions == 2) 269 report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file], 270 reg->indices[0], reg->indices[1], name ); 271 else 272 report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file], 273 reg->indices[0], name ); 274 } 275 if (!is_register_used( ctx, reg )) 276 cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg); 277 else 278 free(reg); 279 } 280 return TRUE; 281} 282 283static boolean 284iter_instruction( 285 struct tgsi_iterate_context *iter, 286 struct tgsi_full_instruction *inst ) 287{ 288 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 289 const struct tgsi_opcode_info *info; 290 uint i; 291 292 if (inst->Instruction.Opcode == TGSI_OPCODE_END) { 293 if (ctx->index_of_END != ~0) { 294 report_error( ctx, "Too many END instructions" ); 295 } 296 ctx->index_of_END = ctx->num_instructions; 297 } 298 299 info = tgsi_get_opcode_info( inst->Instruction.Opcode ); 300 if (info == NULL) { 301 report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode ); 302 return TRUE; 303 } 304 305 if (info->num_dst != inst->Instruction.NumDstRegs) { 306 report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst ); 307 } 308 if (info->num_src != inst->Instruction.NumSrcRegs) { 309 report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src ); 310 } 311 312 /* Check destination and source registers' validity. 313 * Mark the registers as used. 314 */ 315 for (i = 0; i < inst->Instruction.NumDstRegs; i++) { 316 scan_register *reg = create_scan_register_dst(&inst->Dst[i]); 317 check_register_usage( 318 ctx, 319 reg, 320 "destination", 321 FALSE ); 322 } 323 for (i = 0; i < inst->Instruction.NumSrcRegs; i++) { 324 scan_register *reg = create_scan_register_src(&inst->Src[i]); 325 check_register_usage( 326 ctx, 327 reg, 328 "source", 329 (boolean)inst->Src[i].Register.Indirect ); 330 if (inst->Src[i].Register.Indirect) { 331 scan_register *ind_reg = MALLOC(sizeof(scan_register)); 332 333 fill_scan_register1d(ind_reg, 334 inst->Src[i].Indirect.File, 335 inst->Src[i].Indirect.Index); 336 check_register_usage( 337 ctx, 338 reg, 339 "indirect", 340 FALSE ); 341 if (!(reg->file == TGSI_FILE_ADDRESS || reg->file == TGSI_FILE_LOOP) || 342 reg->indices[0] != 0) { 343 report_warning(ctx, "Indirect register neither ADDR[0] nor LOOP[0]"); 344 } 345 } 346 } 347 348 switch (inst->Instruction.Opcode) { 349 case TGSI_OPCODE_BGNFOR: 350 case TGSI_OPCODE_ENDFOR: 351 if (inst->Dst[0].Register.File != TGSI_FILE_LOOP || 352 inst->Dst[0].Register.Index != 0) { 353 report_error(ctx, "Destination register must be LOOP[0]"); 354 } 355 break; 356 } 357 358 switch (inst->Instruction.Opcode) { 359 case TGSI_OPCODE_BGNFOR: 360 if (inst->Src[0].Register.File != TGSI_FILE_CONSTANT && 361 inst->Src[0].Register.File != TGSI_FILE_IMMEDIATE) { 362 report_error(ctx, "Source register file must be either CONST or IMM"); 363 } 364 break; 365 } 366 367 ctx->num_instructions++; 368 369 return TRUE; 370} 371 372static void 373check_and_declare(struct sanity_check_ctx *ctx, 374 scan_register *reg) 375{ 376 if (is_register_declared( ctx, reg)) 377 report_error( ctx, "%s[%u]: The same register declared more than once", 378 file_names[reg->file], reg->indices[0] ); 379 cso_hash_insert(ctx->regs_decl, 380 scan_register_key(reg), 381 reg); 382} 383 384 385static boolean 386iter_declaration( 387 struct tgsi_iterate_context *iter, 388 struct tgsi_full_declaration *decl ) 389{ 390 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 391 uint file; 392 uint i; 393 394 /* No declarations allowed after the first instruction. 395 */ 396 if (ctx->num_instructions > 0) 397 report_error( ctx, "Instruction expected but declaration found" ); 398 399 /* Check registers' validity. 400 * Mark the registers as declared. 401 */ 402 file = decl->Declaration.File; 403 if (!check_file_name( ctx, file )) 404 return TRUE; 405 for (i = decl->Range.First; i <= decl->Range.Last; i++) { 406 /* declared TGSI_FILE_INPUT's for geometry processor 407 * have an implied second dimension */ 408 if (file == TGSI_FILE_INPUT && 409 ctx->iter.processor.Processor == TGSI_PROCESSOR_GEOMETRY) { 410 uint vert; 411 for (vert = 0; vert < ctx->implied_array_size; ++vert) { 412 scan_register *reg = MALLOC(sizeof(scan_register)); 413 fill_scan_register2d(reg, file, vert, i); 414 check_and_declare(ctx, reg); 415 } 416 } else { 417 scan_register *reg = MALLOC(sizeof(scan_register)); 418 fill_scan_register1d(reg, file, i); 419 check_and_declare(ctx, reg); 420 } 421 } 422 423 return TRUE; 424} 425 426static boolean 427iter_immediate( 428 struct tgsi_iterate_context *iter, 429 struct tgsi_full_immediate *imm ) 430{ 431 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 432 scan_register *reg; 433 434 /* No immediates allowed after the first instruction. 435 */ 436 if (ctx->num_instructions > 0) 437 report_error( ctx, "Instruction expected but immediate found" ); 438 439 /* Mark the register as declared. 440 */ 441 reg = MALLOC(sizeof(scan_register)); 442 fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms); 443 cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg); 444 ctx->num_imms++; 445 446 /* Check data type validity. 447 */ 448 if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 && 449 imm->Immediate.DataType != TGSI_IMM_UINT32 && 450 imm->Immediate.DataType != TGSI_IMM_INT32) { 451 report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType ); 452 return TRUE; 453 } 454 455 return TRUE; 456} 457 458 459static boolean 460iter_property( 461 struct tgsi_iterate_context *iter, 462 struct tgsi_full_property *prop ) 463{ 464 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 465 466 if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY && 467 prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) { 468 ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data); 469 } 470 return TRUE; 471} 472 473static boolean 474epilog( 475 struct tgsi_iterate_context *iter ) 476{ 477 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 478 479 /* There must be an END instruction somewhere. 480 */ 481 if (ctx->index_of_END == ~0) { 482 report_error( ctx, "Missing END instruction" ); 483 } 484 485 /* Check if all declared registers were used. 486 */ 487 { 488 struct cso_hash_iter iter = 489 cso_hash_first_node(ctx->regs_decl); 490 491 while (cso_hash_iter_is_null(iter)) { 492 scan_register *reg = (scan_register *)cso_hash_iter_data(iter); 493 if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) { 494 report_warning( ctx, "%s[%u]: Register never used", 495 file_names[reg->file], reg->indices[0] ); 496 } 497 iter = cso_hash_iter_next(iter); 498 } 499 } 500 501 /* Print totals, if any. 502 */ 503 if (ctx->errors || ctx->warnings) 504 debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings ); 505 506 return TRUE; 507} 508 509static void 510regs_hash_destroy(struct cso_hash *hash) 511{ 512 struct cso_hash_iter iter = cso_hash_first_node(hash); 513 while (!cso_hash_iter_is_null(iter)) { 514 scan_register *reg = (scan_register *)cso_hash_iter_data(iter); 515 iter = cso_hash_erase(hash, iter); 516 free(reg); 517 } 518 cso_hash_delete(hash); 519} 520 521boolean 522tgsi_sanity_check( 523 const struct tgsi_token *tokens ) 524{ 525 struct sanity_check_ctx ctx; 526 527 ctx.iter.prolog = NULL; 528 ctx.iter.iterate_instruction = iter_instruction; 529 ctx.iter.iterate_declaration = iter_declaration; 530 ctx.iter.iterate_immediate = iter_immediate; 531 ctx.iter.iterate_property = iter_property; 532 ctx.iter.epilog = epilog; 533 534 ctx.regs_decl = cso_hash_create(); 535 ctx.regs_used = cso_hash_create(); 536 ctx.regs_ind_used = cso_hash_create(); 537 538 ctx.num_imms = 0; 539 ctx.num_instructions = 0; 540 ctx.index_of_END = ~0; 541 542 ctx.errors = 0; 543 ctx.warnings = 0; 544 ctx.implied_array_size = 0; 545 546 if (!tgsi_iterate_shader( tokens, &ctx.iter )) 547 return FALSE; 548 549 regs_hash_destroy(ctx.regs_decl); 550 regs_hash_destroy(ctx.regs_used); 551 regs_hash_destroy(ctx.regs_ind_used); 552 return ctx.errors == 0; 553} 554