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