1 /**************************************************************************
2  *
3  * Copyright 2007-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_string.h"
30 #include "util/u_math.h"
31 #include "util/u_memory.h"
32 #include "tgsi_dump.h"
33 #include "tgsi_info.h"
34 #include "tgsi_iterate.h"
35 #include "tgsi_strings.h"
36 
37 
38 /** Number of spaces to indent for IF/LOOP/etc */
39 static const int indent_spaces = 3;
40 
41 
42 struct dump_ctx
43 {
44    struct tgsi_iterate_context iter;
45 
46    uint instno;
47    int indent;
48 
49    uint indentation;
50 
51    void (*dump_printf)(struct dump_ctx *ctx, const char *format, ...);
52 };
53 
54 static void
dump_ctx_printf(struct dump_ctx * ctx,const char * format,...)55 dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...)
56 {
57    va_list ap;
58    (void)ctx;
59    va_start(ap, format);
60    _debug_vprintf(format, ap);
61    va_end(ap);
62 }
63 
64 static void
dump_enum(struct dump_ctx * ctx,uint e,const char ** enums,uint enum_count)65 dump_enum(
66    struct dump_ctx *ctx,
67    uint e,
68    const char **enums,
69    uint enum_count )
70 {
71    if (e >= enum_count)
72       ctx->dump_printf( ctx, "%u", e );
73    else
74       ctx->dump_printf( ctx, "%s", enums[e] );
75 }
76 
77 #define EOL()           ctx->dump_printf( ctx, "\n" )
78 #define TXT(S)          ctx->dump_printf( ctx, "%s", S )
79 #define CHR(C)          ctx->dump_printf( ctx, "%c", C )
80 #define UIX(I)          ctx->dump_printf( ctx, "0x%x", I )
81 #define UID(I)          ctx->dump_printf( ctx, "%u", I )
82 #define INSTID(I)       ctx->dump_printf( ctx, "% 3u", I )
83 #define SID(I)          ctx->dump_printf( ctx, "%d", I )
84 #define FLT(F)          ctx->dump_printf( ctx, "%10.4f", F )
85 #define ENM(E,ENUMS)    dump_enum( ctx, E, ENUMS, sizeof( ENUMS ) / sizeof( *ENUMS ) )
86 
87 const char *
88 tgsi_swizzle_names[4] =
89 {
90    "x",
91    "y",
92    "z",
93    "w"
94 };
95 
96 static void
_dump_register_src(struct dump_ctx * ctx,const struct tgsi_full_src_register * src)97 _dump_register_src(
98    struct dump_ctx *ctx,
99    const struct tgsi_full_src_register *src )
100 {
101    ENM(src->Register.File, tgsi_file_names);
102    if (src->Register.Dimension) {
103       if (src->Dimension.Indirect) {
104          CHR( '[' );
105          ENM( src->DimIndirect.File, tgsi_file_names );
106          CHR( '[' );
107          SID( src->DimIndirect.Index );
108          TXT( "]." );
109          ENM( src->DimIndirect.SwizzleX, tgsi_swizzle_names );
110          if (src->Dimension.Index != 0) {
111             if (src->Dimension.Index > 0)
112                CHR( '+' );
113             SID( src->Dimension.Index );
114          }
115          CHR( ']' );
116       } else {
117          CHR('[');
118          SID(src->Dimension.Index);
119          CHR(']');
120       }
121    }
122    if (src->Register.Indirect) {
123       CHR( '[' );
124       ENM( src->Indirect.File, tgsi_file_names );
125       CHR( '[' );
126       SID( src->Indirect.Index );
127       TXT( "]." );
128       ENM( src->Indirect.SwizzleX, tgsi_swizzle_names );
129       if (src->Register.Index != 0) {
130          if (src->Register.Index > 0)
131             CHR( '+' );
132          SID( src->Register.Index );
133       }
134       CHR( ']' );
135    } else {
136       CHR( '[' );
137       SID( src->Register.Index );
138       CHR( ']' );
139    }
140 }
141 
142 
143 static void
_dump_register_dst(struct dump_ctx * ctx,const struct tgsi_full_dst_register * dst)144 _dump_register_dst(
145    struct dump_ctx *ctx,
146    const struct tgsi_full_dst_register *dst )
147 {
148    ENM(dst->Register.File, tgsi_file_names);
149    if (dst->Register.Dimension) {
150       if (dst->Dimension.Indirect) {
151          CHR( '[' );
152          ENM( dst->DimIndirect.File, tgsi_file_names );
153          CHR( '[' );
154          SID( dst->DimIndirect.Index );
155          TXT( "]." );
156          ENM( dst->DimIndirect.SwizzleX, tgsi_swizzle_names );
157          if (dst->Dimension.Index != 0) {
158             if (dst->Dimension.Index > 0)
159                CHR( '+' );
160             SID( dst->Dimension.Index );
161          }
162          CHR( ']' );
163       } else {
164          CHR('[');
165          SID(dst->Dimension.Index);
166          CHR(']');
167       }
168    }
169    if (dst->Register.Indirect) {
170       CHR( '[' );
171       ENM( dst->Indirect.File, tgsi_file_names );
172       CHR( '[' );
173       SID( dst->Indirect.Index );
174       TXT( "]." );
175       ENM( dst->Indirect.SwizzleX, tgsi_swizzle_names );
176       if (dst->Register.Index != 0) {
177          if (dst->Register.Index > 0)
178             CHR( '+' );
179          SID( dst->Register.Index );
180       }
181       CHR( ']' );
182    } else {
183       CHR( '[' );
184       SID( dst->Register.Index );
185       CHR( ']' );
186    }
187 }
188 static void
_dump_writemask(struct dump_ctx * ctx,uint writemask)189 _dump_writemask(
190    struct dump_ctx *ctx,
191    uint writemask )
192 {
193    if (writemask != TGSI_WRITEMASK_XYZW) {
194       CHR( '.' );
195       if (writemask & TGSI_WRITEMASK_X)
196          CHR( 'x' );
197       if (writemask & TGSI_WRITEMASK_Y)
198          CHR( 'y' );
199       if (writemask & TGSI_WRITEMASK_Z)
200          CHR( 'z' );
201       if (writemask & TGSI_WRITEMASK_W)
202          CHR( 'w' );
203    }
204 }
205 
206 static void
dump_imm_data(struct tgsi_iterate_context * iter,union tgsi_immediate_data * data,unsigned num_tokens,unsigned data_type)207 dump_imm_data(struct tgsi_iterate_context *iter,
208               union tgsi_immediate_data *data,
209               unsigned num_tokens,
210               unsigned data_type)
211 {
212    struct dump_ctx *ctx = (struct dump_ctx *)iter;
213    unsigned i ;
214 
215    TXT( " {" );
216 
217    assert( num_tokens <= 4 );
218    for (i = 0; i < num_tokens; i++) {
219       switch (data_type) {
220       case TGSI_IMM_FLOAT32:
221          FLT( data[i].Float );
222          break;
223       case TGSI_IMM_UINT32:
224          UID(data[i].Uint);
225          break;
226       case TGSI_IMM_INT32:
227          SID(data[i].Int);
228          break;
229       default:
230          assert( 0 );
231       }
232 
233       if (i < num_tokens - 1)
234          TXT( ", " );
235    }
236    TXT( "}" );
237 }
238 
239 static boolean
iter_declaration(struct tgsi_iterate_context * iter,struct tgsi_full_declaration * decl)240 iter_declaration(
241    struct tgsi_iterate_context *iter,
242    struct tgsi_full_declaration *decl )
243 {
244    struct dump_ctx *ctx = (struct dump_ctx *)iter;
245 
246    TXT( "DCL " );
247 
248    ENM(decl->Declaration.File, tgsi_file_names);
249 
250    /* all geometry shader inputs are two dimensional */
251    if (decl->Declaration.File == TGSI_FILE_INPUT &&
252        iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) {
253       TXT("[]");
254    }
255 
256    if (decl->Declaration.Dimension) {
257       CHR('[');
258       SID(decl->Dim.Index2D);
259       CHR(']');
260    }
261 
262    CHR('[');
263    SID(decl->Range.First);
264    if (decl->Range.First != decl->Range.Last) {
265       TXT("..");
266       SID(decl->Range.Last);
267    }
268    CHR(']');
269 
270    _dump_writemask(
271       ctx,
272       decl->Declaration.UsageMask );
273 
274    if (decl->Declaration.Local)
275       TXT( ", LOCAL" );
276 
277    if (decl->Declaration.Semantic) {
278       TXT( ", " );
279       ENM( decl->Semantic.Name, tgsi_semantic_names );
280       if (decl->Semantic.Index != 0 ||
281           decl->Semantic.Name == TGSI_SEMANTIC_GENERIC) {
282          CHR( '[' );
283          UID( decl->Semantic.Index );
284          CHR( ']' );
285       }
286    }
287 
288    if (decl->Declaration.File == TGSI_FILE_RESOURCE) {
289       TXT(", ");
290       ENM(decl->Resource.Resource, tgsi_texture_names);
291       if (decl->Resource.Writable)
292          TXT(", WR");
293       if (decl->Resource.Raw)
294          TXT(", RAW");
295    }
296 
297    if (decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) {
298       TXT(", ");
299       ENM(decl->SamplerView.Resource, tgsi_texture_names);
300       TXT(", ");
301       if ((decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeY) &&
302           (decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeZ) &&
303           (decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeW)) {
304          ENM(decl->SamplerView.ReturnTypeX, tgsi_type_names);
305       } else {
306          ENM(decl->SamplerView.ReturnTypeX, tgsi_type_names);
307          TXT(", ");
308          ENM(decl->SamplerView.ReturnTypeY, tgsi_type_names);
309          TXT(", ");
310          ENM(decl->SamplerView.ReturnTypeZ, tgsi_type_names);
311          TXT(", ");
312          ENM(decl->SamplerView.ReturnTypeW, tgsi_type_names);
313       }
314    }
315 
316    if (decl->Declaration.Interpolate) {
317       if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT &&
318           decl->Declaration.File == TGSI_FILE_INPUT)
319       {
320          TXT( ", " );
321          ENM( decl->Interp.Interpolate, tgsi_interpolate_names );
322       }
323 
324       if (decl->Interp.Centroid) {
325          TXT( ", CENTROID" );
326       }
327 
328       if (decl->Interp.CylindricalWrap) {
329          TXT(", CYLWRAP_");
330          if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_X) {
331             CHR('X');
332          }
333          if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Y) {
334             CHR('Y');
335          }
336          if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Z) {
337             CHR('Z');
338          }
339          if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_W) {
340             CHR('W');
341          }
342       }
343    }
344 
345    if (decl->Declaration.Invariant) {
346       TXT( ", INVARIANT" );
347    }
348 
349 
350    if (decl->Declaration.File == TGSI_FILE_IMMEDIATE_ARRAY) {
351       unsigned i;
352       char range_indent[4];
353 
354       TXT(" {");
355 
356       if (decl->Range.Last < 10)
357          range_indent[0] = '\0';
358       else if (decl->Range.Last < 100) {
359          range_indent[0] = ' ';
360          range_indent[1] = '\0';
361       } else if (decl->Range.Last < 1000) {
362          range_indent[0] = ' ';
363          range_indent[1] = ' ';
364          range_indent[2] = '\0';
365       } else {
366          range_indent[0] = ' ';
367          range_indent[1] = ' ';
368          range_indent[2] = ' ';
369          range_indent[3] = '\0';
370       }
371 
372       dump_imm_data(iter, decl->ImmediateData.u,
373                     4, TGSI_IMM_FLOAT32);
374       for(i = 1; i <= decl->Range.Last; ++i) {
375          /* indent by strlen of:
376           *   "DCL IMMX[0..1] {" */
377          CHR('\n');
378          TXT( "                " );
379          TXT( range_indent );
380          dump_imm_data(iter, decl->ImmediateData.u + i,
381                        4, TGSI_IMM_FLOAT32);
382       }
383 
384       TXT(" }");
385    }
386 
387    EOL();
388 
389    return TRUE;
390 }
391 
392 void
tgsi_dump_declaration(const struct tgsi_full_declaration * decl)393 tgsi_dump_declaration(
394    const struct tgsi_full_declaration *decl )
395 {
396    struct dump_ctx ctx;
397 
398    ctx.dump_printf = dump_ctx_printf;
399 
400    iter_declaration( &ctx.iter, (struct tgsi_full_declaration *)decl );
401 }
402 
403 static boolean
iter_property(struct tgsi_iterate_context * iter,struct tgsi_full_property * prop)404 iter_property(
405    struct tgsi_iterate_context *iter,
406    struct tgsi_full_property *prop )
407 {
408    int i;
409    struct dump_ctx *ctx = (struct dump_ctx *)iter;
410 
411    TXT( "PROPERTY " );
412    ENM(prop->Property.PropertyName, tgsi_property_names);
413 
414    if (prop->Property.NrTokens > 1)
415       TXT(" ");
416 
417    for (i = 0; i < prop->Property.NrTokens - 1; ++i) {
418       switch (prop->Property.PropertyName) {
419       case TGSI_PROPERTY_GS_INPUT_PRIM:
420       case TGSI_PROPERTY_GS_OUTPUT_PRIM:
421          ENM(prop->u[i].Data, tgsi_primitive_names);
422          break;
423       case TGSI_PROPERTY_FS_COORD_ORIGIN:
424          ENM(prop->u[i].Data, tgsi_fs_coord_origin_names);
425          break;
426       case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER:
427          ENM(prop->u[i].Data, tgsi_fs_coord_pixel_center_names);
428          break;
429       default:
430          SID( prop->u[i].Data );
431          break;
432       }
433       if (i < prop->Property.NrTokens - 2)
434          TXT( ", " );
435    }
436    EOL();
437 
438    return TRUE;
439 }
440 
tgsi_dump_property(const struct tgsi_full_property * prop)441 void tgsi_dump_property(
442    const struct tgsi_full_property *prop )
443 {
444    struct dump_ctx ctx;
445 
446    ctx.dump_printf = dump_ctx_printf;
447 
448    iter_property( &ctx.iter, (struct tgsi_full_property *)prop );
449 }
450 
451 static boolean
iter_immediate(struct tgsi_iterate_context * iter,struct tgsi_full_immediate * imm)452 iter_immediate(
453    struct tgsi_iterate_context *iter,
454    struct tgsi_full_immediate *imm )
455 {
456    struct dump_ctx *ctx = (struct dump_ctx *) iter;
457 
458    TXT( "IMM " );
459    ENM( imm->Immediate.DataType, tgsi_immediate_type_names );
460 
461    dump_imm_data(iter, imm->u, imm->Immediate.NrTokens - 1,
462                  imm->Immediate.DataType);
463 
464    EOL();
465 
466    return TRUE;
467 }
468 
469 void
tgsi_dump_immediate(const struct tgsi_full_immediate * imm)470 tgsi_dump_immediate(
471    const struct tgsi_full_immediate *imm )
472 {
473    struct dump_ctx ctx;
474 
475    ctx.dump_printf = dump_ctx_printf;
476 
477    iter_immediate( &ctx.iter, (struct tgsi_full_immediate *)imm );
478 }
479 
480 static boolean
iter_instruction(struct tgsi_iterate_context * iter,struct tgsi_full_instruction * inst)481 iter_instruction(
482    struct tgsi_iterate_context *iter,
483    struct tgsi_full_instruction *inst )
484 {
485    struct dump_ctx *ctx = (struct dump_ctx *) iter;
486    uint instno = ctx->instno++;
487    const struct tgsi_opcode_info *info = tgsi_get_opcode_info( inst->Instruction.Opcode );
488    uint i;
489    boolean first_reg = TRUE;
490 
491    INSTID( instno );
492    TXT( ": " );
493 
494    ctx->indent -= info->pre_dedent;
495    for(i = 0; (int)i < ctx->indent; ++i)
496       TXT( "  " );
497    ctx->indent += info->post_indent;
498 
499    if (inst->Instruction.Predicate) {
500       CHR( '(' );
501 
502       if (inst->Predicate.Negate)
503          CHR( '!' );
504 
505       TXT( "PRED[" );
506       SID( inst->Predicate.Index );
507       CHR( ']' );
508 
509       if (inst->Predicate.SwizzleX != TGSI_SWIZZLE_X ||
510           inst->Predicate.SwizzleY != TGSI_SWIZZLE_Y ||
511           inst->Predicate.SwizzleZ != TGSI_SWIZZLE_Z ||
512           inst->Predicate.SwizzleW != TGSI_SWIZZLE_W) {
513          CHR( '.' );
514          ENM( inst->Predicate.SwizzleX, tgsi_swizzle_names );
515          ENM( inst->Predicate.SwizzleY, tgsi_swizzle_names );
516          ENM( inst->Predicate.SwizzleZ, tgsi_swizzle_names );
517          ENM( inst->Predicate.SwizzleW, tgsi_swizzle_names );
518       }
519 
520       TXT( ") " );
521    }
522 
523    TXT( info->mnemonic );
524 
525    switch (inst->Instruction.Saturate) {
526    case TGSI_SAT_NONE:
527       break;
528    case TGSI_SAT_ZERO_ONE:
529       TXT( "_SAT" );
530       break;
531    case TGSI_SAT_MINUS_PLUS_ONE:
532       TXT( "_SATNV" );
533       break;
534    default:
535       assert( 0 );
536    }
537 
538    for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
539       const struct tgsi_full_dst_register *dst = &inst->Dst[i];
540 
541       if (!first_reg)
542          CHR( ',' );
543       CHR( ' ' );
544 
545       _dump_register_dst( ctx, dst );
546       _dump_writemask( ctx, dst->Register.WriteMask );
547 
548       first_reg = FALSE;
549    }
550 
551    for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
552       const struct tgsi_full_src_register *src = &inst->Src[i];
553 
554       if (!first_reg)
555          CHR( ',' );
556       CHR( ' ' );
557 
558       if (src->Register.Negate)
559          CHR( '-' );
560       if (src->Register.Absolute)
561          CHR( '|' );
562 
563       _dump_register_src(ctx, src);
564 
565       if (src->Register.SwizzleX != TGSI_SWIZZLE_X ||
566           src->Register.SwizzleY != TGSI_SWIZZLE_Y ||
567           src->Register.SwizzleZ != TGSI_SWIZZLE_Z ||
568           src->Register.SwizzleW != TGSI_SWIZZLE_W) {
569          CHR( '.' );
570          ENM( src->Register.SwizzleX, tgsi_swizzle_names );
571          ENM( src->Register.SwizzleY, tgsi_swizzle_names );
572          ENM( src->Register.SwizzleZ, tgsi_swizzle_names );
573          ENM( src->Register.SwizzleW, tgsi_swizzle_names );
574       }
575 
576       if (src->Register.Absolute)
577          CHR( '|' );
578 
579       first_reg = FALSE;
580    }
581 
582    if (inst->Instruction.Texture) {
583       TXT( ", " );
584       ENM( inst->Texture.Texture, tgsi_texture_names );
585       for (i = 0; i < inst->Texture.NumOffsets; i++) {
586          TXT( ", " );
587          ENM( inst->TexOffsets[i].File, tgsi_file_names);
588          CHR( '[' );
589          SID( inst->TexOffsets[i].Index );
590          CHR( ']' );
591          CHR( '.' );
592          ENM( inst->TexOffsets[i].SwizzleX, tgsi_swizzle_names);
593          ENM( inst->TexOffsets[i].SwizzleY, tgsi_swizzle_names);
594          ENM( inst->TexOffsets[i].SwizzleZ, tgsi_swizzle_names);
595       }
596    }
597 
598    switch (inst->Instruction.Opcode) {
599    case TGSI_OPCODE_IF:
600    case TGSI_OPCODE_ELSE:
601    case TGSI_OPCODE_BGNLOOP:
602    case TGSI_OPCODE_ENDLOOP:
603    case TGSI_OPCODE_CAL:
604       TXT( " :" );
605       UID( inst->Label.Label );
606       break;
607    }
608 
609    /* update indentation */
610    if (inst->Instruction.Opcode == TGSI_OPCODE_IF ||
611        inst->Instruction.Opcode == TGSI_OPCODE_ELSE ||
612        inst->Instruction.Opcode == TGSI_OPCODE_BGNLOOP) {
613       ctx->indentation += indent_spaces;
614    }
615 
616    EOL();
617 
618    return TRUE;
619 }
620 
621 void
tgsi_dump_instruction(const struct tgsi_full_instruction * inst,uint instno)622 tgsi_dump_instruction(
623    const struct tgsi_full_instruction *inst,
624    uint instno )
625 {
626    struct dump_ctx ctx;
627 
628    ctx.instno = instno;
629    ctx.indent = 0;
630    ctx.dump_printf = dump_ctx_printf;
631    ctx.indentation = 0;
632 
633    iter_instruction( &ctx.iter, (struct tgsi_full_instruction *)inst );
634 }
635 
636 static boolean
prolog(struct tgsi_iterate_context * iter)637 prolog(
638    struct tgsi_iterate_context *iter )
639 {
640    struct dump_ctx *ctx = (struct dump_ctx *) iter;
641    ENM( iter->processor.Processor, tgsi_processor_type_names );
642    EOL();
643    return TRUE;
644 }
645 
646 void
tgsi_dump(const struct tgsi_token * tokens,uint flags)647 tgsi_dump(
648    const struct tgsi_token *tokens,
649    uint flags )
650 {
651    struct dump_ctx ctx;
652 
653    ctx.iter.prolog = prolog;
654    ctx.iter.iterate_instruction = iter_instruction;
655    ctx.iter.iterate_declaration = iter_declaration;
656    ctx.iter.iterate_immediate = iter_immediate;
657    ctx.iter.iterate_property = iter_property;
658    ctx.iter.epilog = NULL;
659 
660    ctx.instno = 0;
661    ctx.indent = 0;
662    ctx.dump_printf = dump_ctx_printf;
663    ctx.indentation = 0;
664 
665    tgsi_iterate_shader( tokens, &ctx.iter );
666 }
667 
668 struct str_dump_ctx
669 {
670    struct dump_ctx base;
671    char *str;
672    char *ptr;
673    int left;
674 };
675 
676 static void
str_dump_ctx_printf(struct dump_ctx * ctx,const char * format,...)677 str_dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...)
678 {
679    struct str_dump_ctx *sctx = (struct str_dump_ctx *)ctx;
680 
681    if(sctx->left > 1) {
682       int written;
683       va_list ap;
684       va_start(ap, format);
685       written = util_vsnprintf(sctx->ptr, sctx->left, format, ap);
686       va_end(ap);
687 
688       /* Some complicated logic needed to handle the return value of
689        * vsnprintf:
690        */
691       if (written > 0) {
692          written = MIN2(sctx->left, written);
693          sctx->ptr += written;
694          sctx->left -= written;
695       }
696    }
697 }
698 
699 void
tgsi_dump_str(const struct tgsi_token * tokens,uint flags,char * str,size_t size)700 tgsi_dump_str(
701    const struct tgsi_token *tokens,
702    uint flags,
703    char *str,
704    size_t size)
705 {
706    struct str_dump_ctx ctx;
707 
708    ctx.base.iter.prolog = prolog;
709    ctx.base.iter.iterate_instruction = iter_instruction;
710    ctx.base.iter.iterate_declaration = iter_declaration;
711    ctx.base.iter.iterate_immediate = iter_immediate;
712    ctx.base.iter.iterate_property = iter_property;
713    ctx.base.iter.epilog = NULL;
714 
715    ctx.base.instno = 0;
716    ctx.base.indent = 0;
717    ctx.base.dump_printf = &str_dump_ctx_printf;
718    ctx.base.indentation = 0;
719 
720    ctx.str = str;
721    ctx.str[0] = 0;
722    ctx.ptr = str;
723    ctx.left = (int)size;
724 
725    tgsi_iterate_shader( tokens, &ctx.base.iter );
726 }
727