revert 213 commits (to 56092) from the last month. 10 still need work to resolve...
[AROS.git] / workbench / libs / mesa / src / gallium / auxiliary / tgsi / tgsi_sanity.c
blob509c534683789673c8ebd8ee5be65867af8131ac
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:
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.
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.
26 **************************************************************************/
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"
37 DEBUG_GET_ONCE_BOOL_OPTION(print_sanity, "TGSI_PRINT_SANITY", FALSE)
40 typedef struct {
41 uint file : 28;
42 /* max 2 dimensions */
43 uint dimensions : 4;
44 uint indices[2];
45 } scan_register;
47 struct sanity_check_ctx
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;
54 uint num_imms;
55 uint num_instructions;
56 uint index_of_END;
58 uint errors;
59 uint warnings;
60 uint implied_array_size;
62 boolean print;
65 static INLINE unsigned
66 scan_register_key(const scan_register *reg)
68 unsigned key = reg->file;
69 key |= (reg->indices[0] << 4);
70 key |= (reg->indices[1] << 18);
72 return key;
75 static void
76 fill_scan_register1d(scan_register *reg,
77 uint file, uint index)
79 reg->file = file;
80 reg->dimensions = 1;
81 reg->indices[0] = index;
82 reg->indices[1] = 0;
85 static void
86 fill_scan_register2d(scan_register *reg,
87 uint file, uint index1, uint index2)
89 reg->file = file;
90 reg->dimensions = 2;
91 reg->indices[0] = index1;
92 reg->indices[1] = index2;
95 static void
96 scan_register_dst(scan_register *reg,
97 struct tgsi_full_dst_register *dst)
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);
113 static void
114 scan_register_src(scan_register *reg,
115 struct tgsi_full_src_register *src)
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);
131 static scan_register *
132 create_scan_register_src(struct tgsi_full_src_register *src)
134 scan_register *reg = MALLOC(sizeof(scan_register));
135 scan_register_src(reg, src);
137 return reg;
140 static scan_register *
141 create_scan_register_dst(struct tgsi_full_dst_register *dst)
143 scan_register *reg = MALLOC(sizeof(scan_register));
144 scan_register_dst(reg, dst);
146 return reg;
149 static void
150 report_error(
151 struct sanity_check_ctx *ctx,
152 const char *format,
153 ... )
155 va_list args;
157 if (!ctx->print)
158 return;
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++;
168 static void
169 report_warning(
170 struct sanity_check_ctx *ctx,
171 const char *format,
172 ... )
174 va_list args;
176 if (!ctx->print)
177 return;
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++;
187 static boolean
188 check_file_name(
189 struct sanity_check_ctx *ctx,
190 uint file )
192 if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) {
193 report_error( ctx, "(%u): Invalid register file name", file );
194 return FALSE;
196 return TRUE;
199 static boolean
200 is_register_declared(
201 struct sanity_check_ctx *ctx,
202 const scan_register *reg)
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;
210 static boolean
211 is_any_register_declared(
212 struct sanity_check_ctx *ctx,
213 uint file )
215 struct cso_hash_iter iter =
216 cso_hash_first_node(ctx->regs_decl);
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);
225 return FALSE;
228 static boolean
229 is_register_used(
230 struct sanity_check_ctx *ctx,
231 scan_register *reg)
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;
240 static boolean
241 is_ind_register_used(
242 struct sanity_check_ctx *ctx,
243 scan_register *reg)
245 return cso_hash_contains(ctx->regs_ind_used, reg->file);
248 static const char *file_names[TGSI_FILE_COUNT] =
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"
265 static boolean
266 check_register_usage(
267 struct sanity_check_ctx *ctx,
268 scan_register *reg,
269 const char *name,
270 boolean indirect_access )
272 if (!check_file_name( ctx, reg->file )) {
273 FREE(reg);
274 return FALSE;
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);
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 );
295 else {
296 report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file],
297 reg->indices[0], name );
300 if (!is_register_used( ctx, reg ))
301 cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg);
302 else
303 FREE(reg);
305 return TRUE;
308 static boolean
309 iter_instruction(
310 struct tgsi_iterate_context *iter,
311 struct tgsi_full_instruction *inst )
313 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
314 const struct tgsi_opcode_info *info;
315 uint i;
317 if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
318 if (ctx->index_of_END != ~0) {
319 report_error( ctx, "Too many END instructions" );
321 ctx->index_of_END = ctx->num_instructions;
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;
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 );
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 );
337 /* Check destination and source registers' validity.
338 * Mark the registers as used.
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");
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));
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 );
372 ctx->num_instructions++;
374 return TRUE;
377 static void
378 check_and_declare(struct sanity_check_ctx *ctx,
379 scan_register *reg)
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);
390 static boolean
391 iter_declaration(
392 struct tgsi_iterate_context *iter,
393 struct tgsi_full_declaration *decl )
395 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
396 uint file;
397 uint i;
399 /* No declarations allowed after the first instruction.
401 if (ctx->num_instructions > 0)
402 report_error( ctx, "Instruction expected but declaration found" );
404 /* Check registers' validity.
405 * Mark the registers as declared.
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);
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);
428 check_and_declare(ctx, reg);
432 return TRUE;
435 static boolean
436 iter_immediate(
437 struct tgsi_iterate_context *iter,
438 struct tgsi_full_immediate *imm )
440 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
441 scan_register *reg;
443 /* No immediates allowed after the first instruction.
445 if (ctx->num_instructions > 0)
446 report_error( ctx, "Instruction expected but immediate found" );
448 /* Mark the register as declared.
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++;
455 /* Check data type validity.
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;
464 return TRUE;
468 static boolean
469 iter_property(
470 struct tgsi_iterate_context *iter,
471 struct tgsi_full_property *prop )
473 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
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);
479 return TRUE;
482 static boolean
483 epilog(
484 struct tgsi_iterate_context *iter )
486 struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter;
488 /* There must be an END instruction somewhere.
490 if (ctx->index_of_END == ~0) {
491 report_error( ctx, "Missing END instruction" );
494 /* Check if all declared registers were used.
497 struct cso_hash_iter iter =
498 cso_hash_first_node(ctx->regs_decl);
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] );
506 iter = cso_hash_iter_next(iter);
510 /* Print totals, if any.
512 if (ctx->errors || ctx->warnings)
513 debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings );
515 return TRUE;
518 static void
519 regs_hash_destroy(struct cso_hash *hash)
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);
528 cso_hash_delete(hash);
531 boolean
532 tgsi_sanity_check(
533 const struct tgsi_token *tokens )
535 struct sanity_check_ctx ctx;
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;
544 ctx.regs_decl = cso_hash_create();
545 ctx.regs_used = cso_hash_create();
546 ctx.regs_ind_used = cso_hash_create();
548 ctx.num_imms = 0;
549 ctx.num_instructions = 0;
550 ctx.index_of_END = ~0;
552 ctx.errors = 0;
553 ctx.warnings = 0;
554 ctx.implied_array_size = 0;
555 ctx.print = debug_get_option_print_sanity();
557 if (!tgsi_iterate_shader( tokens, &ctx.iter ))
558 return FALSE;
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;