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