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