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