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