OSDN Git Service

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