tgsi_sanity.c revision 7472cd0f1f0f8284729274bc5c453d59d9ab4b55
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 } 272 else { 273 report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file], 274 reg->indices[0], name ); 275 } 276 } 277 if (!is_register_used( ctx, reg )) 278 cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg); 279 else 280 FREE(reg); 281 } 282 return TRUE; 283} 284 285static boolean 286iter_instruction( 287 struct tgsi_iterate_context *iter, 288 struct tgsi_full_instruction *inst ) 289{ 290 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 291 const struct tgsi_opcode_info *info; 292 uint i; 293 294 if (inst->Instruction.Opcode == TGSI_OPCODE_END) { 295 if (ctx->index_of_END != ~0) { 296 report_error( ctx, "Too many END instructions" ); 297 } 298 ctx->index_of_END = ctx->num_instructions; 299 } 300 301 info = tgsi_get_opcode_info( inst->Instruction.Opcode ); 302 if (info == NULL) { 303 report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode ); 304 return TRUE; 305 } 306 307 if (info->num_dst != inst->Instruction.NumDstRegs) { 308 report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst ); 309 } 310 if (info->num_src != inst->Instruction.NumSrcRegs) { 311 report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src ); 312 } 313 314 /* Check destination and source registers' validity. 315 * Mark the registers as used. 316 */ 317 for (i = 0; i < inst->Instruction.NumDstRegs; i++) { 318 scan_register *reg = create_scan_register_dst(&inst->Dst[i]); 319 check_register_usage( 320 ctx, 321 reg, 322 "destination", 323 FALSE ); 324 } 325 for (i = 0; i < inst->Instruction.NumSrcRegs; i++) { 326 scan_register *reg = create_scan_register_src(&inst->Src[i]); 327 check_register_usage( 328 ctx, 329 reg, 330 "source", 331 (boolean)inst->Src[i].Register.Indirect ); 332 if (inst->Src[i].Register.Indirect) { 333 scan_register *ind_reg = MALLOC(sizeof(scan_register)); 334 335 fill_scan_register1d(ind_reg, 336 inst->Src[i].Indirect.File, 337 inst->Src[i].Indirect.Index); 338 check_register_usage( 339 ctx, 340 ind_reg, 341 "indirect", 342 FALSE ); 343 } 344 } 345 346 switch (inst->Instruction.Opcode) { 347 case TGSI_OPCODE_BGNFOR: 348 case TGSI_OPCODE_ENDFOR: 349 if (inst->Dst[0].Register.File != TGSI_FILE_LOOP || 350 inst->Dst[0].Register.Index != 0) { 351 report_error(ctx, "Destination register must be LOOP[0]"); 352 } 353 break; 354 } 355 356 switch (inst->Instruction.Opcode) { 357 case TGSI_OPCODE_BGNFOR: 358 if (inst->Src[0].Register.File != TGSI_FILE_CONSTANT && 359 inst->Src[0].Register.File != TGSI_FILE_IMMEDIATE) { 360 report_error(ctx, "Source register file must be either CONST or IMM"); 361 } 362 break; 363 } 364 365 ctx->num_instructions++; 366 367 return TRUE; 368} 369 370static void 371check_and_declare(struct sanity_check_ctx *ctx, 372 scan_register *reg) 373{ 374 if (is_register_declared( ctx, reg)) 375 report_error( ctx, "%s[%u]: The same register declared more than once", 376 file_names[reg->file], reg->indices[0] ); 377 cso_hash_insert(ctx->regs_decl, 378 scan_register_key(reg), 379 reg); 380} 381 382 383static boolean 384iter_declaration( 385 struct tgsi_iterate_context *iter, 386 struct tgsi_full_declaration *decl ) 387{ 388 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 389 uint file; 390 uint i; 391 392 /* No declarations allowed after the first instruction. 393 */ 394 if (ctx->num_instructions > 0) 395 report_error( ctx, "Instruction expected but declaration found" ); 396 397 /* Check registers' validity. 398 * Mark the registers as declared. 399 */ 400 file = decl->Declaration.File; 401 if (!check_file_name( ctx, file )) 402 return TRUE; 403 for (i = decl->Range.First; i <= decl->Range.Last; i++) { 404 /* declared TGSI_FILE_INPUT's for geometry processor 405 * have an implied second dimension */ 406 if (file == TGSI_FILE_INPUT && 407 ctx->iter.processor.Processor == TGSI_PROCESSOR_GEOMETRY) { 408 uint vert; 409 for (vert = 0; vert < ctx->implied_array_size; ++vert) { 410 scan_register *reg = MALLOC(sizeof(scan_register)); 411 fill_scan_register2d(reg, file, i, vert); 412 check_and_declare(ctx, reg); 413 } 414 } else { 415 scan_register *reg = MALLOC(sizeof(scan_register)); 416 fill_scan_register1d(reg, file, i); 417 check_and_declare(ctx, reg); 418 } 419 } 420 421 return TRUE; 422} 423 424static boolean 425iter_immediate( 426 struct tgsi_iterate_context *iter, 427 struct tgsi_full_immediate *imm ) 428{ 429 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 430 scan_register *reg; 431 432 /* No immediates allowed after the first instruction. 433 */ 434 if (ctx->num_instructions > 0) 435 report_error( ctx, "Instruction expected but immediate found" ); 436 437 /* Mark the register as declared. 438 */ 439 reg = MALLOC(sizeof(scan_register)); 440 fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms); 441 cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg); 442 ctx->num_imms++; 443 444 /* Check data type validity. 445 */ 446 if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 && 447 imm->Immediate.DataType != TGSI_IMM_UINT32 && 448 imm->Immediate.DataType != TGSI_IMM_INT32) { 449 report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType ); 450 return TRUE; 451 } 452 453 return TRUE; 454} 455 456 457static boolean 458iter_property( 459 struct tgsi_iterate_context *iter, 460 struct tgsi_full_property *prop ) 461{ 462 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 463 464 if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY && 465 prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) { 466 ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data); 467 } 468 return TRUE; 469} 470 471static boolean 472epilog( 473 struct tgsi_iterate_context *iter ) 474{ 475 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; 476 477 /* There must be an END instruction somewhere. 478 */ 479 if (ctx->index_of_END == ~0) { 480 report_error( ctx, "Missing END instruction" ); 481 } 482 483 /* Check if all declared registers were used. 484 */ 485 { 486 struct cso_hash_iter iter = 487 cso_hash_first_node(ctx->regs_decl); 488 489 while (!cso_hash_iter_is_null(iter)) { 490 scan_register *reg = (scan_register *)cso_hash_iter_data(iter); 491 if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) { 492 report_warning( ctx, "%s[%u]: Register never used", 493 file_names[reg->file], reg->indices[0] ); 494 } 495 iter = cso_hash_iter_next(iter); 496 } 497 } 498 499 /* Print totals, if any. 500 */ 501 if (ctx->errors || ctx->warnings) 502 debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings ); 503 504 return TRUE; 505} 506 507static void 508regs_hash_destroy(struct cso_hash *hash) 509{ 510 struct cso_hash_iter iter = cso_hash_first_node(hash); 511 while (!cso_hash_iter_is_null(iter)) { 512 scan_register *reg = (scan_register *)cso_hash_iter_data(iter); 513 iter = cso_hash_erase(hash, iter); 514 assert(reg->file < TGSI_FILE_COUNT); 515 FREE(reg); 516 } 517 cso_hash_delete(hash); 518} 519 520boolean 521tgsi_sanity_check( 522 const struct tgsi_token *tokens ) 523{ 524 struct sanity_check_ctx ctx; 525 526 ctx.iter.prolog = NULL; 527 ctx.iter.iterate_instruction = iter_instruction; 528 ctx.iter.iterate_declaration = iter_declaration; 529 ctx.iter.iterate_immediate = iter_immediate; 530 ctx.iter.iterate_property = iter_property; 531 ctx.iter.epilog = epilog; 532 533 ctx.regs_decl = cso_hash_create(); 534 ctx.regs_used = cso_hash_create(); 535 ctx.regs_ind_used = cso_hash_create(); 536 537 ctx.num_imms = 0; 538 ctx.num_instructions = 0; 539 ctx.index_of_END = ~0; 540 541 ctx.errors = 0; 542 ctx.warnings = 0; 543 ctx.implied_array_size = 0; 544 545 if (!tgsi_iterate_shader( tokens, &ctx.iter )) 546 return FALSE; 547 548 regs_hash_destroy(ctx.regs_decl); 549 regs_hash_destroy(ctx.regs_used); 550 regs_hash_destroy(ctx.regs_ind_used); 551 return ctx.errors == 0; 552} 553