2 * Copyright © 2016 Intel Corporation
3 * Copyright © 2017 Broadcom
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
34 #include <util/macros.h>
35 #include <util/ralloc.h>
37 #include "v3d_decoder.h"
38 #include "v3d_packet_helpers.h"
45 struct v3d_group
*commands
[256];
47 struct v3d_group
*structs
[256];
49 struct v3d_group
*registers
[256];
51 struct v3d_enum
*enums
[256];
59 struct parser_context
{
64 struct v3d_group
*group
;
65 struct v3d_enum
*enoom
;
68 struct v3d_value
*values
[256];
70 struct v3d_spec
*spec
;
74 v3d_group_get_name(struct v3d_group
*group
)
80 v3d_group_get_opcode(struct v3d_group
*group
)
86 v3d_spec_find_struct(struct v3d_spec
*spec
, const char *name
)
88 for (int i
= 0; i
< spec
->nstructs
; i
++)
89 if (strcmp(spec
->structs
[i
]->name
, name
) == 0)
90 return spec
->structs
[i
];
96 v3d_spec_find_register(struct v3d_spec
*spec
, uint32_t offset
)
98 for (int i
= 0; i
< spec
->nregisters
; i
++)
99 if (spec
->registers
[i
]->register_offset
== offset
)
100 return spec
->registers
[i
];
106 v3d_spec_find_register_by_name(struct v3d_spec
*spec
, const char *name
)
108 for (int i
= 0; i
< spec
->nregisters
; i
++) {
109 if (strcmp(spec
->registers
[i
]->name
, name
) == 0)
110 return spec
->registers
[i
];
117 v3d_spec_find_enum(struct v3d_spec
*spec
, const char *name
)
119 for (int i
= 0; i
< spec
->nenums
; i
++)
120 if (strcmp(spec
->enums
[i
]->name
, name
) == 0)
121 return spec
->enums
[i
];
126 static void __attribute__((noreturn
))
127 fail(struct location
*loc
, const char *msg
, ...)
132 fprintf(stderr
, "%s:%d: error: ",
133 loc
->filename
, loc
->line_number
);
134 vfprintf(stderr
, msg
, ap
);
135 fprintf(stderr
, "\n");
141 fail_on_null(void *p
)
144 fprintf(stderr
, "aubinator: out of memory\n");
152 xstrdup(const char *s
)
154 return fail_on_null(strdup(s
));
166 return fail_on_null(zalloc(s
));
169 /* We allow fields to have either a bit index, or append "b" for a byte index.
172 is_byte_offset(const char *value
)
174 return value
[strlen(value
) - 1] == 'b';
178 get_group_offset_count(const char **atts
, uint32_t *offset
, uint32_t *count
,
179 uint32_t *size
, bool *variable
)
184 for (i
= 0; atts
[i
]; i
+= 2) {
185 if (strcmp(atts
[i
], "count") == 0) {
186 *count
= strtoul(atts
[i
+ 1], &p
, 0);
189 } else if (strcmp(atts
[i
], "start") == 0) {
190 *offset
= strtoul(atts
[i
+ 1], &p
, 0);
191 } else if (strcmp(atts
[i
], "size") == 0) {
192 *size
= strtoul(atts
[i
+ 1], &p
, 0);
198 static struct v3d_group
*
199 create_group(struct parser_context
*ctx
,
202 struct v3d_group
*parent
)
204 struct v3d_group
*group
;
206 group
= xzalloc(sizeof(*group
));
208 group
->name
= xstrdup(name
);
210 group
->spec
= ctx
->spec
;
211 group
->group_offset
= 0;
212 group
->group_count
= 0;
213 group
->variable
= false;
216 group
->parent
= parent
;
217 get_group_offset_count(atts
,
218 &group
->group_offset
,
227 static struct v3d_enum
*
228 create_enum(struct parser_context
*ctx
, const char *name
, const char **atts
)
232 e
= xzalloc(sizeof(*e
));
234 e
->name
= xstrdup(name
);
242 get_register_offset(const char **atts
, uint32_t *offset
)
247 for (i
= 0; atts
[i
]; i
+= 2) {
248 if (strcmp(atts
[i
], "num") == 0)
249 *offset
= strtoul(atts
[i
+ 1], &p
, 0);
255 get_start_end_pos(int *start
, int *end
)
257 /* start value has to be mod with 32 as we need the relative
258 * start position in the first DWord. For the end position, add
259 * the length of the field to the start position to get the
260 * relative postion in the 64 bit address.
262 if (*end
- *start
> 32) {
263 int len
= *end
- *start
;
264 *start
= *start
% 32;
267 *start
= *start
% 32;
274 static inline uint64_t
275 mask(int start
, int end
)
279 v
= ~0ULL >> (63 - end
+ start
);
284 static inline uint64_t
285 field(uint64_t value
, int start
, int end
)
287 get_start_end_pos(&start
, &end
);
288 return (value
& mask(start
, end
)) >> (start
);
291 static inline uint64_t
292 field_address(uint64_t value
, int start
, int end
)
294 /* no need to right shift for address/offset */
295 get_start_end_pos(&start
, &end
);
296 return (value
& mask(start
, end
));
299 static struct v3d_type
300 string_to_type(struct parser_context
*ctx
, const char *s
)
306 if (strcmp(s
, "int") == 0)
307 return (struct v3d_type
) { .kind
= V3D_TYPE_INT
};
308 else if (strcmp(s
, "uint") == 0)
309 return (struct v3d_type
) { .kind
= V3D_TYPE_UINT
};
310 else if (strcmp(s
, "bool") == 0)
311 return (struct v3d_type
) { .kind
= V3D_TYPE_BOOL
};
312 else if (strcmp(s
, "float") == 0)
313 return (struct v3d_type
) { .kind
= V3D_TYPE_FLOAT
};
314 else if (strcmp(s
, "address") == 0)
315 return (struct v3d_type
) { .kind
= V3D_TYPE_ADDRESS
};
316 else if (strcmp(s
, "offset") == 0)
317 return (struct v3d_type
) { .kind
= V3D_TYPE_OFFSET
};
318 else if (sscanf(s
, "u%d.%d", &i
, &f
) == 2)
319 return (struct v3d_type
) { .kind
= V3D_TYPE_UFIXED
, .i
= i
, .f
= f
};
320 else if (sscanf(s
, "s%d.%d", &i
, &f
) == 2)
321 return (struct v3d_type
) { .kind
= V3D_TYPE_SFIXED
, .i
= i
, .f
= f
};
322 else if (g
= v3d_spec_find_struct(ctx
->spec
, s
), g
!= NULL
)
323 return (struct v3d_type
) { .kind
= V3D_TYPE_STRUCT
, .v3d_struct
= g
};
324 else if (e
= v3d_spec_find_enum(ctx
->spec
, s
), e
!= NULL
)
325 return (struct v3d_type
) { .kind
= V3D_TYPE_ENUM
, .v3d_enum
= e
};
326 else if (strcmp(s
, "mbo") == 0)
327 return (struct v3d_type
) { .kind
= V3D_TYPE_MBO
};
329 fail(&ctx
->loc
, "invalid type: %s", s
);
332 static struct v3d_field
*
333 create_field(struct parser_context
*ctx
, const char **atts
)
335 struct v3d_field
*field
;
340 field
= xzalloc(sizeof(*field
));
342 for (i
= 0; atts
[i
]; i
+= 2) {
343 if (strcmp(atts
[i
], "name") == 0)
344 field
->name
= xstrdup(atts
[i
+ 1]);
345 else if (strcmp(atts
[i
], "start") == 0) {
346 field
->start
= strtoul(atts
[i
+ 1], &p
, 0);
347 if (is_byte_offset(atts
[i
+ 1]))
349 } else if (strcmp(atts
[i
], "end") == 0) {
350 field
->end
= strtoul(atts
[i
+ 1], &p
, 0) - 1;
351 if (is_byte_offset(atts
[i
+ 1]))
353 } else if (strcmp(atts
[i
], "size") == 0) {
354 size
= strtoul(atts
[i
+ 1], &p
, 0);
355 if (is_byte_offset(atts
[i
+ 1]))
357 } else if (strcmp(atts
[i
], "type") == 0)
358 field
->type
= string_to_type(ctx
, atts
[i
+ 1]);
359 else if (strcmp(atts
[i
], "default") == 0 &&
360 field
->start
>= 16 && field
->end
<= 31) {
361 field
->has_default
= true;
362 field
->default_value
= strtoul(atts
[i
+ 1], &p
, 0);
367 field
->end
= field
->start
+ size
- 1;
372 static struct v3d_value
*
373 create_value(struct parser_context
*ctx
, const char **atts
)
375 struct v3d_value
*value
= xzalloc(sizeof(*value
));
377 for (int i
= 0; atts
[i
]; i
+= 2) {
378 if (strcmp(atts
[i
], "name") == 0)
379 value
->name
= xstrdup(atts
[i
+ 1]);
380 else if (strcmp(atts
[i
], "value") == 0)
381 value
->value
= strtoul(atts
[i
+ 1], NULL
, 0);
388 create_and_append_field(struct parser_context
*ctx
,
391 if (ctx
->group
->nfields
== ctx
->group
->fields_size
) {
392 ctx
->group
->fields_size
= MAX2(ctx
->group
->fields_size
* 2, 2);
394 (struct v3d_field
**) realloc(ctx
->group
->fields
,
395 sizeof(ctx
->group
->fields
[0]) *
396 ctx
->group
->fields_size
);
399 ctx
->group
->fields
[ctx
->group
->nfields
++] = create_field(ctx
, atts
);
403 set_group_opcode(struct v3d_group
*group
, const char **atts
)
408 for (i
= 0; atts
[i
]; i
+= 2) {
409 if (strcmp(atts
[i
], "code") == 0)
410 group
->opcode
= strtoul(atts
[i
+ 1], &p
, 0);
416 start_element(void *data
, const char *element_name
, const char **atts
)
418 struct parser_context
*ctx
= data
;
420 const char *name
= NULL
;
421 const char *ver
= NULL
;
423 ctx
->loc
.line_number
= XML_GetCurrentLineNumber(ctx
->parser
);
425 for (i
= 0; atts
[i
]; i
+= 2) {
426 if (strcmp(atts
[i
], "name") == 0)
428 else if (strcmp(atts
[i
], "gen") == 0)
432 if (strcmp(element_name
, "vcxml") == 0) {
434 fail(&ctx
->loc
, "no ver given");
437 int n
= sscanf(ver
, "%d.%d", &major
, &minor
);
439 fail(&ctx
->loc
, "invalid ver given: %s", ver
);
443 ctx
->spec
->ver
= major
* 10 + minor
;
444 } else if (strcmp(element_name
, "packet") == 0 ||
445 strcmp(element_name
, "struct") == 0) {
446 ctx
->group
= create_group(ctx
, name
, atts
, NULL
);
448 if (strcmp(element_name
, "packet") == 0)
449 set_group_opcode(ctx
->group
, atts
);
450 } else if (strcmp(element_name
, "register") == 0) {
451 ctx
->group
= create_group(ctx
, name
, atts
, NULL
);
452 get_register_offset(atts
, &ctx
->group
->register_offset
);
453 } else if (strcmp(element_name
, "group") == 0) {
454 struct v3d_group
*previous_group
= ctx
->group
;
455 while (previous_group
->next
)
456 previous_group
= previous_group
->next
;
458 struct v3d_group
*group
= create_group(ctx
, "", atts
,
460 previous_group
->next
= group
;
462 } else if (strcmp(element_name
, "field") == 0) {
463 create_and_append_field(ctx
, atts
);
464 } else if (strcmp(element_name
, "enum") == 0) {
465 ctx
->enoom
= create_enum(ctx
, name
, atts
);
466 } else if (strcmp(element_name
, "value") == 0) {
467 ctx
->values
[ctx
->nvalues
++] = create_value(ctx
, atts
);
468 assert(ctx
->nvalues
< ARRAY_SIZE(ctx
->values
));
474 end_element(void *data
, const char *name
)
476 struct parser_context
*ctx
= data
;
477 struct v3d_spec
*spec
= ctx
->spec
;
479 if (strcmp(name
, "packet") == 0 ||
480 strcmp(name
, "struct") == 0 ||
481 strcmp(name
, "register") == 0) {
482 struct v3d_group
*group
= ctx
->group
;
484 ctx
->group
= ctx
->group
->parent
;
486 if (strcmp(name
, "packet") == 0) {
487 spec
->commands
[spec
->ncommands
++] = group
;
489 /* V3D packet XML has the packet contents with offsets
490 * starting from the first bit after the opcode, to
491 * match the spec. Shift the fields up now.
493 for (int i
= 0; i
< group
->nfields
; i
++) {
494 group
->fields
[i
]->start
+= 8;
495 group
->fields
[i
]->end
+= 8;
498 else if (strcmp(name
, "struct") == 0)
499 spec
->structs
[spec
->nstructs
++] = group
;
500 else if (strcmp(name
, "register") == 0)
501 spec
->registers
[spec
->nregisters
++] = group
;
503 assert(spec
->ncommands
< ARRAY_SIZE(spec
->commands
));
504 assert(spec
->nstructs
< ARRAY_SIZE(spec
->structs
));
505 assert(spec
->nregisters
< ARRAY_SIZE(spec
->registers
));
506 } else if (strcmp(name
, "group") == 0) {
507 ctx
->group
= ctx
->group
->parent
;
508 } else if (strcmp(name
, "field") == 0) {
509 assert(ctx
->group
->nfields
> 0);
510 struct v3d_field
*field
= ctx
->group
->fields
[ctx
->group
->nfields
- 1];
511 size_t size
= ctx
->nvalues
* sizeof(ctx
->values
[0]);
512 field
->inline_enum
.values
= xzalloc(size
);
513 field
->inline_enum
.nvalues
= ctx
->nvalues
;
514 memcpy(field
->inline_enum
.values
, ctx
->values
, size
);
516 } else if (strcmp(name
, "enum") == 0) {
517 struct v3d_enum
*e
= ctx
->enoom
;
518 size_t size
= ctx
->nvalues
* sizeof(ctx
->values
[0]);
519 e
->values
= xzalloc(size
);
520 e
->nvalues
= ctx
->nvalues
;
521 memcpy(e
->values
, ctx
->values
, size
);
524 spec
->enums
[spec
->nenums
++] = e
;
529 character_data(void *data
, const XML_Char
*s
, int len
)
533 static uint32_t zlib_inflate(const void *compressed_data
,
534 uint32_t compressed_len
,
537 struct z_stream_s zstream
;
540 memset(&zstream
, 0, sizeof(zstream
));
542 zstream
.next_in
= (unsigned char *)compressed_data
;
543 zstream
.avail_in
= compressed_len
;
545 if (inflateInit(&zstream
) != Z_OK
)
549 zstream
.next_out
= out
;
550 zstream
.avail_out
= 4096;
553 switch (inflate(&zstream
, Z_SYNC_FLUSH
)) {
559 inflateEnd(&zstream
);
563 if (zstream
.avail_out
)
566 out
= realloc(out
, 2*zstream
.total_out
);
568 inflateEnd(&zstream
);
572 zstream
.next_out
= (unsigned char *)out
+ zstream
.total_out
;
573 zstream
.avail_out
= zstream
.total_out
;
576 inflateEnd(&zstream
);
578 return zstream
.total_out
;
582 v3d_spec_load(const struct v3d_device_info
*devinfo
)
584 struct parser_context ctx
;
586 uint8_t *text_data
= NULL
;
587 uint32_t text_offset
= 0, text_length
= 0, total_length
;
589 for (int i
= 0; i
< ARRAY_SIZE(genxml_files_table
); i
++) {
590 if (genxml_files_table
[i
].gen_10
== devinfo
->ver
) {
591 text_offset
= genxml_files_table
[i
].offset
;
592 text_length
= genxml_files_table
[i
].length
;
597 if (text_length
== 0) {
598 fprintf(stderr
, "unable to find gen (%u) data\n", devinfo
->ver
);
602 memset(&ctx
, 0, sizeof ctx
);
603 ctx
.parser
= XML_ParserCreate(NULL
);
604 XML_SetUserData(ctx
.parser
, &ctx
);
605 if (ctx
.parser
== NULL
) {
606 fprintf(stderr
, "failed to create parser\n");
610 XML_SetElementHandler(ctx
.parser
, start_element
, end_element
);
611 XML_SetCharacterDataHandler(ctx
.parser
, character_data
);
613 ctx
.spec
= xzalloc(sizeof(*ctx
.spec
));
615 total_length
= zlib_inflate(compress_genxmls
,
616 sizeof(compress_genxmls
),
617 (void **) &text_data
);
618 assert(text_offset
+ text_length
<= total_length
);
620 buf
= XML_GetBuffer(ctx
.parser
, text_length
);
621 memcpy(buf
, &text_data
[text_offset
], text_length
);
623 if (XML_ParseBuffer(ctx
.parser
, text_length
, true) == 0) {
625 "Error parsing XML at line %ld col %ld byte %ld/%u: %s\n",
626 XML_GetCurrentLineNumber(ctx
.parser
),
627 XML_GetCurrentColumnNumber(ctx
.parser
),
628 XML_GetCurrentByteIndex(ctx
.parser
), text_length
,
629 XML_ErrorString(XML_GetErrorCode(ctx
.parser
)));
630 XML_ParserFree(ctx
.parser
);
635 XML_ParserFree(ctx
.parser
);
642 v3d_spec_find_instruction(struct v3d_spec
*spec
, const uint8_t *p
)
644 for (int i
= 0; i
< spec
->ncommands
; i
++) {
646 if (opcode
== spec
->commands
[i
]->opcode
)
647 return spec
->commands
[i
];
653 /** Returns the size of a V3D packet. */
655 v3d_group_get_length(struct v3d_group
*group
)
658 for (int i
= 0; i
< group
->nfields
; i
++) {
659 struct v3d_field
*field
= group
->fields
[i
];
661 last_bit
= MAX2(last_bit
, field
->end
);
663 return last_bit
/ 8 + 1;
667 v3d_field_iterator_init(struct v3d_field_iterator
*iter
,
668 struct v3d_group
*group
,
672 memset(iter
, 0, sizeof(*iter
));
676 iter
->print_colors
= print_colors
;
680 v3d_get_enum_name(struct v3d_enum
*e
, uint64_t value
)
682 for (int i
= 0; i
< e
->nvalues
; i
++) {
683 if (e
->values
[i
]->value
== value
) {
684 return e
->values
[i
]->name
;
691 iter_more_fields(const struct v3d_field_iterator
*iter
)
693 return iter
->field_iter
< iter
->group
->nfields
;
697 iter_group_offset_bits(const struct v3d_field_iterator
*iter
,
700 return iter
->group
->group_offset
+ (group_iter
*
701 iter
->group
->group_size
);
705 iter_more_groups(const struct v3d_field_iterator
*iter
)
707 if (iter
->group
->variable
) {
708 return iter_group_offset_bits(iter
, iter
->group_iter
+ 1) <
709 (v3d_group_get_length(iter
->group
) * 8);
711 return (iter
->group_iter
+ 1) < iter
->group
->group_count
||
712 iter
->group
->next
!= NULL
;
717 iter_advance_group(struct v3d_field_iterator
*iter
)
719 if (iter
->group
->variable
)
722 if ((iter
->group_iter
+ 1) < iter
->group
->group_count
) {
725 iter
->group
= iter
->group
->next
;
726 iter
->group_iter
= 0;
730 iter
->field_iter
= 0;
734 iter_advance_field(struct v3d_field_iterator
*iter
)
736 while (!iter_more_fields(iter
)) {
737 if (!iter_more_groups(iter
))
740 iter_advance_group(iter
);
743 iter
->field
= iter
->group
->fields
[iter
->field_iter
++];
744 if (iter
->field
->name
)
745 strncpy(iter
->name
, iter
->field
->name
, sizeof(iter
->name
));
747 memset(iter
->name
, 0, sizeof(iter
->name
));
748 iter
->offset
= iter_group_offset_bits(iter
, iter
->group_iter
) / 8 +
749 iter
->field
->start
/ 8;
750 iter
->struct_desc
= NULL
;
756 v3d_field_iterator_next(struct v3d_field_iterator
*iter
)
758 if (!iter_advance_field(iter
))
761 const char *enum_name
= NULL
;
763 int s
= iter
->field
->start
;
764 int e
= iter
->field
->end
;
766 switch (iter
->field
->type
.kind
) {
767 case V3D_TYPE_UNKNOWN
:
769 uint32_t value
= __gen_unpack_sint(iter
->p
, s
, e
);
770 snprintf(iter
->value
, sizeof(iter
->value
), "%d", value
);
771 enum_name
= v3d_get_enum_name(&iter
->field
->inline_enum
, value
);
774 case V3D_TYPE_UINT
: {
775 uint32_t value
= __gen_unpack_uint(iter
->p
, s
, e
);
776 snprintf(iter
->value
, sizeof(iter
->value
), "%u", value
);
777 enum_name
= v3d_get_enum_name(&iter
->field
->inline_enum
, value
);
780 case V3D_TYPE_BOOL
: {
781 const char *true_string
=
782 iter
->print_colors
? "\e[0;35mtrue\e[0m" : "true";
783 snprintf(iter
->value
, sizeof(iter
->value
), "%s",
784 __gen_unpack_uint(iter
->p
, s
, e
) ?
785 true_string
: "false");
789 snprintf(iter
->value
, sizeof(iter
->value
), "%f",
790 __gen_unpack_float(iter
->p
, s
, e
));
792 case V3D_TYPE_ADDRESS
:
793 case V3D_TYPE_OFFSET
:
794 snprintf(iter
->value
, sizeof(iter
->value
), "0x%08"PRIx64
,
795 __gen_unpack_uint(iter
->p
, s
, e
));
797 case V3D_TYPE_STRUCT
:
798 snprintf(iter
->value
, sizeof(iter
->value
), "<struct %s>",
799 iter
->field
->type
.v3d_struct
->name
);
801 v3d_spec_find_struct(iter
->group
->spec
,
802 iter
->field
->type
.v3d_struct
->name
);
804 case V3D_TYPE_SFIXED
:
805 snprintf(iter
->value
, sizeof(iter
->value
), "%f",
806 __gen_unpack_sfixed(iter
->p
, s
, e
,
807 iter
->field
->type
.f
));
809 case V3D_TYPE_UFIXED
:
810 snprintf(iter
->value
, sizeof(iter
->value
), "%f",
811 __gen_unpack_ufixed(iter
->p
, s
, e
,
812 iter
->field
->type
.f
));
816 case V3D_TYPE_ENUM
: {
817 uint32_t value
= __gen_unpack_uint(iter
->p
, s
, e
);
818 snprintf(iter
->value
, sizeof(iter
->value
), "%d", value
);
819 enum_name
= v3d_get_enum_name(iter
->field
->type
.v3d_enum
, value
);
824 if (strlen(iter
->group
->name
) == 0) {
825 int length
= strlen(iter
->name
);
826 snprintf(iter
->name
+ length
, sizeof(iter
->name
) - length
,
827 "[%i]", iter
->group_iter
);
831 int length
= strlen(iter
->value
);
832 snprintf(iter
->value
+ length
, sizeof(iter
->value
) - length
,
840 v3d_print_group(FILE *outfile
, struct v3d_group
*group
,
841 uint64_t offset
, const uint8_t *p
, bool color
)
843 struct v3d_field_iterator iter
;
845 v3d_field_iterator_init(&iter
, group
, p
, color
);
846 while (v3d_field_iterator_next(&iter
)) {
847 fprintf(outfile
, " %s: %s\n", iter
.name
, iter
.value
);
848 if (iter
.struct_desc
) {
849 uint64_t struct_offset
= offset
+ iter
.offset
;
850 v3d_print_group(outfile
, iter
.struct_desc
,
852 &p
[iter
.offset
], color
);