2 * Copyright © 2016 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33 #include <util/macros.h>
35 #include "gen_decoder.h"
37 #include "genxml/gen6_xml.h"
38 #include "genxml/gen7_xml.h"
39 #include "genxml/gen75_xml.h"
40 #include "genxml/gen8_xml.h"
41 #include "genxml/gen9_xml.h"
43 #define XML_BUFFER_SIZE 4096
45 #define MAKE_GEN(major, minor) ( ((major) << 8) | (minor) )
51 struct gen_group
*commands
[256];
53 struct gen_group
*structs
[256];
55 struct gen_group
*registers
[256];
57 struct gen_enum
*enums
[256];
65 struct parser_context
{
71 struct gen_group
*group
;
72 struct gen_enum
*enoom
;
75 struct gen_field
*fields
[128];
78 struct gen_value
*values
[256];
80 struct gen_spec
*spec
;
84 gen_group_get_name(struct gen_group
*group
)
90 gen_group_get_opcode(struct gen_group
*group
)
96 gen_spec_find_struct(struct gen_spec
*spec
, const char *name
)
98 for (int i
= 0; i
< spec
->nstructs
; i
++)
99 if (strcmp(spec
->structs
[i
]->name
, name
) == 0)
100 return spec
->structs
[i
];
106 gen_spec_find_register(struct gen_spec
*spec
, uint32_t offset
)
108 for (int i
= 0; i
< spec
->nregisters
; i
++)
109 if (spec
->registers
[i
]->register_offset
== offset
)
110 return spec
->registers
[i
];
116 gen_spec_find_enum(struct gen_spec
*spec
, const char *name
)
118 for (int i
= 0; i
< spec
->nenums
; i
++)
119 if (strcmp(spec
->enums
[i
]->name
, name
) == 0)
120 return spec
->enums
[i
];
126 gen_spec_get_gen(struct gen_spec
*spec
)
131 static void __attribute__((noreturn
))
132 fail(struct location
*loc
, const char *msg
, ...)
137 fprintf(stderr
, "%s:%d: error: ",
138 loc
->filename
, loc
->line_number
);
139 vfprintf(stderr
, msg
, ap
);
140 fprintf(stderr
, "\n");
146 fail_on_null(void *p
)
149 fprintf(stderr
, "aubinator: out of memory\n");
157 xstrdup(const char *s
)
159 return fail_on_null(strdup(s
));
171 return fail_on_null(zalloc(s
));
174 static struct gen_group
*
175 create_group(struct parser_context
*ctx
, const char *name
, const char **atts
)
177 struct gen_group
*group
;
179 group
= xzalloc(sizeof(*group
));
181 group
->name
= xstrdup(name
);
183 group
->spec
= ctx
->spec
;
184 group
->group_offset
= 0;
185 group
->group_count
= 0;
190 static struct gen_enum
*
191 create_enum(struct parser_context
*ctx
, const char *name
, const char **atts
)
195 e
= xzalloc(sizeof(*e
));
197 e
->name
= xstrdup(name
);
205 get_group_offset_count(struct parser_context
*ctx
, const char *name
,
206 const char **atts
, uint32_t *offset
, uint32_t *count
)
211 for (i
= 0; atts
[i
]; i
+= 2) {
212 if (strcmp(atts
[i
], "count") == 0)
213 *count
= strtoul(atts
[i
+ 1], &p
, 0);
214 else if (strcmp(atts
[i
], "start") == 0)
215 *offset
= strtoul(atts
[i
+ 1], &p
, 0);
221 get_register_offset(const char **atts
, uint32_t *offset
)
226 for (i
= 0; atts
[i
]; i
+= 2) {
227 if (strcmp(atts
[i
], "num") == 0)
228 *offset
= strtoul(atts
[i
+ 1], &p
, 0);
234 get_start_end_pos(int *start
, int *end
)
236 /* start value has to be mod with 32 as we need the relative
237 * start position in the first DWord. For the end position, add
238 * the length of the field to the start position to get the
239 * relative postion in the 64 bit address.
241 if (*end
- *start
> 32) {
242 int len
= *end
- *start
;
243 *start
= *start
% 32;
246 *start
= *start
% 32;
253 static inline uint64_t
254 mask(int start
, int end
)
258 v
= ~0ULL >> (63 - end
+ start
);
263 static inline uint64_t
264 field(uint64_t value
, int start
, int end
)
266 get_start_end_pos(&start
, &end
);
267 return (value
& mask(start
, end
)) >> (start
);
270 static inline uint64_t
271 field_address(uint64_t value
, int start
, int end
)
273 /* no need to right shift for address/offset */
274 get_start_end_pos(&start
, &end
);
275 return (value
& mask(start
, end
));
278 static struct gen_type
279 string_to_type(struct parser_context
*ctx
, const char *s
)
285 if (strcmp(s
, "int") == 0)
286 return (struct gen_type
) { .kind
= GEN_TYPE_INT
};
287 else if (strcmp(s
, "uint") == 0)
288 return (struct gen_type
) { .kind
= GEN_TYPE_UINT
};
289 else if (strcmp(s
, "bool") == 0)
290 return (struct gen_type
) { .kind
= GEN_TYPE_BOOL
};
291 else if (strcmp(s
, "float") == 0)
292 return (struct gen_type
) { .kind
= GEN_TYPE_FLOAT
};
293 else if (strcmp(s
, "address") == 0)
294 return (struct gen_type
) { .kind
= GEN_TYPE_ADDRESS
};
295 else if (strcmp(s
, "offset") == 0)
296 return (struct gen_type
) { .kind
= GEN_TYPE_OFFSET
};
297 else if (sscanf(s
, "u%d.%d", &i
, &f
) == 2)
298 return (struct gen_type
) { .kind
= GEN_TYPE_UFIXED
, .i
= i
, .f
= f
};
299 else if (sscanf(s
, "s%d.%d", &i
, &f
) == 2)
300 return (struct gen_type
) { .kind
= GEN_TYPE_SFIXED
, .i
= i
, .f
= f
};
301 else if (g
= gen_spec_find_struct(ctx
->spec
, s
), g
!= NULL
)
302 return (struct gen_type
) { .kind
= GEN_TYPE_STRUCT
, .gen_struct
= g
};
303 else if (e
= gen_spec_find_enum(ctx
->spec
, s
), e
!= NULL
)
304 return (struct gen_type
) { .kind
= GEN_TYPE_ENUM
, .gen_enum
= e
};
305 else if (strcmp(s
, "mbo") == 0)
306 return (struct gen_type
) { .kind
= GEN_TYPE_MBO
};
308 fail(&ctx
->loc
, "invalid type: %s", s
);
311 static struct gen_field
*
312 create_field(struct parser_context
*ctx
, const char **atts
)
314 struct gen_field
*field
;
318 field
= xzalloc(sizeof(*field
));
320 for (i
= 0; atts
[i
]; i
+= 2) {
321 if (strcmp(atts
[i
], "name") == 0)
322 field
->name
= xstrdup(atts
[i
+ 1]);
323 else if (strcmp(atts
[i
], "start") == 0)
324 field
->start
= ctx
->group
->group_offset
+strtoul(atts
[i
+ 1], &p
, 0);
325 else if (strcmp(atts
[i
], "end") == 0) {
326 field
->end
= ctx
->group
->group_offset
+strtoul(atts
[i
+ 1], &p
, 0);
327 if (ctx
->group
->group_offset
)
328 ctx
->group
->group_offset
= field
->end
+1;
329 } else if (strcmp(atts
[i
], "type") == 0)
330 field
->type
= string_to_type(ctx
, atts
[i
+ 1]);
331 else if (strcmp(atts
[i
], "default") == 0 &&
332 field
->start
>= 16 && field
->end
<= 31) {
333 field
->has_default
= true;
334 field
->default_value
= strtoul(atts
[i
+ 1], &p
, 0);
341 static struct gen_value
*
342 create_value(struct parser_context
*ctx
, const char **atts
)
344 struct gen_value
*value
= xzalloc(sizeof(*value
));
346 for (int i
= 0; atts
[i
]; i
+= 2) {
347 if (strcmp(atts
[i
], "name") == 0)
348 value
->name
= xstrdup(atts
[i
+ 1]);
349 else if (strcmp(atts
[i
], "value") == 0)
350 value
->value
= strtoul(atts
[i
+ 1], NULL
, 0);
357 start_element(void *data
, const char *element_name
, const char **atts
)
359 struct parser_context
*ctx
= data
;
361 const char *name
= NULL
;
362 const char *gen
= NULL
;
364 ctx
->loc
.line_number
= XML_GetCurrentLineNumber(ctx
->parser
);
366 for (i
= 0; atts
[i
]; i
+= 2) {
367 if (strcmp(atts
[i
], "name") == 0)
369 else if (strcmp(atts
[i
], "gen") == 0)
373 if (strcmp(element_name
, "genxml") == 0) {
375 fail(&ctx
->loc
, "no platform name given");
377 fail(&ctx
->loc
, "no gen given");
379 ctx
->platform
= xstrdup(name
);
381 int n
= sscanf(gen
, "%d.%d", &major
, &minor
);
383 fail(&ctx
->loc
, "invalid gen given: %s", gen
);
387 ctx
->spec
->gen
= MAKE_GEN(major
, minor
);
388 } else if (strcmp(element_name
, "instruction") == 0 ||
389 strcmp(element_name
, "struct") == 0) {
390 ctx
->group
= create_group(ctx
, name
, atts
);
391 } else if (strcmp(element_name
, "register") == 0) {
392 ctx
->group
= create_group(ctx
, name
, atts
);
393 get_register_offset(atts
, &ctx
->group
->register_offset
);
394 } else if (strcmp(element_name
, "group") == 0) {
395 get_group_offset_count(ctx
, name
, atts
, &ctx
->group
->group_offset
,
396 &ctx
->group
->group_count
);
397 } else if (strcmp(element_name
, "field") == 0) {
399 ctx
->fields
[ctx
->nfields
++] = create_field(ctx
, atts
);
400 if (ctx
->group
->group_count
)
401 ctx
->group
->group_count
--;
402 } while (ctx
->group
->group_count
> 0);
403 } else if (strcmp(element_name
, "enum") == 0) {
404 ctx
->enoom
= create_enum(ctx
, name
, atts
);
405 } else if (strcmp(element_name
, "value") == 0) {
406 ctx
->values
[ctx
->nvalues
++] = create_value(ctx
, atts
);
411 end_element(void *data
, const char *name
)
413 struct parser_context
*ctx
= data
;
414 struct gen_spec
*spec
= ctx
->spec
;
416 if (strcmp(name
, "instruction") == 0 ||
417 strcmp(name
, "struct") == 0 ||
418 strcmp(name
, "register") == 0) {
419 size_t size
= ctx
->nfields
* sizeof(ctx
->fields
[0]);
420 struct gen_group
*group
= ctx
->group
;
422 group
->fields
= xzalloc(size
);
423 group
->nfields
= ctx
->nfields
;
424 memcpy(group
->fields
, ctx
->fields
, size
);
428 for (int i
= 0; i
< group
->nfields
; i
++) {
429 if (group
->fields
[i
]->start
>= 16 &&
430 group
->fields
[i
]->end
<= 31 &&
431 group
->fields
[i
]->has_default
) {
432 group
->opcode_mask
|=
433 mask(group
->fields
[i
]->start
% 32, group
->fields
[i
]->end
% 32);
435 group
->fields
[i
]->default_value
<< group
->fields
[i
]->start
;
439 if (strcmp(name
, "instruction") == 0)
440 spec
->commands
[spec
->ncommands
++] = group
;
441 else if (strcmp(name
, "struct") == 0)
442 spec
->structs
[spec
->nstructs
++] = group
;
443 else if (strcmp(name
, "register") == 0)
444 spec
->registers
[spec
->nregisters
++] = group
;
445 } else if (strcmp(name
, "group") == 0) {
446 ctx
->group
->group_offset
= 0;
447 ctx
->group
->group_count
= 0;
448 } else if (strcmp(name
, "field") == 0) {
449 assert(ctx
->nfields
> 0);
450 struct gen_field
*field
= ctx
->fields
[ctx
->nfields
- 1];
451 size_t size
= ctx
->nvalues
* sizeof(ctx
->values
[0]);
452 field
->inline_enum
.values
= xzalloc(size
);
453 field
->inline_enum
.nvalues
= ctx
->nvalues
;
454 memcpy(field
->inline_enum
.values
, ctx
->values
, size
);
456 } else if (strcmp(name
, "enum") == 0) {
457 struct gen_enum
*e
= ctx
->enoom
;
458 size_t size
= ctx
->nvalues
* sizeof(ctx
->values
[0]);
459 e
->values
= xzalloc(size
);
460 e
->nvalues
= ctx
->nvalues
;
461 memcpy(e
->values
, ctx
->values
, size
);
464 spec
->enums
[spec
->nenums
++] = e
;
469 character_data(void *data
, const XML_Char
*s
, int len
)
474 devinfo_to_gen(const struct gen_device_info
*devinfo
)
476 int value
= 10 * devinfo
->gen
;
478 if (devinfo
->is_baytrail
|| devinfo
->is_haswell
)
484 static const struct {
489 { .gen
= 60, .data
= gen6_xml
, .data_length
= sizeof(gen6_xml
) },
490 { .gen
= 70, .data
= gen7_xml
, .data_length
= sizeof(gen7_xml
) },
491 { .gen
= 75, .data
= gen75_xml
, .data_length
= sizeof(gen75_xml
) },
492 { .gen
= 80, .data
= gen8_xml
, .data_length
= sizeof(gen8_xml
) },
493 { .gen
= 90, .data
= gen9_xml
, .data_length
= sizeof(gen9_xml
) }
496 static const uint8_t *
497 devinfo_to_xml_data(const struct gen_device_info
*devinfo
,
498 uint32_t *data_length
)
500 int i
, gen
= devinfo_to_gen(devinfo
);
502 for (i
= 0; i
< ARRAY_SIZE(gen_data
); i
++) {
503 if (gen_data
[i
].gen
== gen
) {
504 *data_length
= gen_data
[i
].data_length
;
505 return gen_data
[i
].data
;
509 unreachable("Unknown generation");
513 static uint32_t zlib_inflate(const void *compressed_data
,
514 uint32_t compressed_len
,
517 struct z_stream_s zstream
;
520 memset(&zstream
, 0, sizeof(zstream
));
522 zstream
.next_in
= (unsigned char *)compressed_data
;
523 zstream
.avail_in
= compressed_len
;
525 if (inflateInit(&zstream
) != Z_OK
)
529 zstream
.next_out
= out
;
530 zstream
.avail_out
= 4096;
533 switch (inflate(&zstream
, Z_SYNC_FLUSH
)) {
539 inflateEnd(&zstream
);
543 if (zstream
.avail_out
)
546 out
= realloc(out
, 2*zstream
.total_out
);
548 inflateEnd(&zstream
);
552 zstream
.next_out
= (unsigned char *)out
+ zstream
.total_out
;
553 zstream
.avail_out
= zstream
.total_out
;
556 inflateEnd(&zstream
);
558 return zstream
.total_out
;
562 gen_spec_load(const struct gen_device_info
*devinfo
)
564 struct parser_context ctx
;
566 const void *zlib_data
;
568 uint32_t zlib_length
= 0, text_length
;
570 memset(&ctx
, 0, sizeof ctx
);
571 ctx
.parser
= XML_ParserCreate(NULL
);
572 XML_SetUserData(ctx
.parser
, &ctx
);
573 if (ctx
.parser
== NULL
) {
574 fprintf(stderr
, "failed to create parser\n");
578 XML_SetElementHandler(ctx
.parser
, start_element
, end_element
);
579 XML_SetCharacterDataHandler(ctx
.parser
, character_data
);
581 ctx
.spec
= xzalloc(sizeof(*ctx
.spec
));
583 zlib_data
= devinfo_to_xml_data(devinfo
, &zlib_length
);
584 text_length
= zlib_inflate(zlib_data
, zlib_length
, &text_data
);
586 buf
= XML_GetBuffer(ctx
.parser
, text_length
);
587 memcpy(buf
, text_data
, text_length
);
589 if (XML_ParseBuffer(ctx
.parser
, text_length
, true) == 0) {
591 "Error parsing XML at line %ld col %ld byte %ld/%u: %s\n",
592 XML_GetCurrentLineNumber(ctx
.parser
),
593 XML_GetCurrentColumnNumber(ctx
.parser
),
594 XML_GetCurrentByteIndex(ctx
.parser
), text_length
,
595 XML_ErrorString(XML_GetErrorCode(ctx
.parser
)));
596 XML_ParserFree(ctx
.parser
);
601 XML_ParserFree(ctx
.parser
);
608 gen_spec_load_from_path(const struct gen_device_info
*devinfo
,
611 struct parser_context ctx
;
612 size_t len
, filename_len
= strlen(path
) + 20;
613 char *filename
= malloc(filename_len
);
617 len
= snprintf(filename
, filename_len
, "%s/gen%i.xml",
618 path
, devinfo_to_gen(devinfo
));
619 assert(len
< filename_len
);
621 input
= fopen(filename
, "r");
623 fprintf(stderr
, "failed to open xml description\n");
628 memset(&ctx
, 0, sizeof ctx
);
629 ctx
.parser
= XML_ParserCreate(NULL
);
630 XML_SetUserData(ctx
.parser
, &ctx
);
631 if (ctx
.parser
== NULL
) {
632 fprintf(stderr
, "failed to create parser\n");
638 XML_SetElementHandler(ctx
.parser
, start_element
, end_element
);
639 XML_SetCharacterDataHandler(ctx
.parser
, character_data
);
640 ctx
.loc
.filename
= filename
;
641 ctx
.spec
= xzalloc(sizeof(*ctx
.spec
));
644 buf
= XML_GetBuffer(ctx
.parser
, XML_BUFFER_SIZE
);
645 len
= fread(buf
, 1, XML_BUFFER_SIZE
, input
);
647 fprintf(stderr
, "fread: %m\n");
652 if (XML_ParseBuffer(ctx
.parser
, len
, len
== 0) == 0) {
654 "Error parsing XML at line %ld col %ld: %s\n",
655 XML_GetCurrentLineNumber(ctx
.parser
),
656 XML_GetCurrentColumnNumber(ctx
.parser
),
657 XML_ErrorString(XML_GetErrorCode(ctx
.parser
)));
664 XML_ParserFree(ctx
.parser
);
673 gen_spec_find_instruction(struct gen_spec
*spec
, const uint32_t *p
)
675 for (int i
= 0; i
< spec
->ncommands
; i
++) {
676 uint32_t opcode
= *p
& spec
->commands
[i
]->opcode_mask
;
677 if (opcode
== spec
->commands
[i
]->opcode
)
678 return spec
->commands
[i
];
685 gen_group_get_length(struct gen_group
*group
, const uint32_t *p
)
688 uint32_t type
= field(h
, 29, 31);
692 uint32_t opcode
= field(h
, 23, 28);
696 return field(h
, 0, 7) + 2;
700 case 3: /* Render */ {
701 uint32_t subtype
= field(h
, 27, 28);
704 return field(h
, 0, 7) + 2;
710 return field(h
, 0, 7) + 2;
715 unreachable("bad opcode");
719 gen_field_iterator_init(struct gen_field_iterator
*iter
,
720 struct gen_group
*group
,
727 iter
->print_colors
= print_colors
;
731 gen_get_enum_name(struct gen_enum
*e
, uint64_t value
)
733 for (int i
= 0; i
< e
->nvalues
; i
++) {
734 if (e
->values
[i
]->value
== value
) {
735 return e
->values
[i
]->name
;
742 gen_field_iterator_next(struct gen_field_iterator
*iter
)
750 if (iter
->i
== iter
->group
->nfields
)
753 f
= iter
->group
->fields
[iter
->i
++];
754 iter
->name
= f
->name
;
755 iter
->dword
= f
->start
/ 32;
756 iter
->struct_desc
= NULL
;
758 if ((f
->end
- f
->start
) > 32)
759 v
.qw
= ((uint64_t) iter
->p
[iter
->dword
+1] << 32) | iter
->p
[iter
->dword
];
761 v
.qw
= iter
->p
[iter
->dword
];
763 const char *enum_name
= NULL
;
765 switch (f
->type
.kind
) {
766 case GEN_TYPE_UNKNOWN
:
768 uint64_t value
= field(v
.qw
, f
->start
, f
->end
);
769 snprintf(iter
->value
, sizeof(iter
->value
),
771 enum_name
= gen_get_enum_name(&f
->inline_enum
, value
);
774 case GEN_TYPE_UINT
: {
775 uint64_t value
= field(v
.qw
, f
->start
, f
->end
);
776 snprintf(iter
->value
, sizeof(iter
->value
),
778 enum_name
= gen_get_enum_name(&f
->inline_enum
, value
);
781 case GEN_TYPE_BOOL
: {
782 const char *true_string
=
783 iter
->print_colors
? "\e[0;35mtrue\e[0m" : "true";
784 snprintf(iter
->value
, sizeof(iter
->value
),
785 "%s", field(v
.qw
, f
->start
, f
->end
) ? true_string
: "false");
789 snprintf(iter
->value
, sizeof(iter
->value
), "%f", v
.f
);
791 case GEN_TYPE_ADDRESS
:
792 case GEN_TYPE_OFFSET
:
793 snprintf(iter
->value
, sizeof(iter
->value
),
794 "0x%08"PRIx64
, field_address(v
.qw
, f
->start
, f
->end
));
796 case GEN_TYPE_STRUCT
:
797 snprintf(iter
->value
, sizeof(iter
->value
),
798 "<struct %s>", f
->type
.gen_struct
->name
);
800 gen_spec_find_struct(iter
->group
->spec
, f
->type
.gen_struct
->name
);
802 case GEN_TYPE_UFIXED
:
803 snprintf(iter
->value
, sizeof(iter
->value
),
804 "%f", (float) field(v
.qw
, f
->start
, f
->end
) / (1 << f
->type
.f
));
806 case GEN_TYPE_SFIXED
:
807 /* FIXME: Sign extend extracted field. */
808 snprintf(iter
->value
, sizeof(iter
->value
), "%s", "foo");
812 case GEN_TYPE_ENUM
: {
813 uint64_t value
= field(v
.qw
, f
->start
, f
->end
);
814 snprintf(iter
->value
, sizeof(iter
->value
),
816 enum_name
= gen_get_enum_name(f
->type
.gen_enum
, value
);
822 int length
= strlen(iter
->value
);
823 snprintf(iter
->value
+ length
, sizeof(iter
->value
) - length
,
831 print_dword_header(FILE *outfile
,
832 struct gen_field_iterator
*iter
, uint64_t offset
)
834 fprintf(outfile
, "0x%08"PRIx64
": 0x%08x : Dword %d\n",
835 offset
+ 4 * iter
->dword
, iter
->p
[iter
->dword
], iter
->dword
);
839 gen_print_group(FILE *outfile
, struct gen_group
*group
,
840 uint64_t offset
, const uint32_t *p
,
841 int starting_dword
, bool color
)
843 struct gen_field_iterator iter
;
846 gen_field_iterator_init(&iter
, group
, p
, color
);
847 while (gen_field_iterator_next(&iter
)) {
848 if (last_dword
!= iter
.dword
) {
849 print_dword_header(outfile
, &iter
, offset
);
850 last_dword
= iter
.dword
;
852 if (iter
.dword
>= starting_dword
) {
853 fprintf(outfile
, " %s: %s\n", iter
.name
, iter
.value
);
854 if (iter
.struct_desc
) {
855 uint64_t struct_offset
= offset
+ 4 * iter
.dword
;
856 print_dword_header(outfile
, &iter
, struct_offset
);
857 gen_print_group(outfile
, iter
.struct_desc
, struct_offset
,
858 &p
[iter
.dword
], 0, color
);