xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/tgsi/tgsi_sanity.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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