2 * export c types and prototypes to perl file.
4 * Copyright (c) 2019 Yonatan Goldschmidt
5 * Copyright (c) 2020,2021 Michael Zucchi
7 * The MIT License (MIT)
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 skeleton taken from structsizes.cc plugin by Richard W.M. Jones
30 https://rwmj.wordpress.com/2016/02/24/playing-with-gcc-plugins/
32 https://blog.adacore.com/bindings-gcc-plugins
36 TODO: get header name from tree
41 function declarations, i think
43 chain param type name size
44 TYPE_DECL TYPE_ARG_TYPES TYPE_VALUE(item):TREE_LIST <empty> TYPE_SIZE(item_type)
47 FUNCTION_DECL DECL_ARGUENTS TREE_TYPE(item):PARM_DECL DECL_NAME(item) DECL_SIZE(item)
55 #include <gcc-plugin.h>
57 #include <print-tree.h>
58 #include <tree-pretty-print.h>
62 #define D(x) do { x; } while(0)
64 extern const char *tree_codes[];
65 #define ZTREE_CODE(x) tree_codes[TREE_CODE(x)]
67 //Enums have a type, otherwise they're just integers
70 // remove some of the debug
73 int plugin_is_GPL_compatible; // must be defined for the plugin to run
75 static FILE *output_file;
76 static int output_enabled = 1;
78 static int debug_level = 0;
80 static void debug_tree_helper(tree t, const char *msg) {
82 if (debug_level > 2) {
84 fprintf(stderr, "dumping tree: '%s'\n", msg);
86 fprintf(stderr, "\n\n");
92 static struct hash dumped;
93 static struct list todump;
94 static struct list context_stack;
95 static struct list parameters; // last list of params
97 static struct hash forward_types;
99 static int generate(const char *fmt, ...) {
101 if (output_enabled) {
104 res = vfprintf(output_file, fmt, ap);
111 Join all names in the stack, in reverse order.
113 static char *stack_path(struct list *stack, const char *sep) {
116 for (struct node *n = stack->tail; n; n=n->link)
117 total += strlen(n->name)+strlen(sep);
119 char *data = (char *)xmalloc(total);
122 // FIXME: some other context name
123 for (struct node *n = stack->tail; n; n=n->link) {
124 p = stpcpy(p, n->name);
132 static void list_clear(struct list *list) {
135 while ((node = list_remhead(list))) {
137 fprintf(stderr, " free: %s\n", node->name);
142 // returns 0 if type has no size (i.e VOID_TYPE)
143 static bool is_struct_or_union(const_tree type) {
144 return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
147 static void print_spaces(int n) {
148 if (output_enabled) {
149 for (int i = 0; i < n; ++i)
150 fputc('\t', output_file);
154 static int is_ref_type(tree type) {
155 switch (TREE_CODE(type)) {
166 static tree target_type(tree type) {
167 while (is_ref_type(type))
168 type = TREE_TYPE(type);
172 static size_t value_size(tree n) {
173 return n ? tree_to_uhwi(n) : 0;
176 static const char *value_name(tree type) {
177 tree test = TYPE_IDENTIFIER(type);
178 const char *value = test ? IDENTIFIER_POINTER(test) : NULL;
180 // __va_list_tag is the final type beneath __builtin_va_list.
181 // it behaves different from other types - it has a TYPE_MAIN_VARIANT, but the main TYPE_NAME seems to give
182 // an unexpected tree, and therefore ORIG_TYPE_NAME returns a garbage value.
183 // I think this patch is good enough.
184 if (value && 0 == strcmp("__va_list_tag", value))
185 return "__va_list_tag";
187 test = TYPE_MAIN_VARIANT(type);
188 test = test ? TYPE_NAME(test) : test;
189 test = test && TREE_CODE(test) == TYPE_DECL ? DECL_NAME(test) : test;
191 return test ? IDENTIFIER_POINTER(test) : value;
195 Find a non-ref type in the type chain, i.e. skip pointers/arrays.
197 static tree simple_type(tree t) {
198 while (is_ref_type(t))
204 Create a 'panama' signature for a single type.
206 static void export_desc(tree field, tree field_type, struct buffer *b) {
207 const size_t data_size = value_size(TYPE_SIZE(field_type));
209 switch (TREE_CODE(field_type)) {
215 const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
218 if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
219 // it is a flexible array or empty types
222 // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
223 num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
227 b->pos += sprintf(b->data + b->pos, "[%zu", num_elem);
228 export_desc(field, TREE_TYPE(field_type), b);
235 b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
236 export_desc(field, TREE_TYPE(field_type), b);
238 case FUNCTION_TYPE: {
239 // TODO: handle void f() type -> null TYPE_ARG_TYPES()
240 tree return_type = TREE_TYPE(field_type);
243 for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
244 tree param_type = TREE_VALUE(param);
246 if (TREE_CODE(param_type) == VOID_TYPE)
249 export_desc(param, param_type, b);
252 export_desc(field, return_type, b);
256 #if defined(TYPED_ENUMS)
258 buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
262 b->pos += sprintf(b->data + b->pos, "%c%zu",
263 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
269 if (TYPE_IDENTIFIER(field_type)) {
270 list_add(&todump, node_alloc(field_type, NULL));
273 buffer_add(b, value_name(field_type));
276 char *name = stack_path(&context_stack, "_");
278 list_add(&todump, node_alloc(field_type, name));
289 b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
293 b->pos += sprintf(b->data + b->pos, "%c%zu",
294 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
298 debug_tree_helper(field_type, "unknown type!");
305 Create a 'c' description of type.
307 static void export_cdesc(tree field, tree field_type, struct buffer *b) {
308 const size_t data_size = value_size(TYPE_SIZE(field_type));
310 switch (TREE_CODE(field_type)) {
312 buffer_add(b, "void");
316 const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
319 if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
320 // it is a flexible array or empty types
323 // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
324 num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
327 export_cdesc(field, TREE_TYPE(field_type), b);
332 b->pos += sprintf(b->data + b->pos, "%zu", num_elem);
340 //b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
341 export_cdesc(field, TREE_TYPE(field_type), b);
343 case FUNCTION_TYPE: {
344 // TODO: handle void f() type -> null TYPE_ARG_TYPES()
345 tree return_type = TREE_TYPE(field_type);
348 export_cdesc(field, return_type, b);
350 buffer_add(b, "(*)");
352 for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
353 tree param_type = TREE_VALUE(param);
355 // TREE_TYPE might? point to a type that a previous decl has also referenced
360 if (TREE_CODE(param_type) == VOID_TYPE) {
361 buffer_add(b, "void");
365 export_cdesc(param, param_type, b);
371 #if defined(TYPED_ENUMS)
373 buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
377 b->pos += sprintf(b->data + b->pos, "%c%zu",
378 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
384 if (TYPE_IDENTIFIER(field_type)) {
385 list_add(&todump, node_alloc(field_type, NULL));
388 buffer_add(b, value_name(field_type));
391 char *name = stack_path(&context_stack, "_");
393 list_add(&todump, node_alloc(field_type, name));
404 b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
408 b->pos += sprintf(b->data + b->pos, "%c%zu",
409 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
413 debug_tree_helper(field_type, "unknown type!");
420 Print a single parameter or field.
422 static void export_param(tree field, tree field_type, size_t field_size) {
423 switch (TREE_CODE(field_type)) {
427 case REFERENCE_TYPE: {
430 buffer_init(&b, 256);
431 export_desc(field, field_type, &b);
432 generate(" deref => '%s',", b.data);
435 field_type = simple_type(field_type);
436 field_size = value_size(TYPE_SIZE(field_type));
438 export_param(field, field_type, field_size);
442 generate(" type => 'void',");
443 generate(" ctype => 'void',");
445 case ENUMERAL_TYPE: {
446 #if defined(TYPED_ENUMS)
447 const char *names = TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum";
448 generate(" type => 'enum:%s',", names);
450 generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
452 generate(" ctype => 'enum %s',", value_name(field_type));
455 case FUNCTION_TYPE: {
457 tree root_type = TREE_TYPE(field);
459 // If this is a typedef we might have a name for the type, otherwise it's a signature based name
460 if (root_type && TYPE_IDENTIFIER(root_type)) {
461 generate(" type => 'call:%s', ", value_name(root_type));
464 fprintf(stderr, "save for later param type %p\n", field_type);
465 buffer_init(&b, 256);
466 export_desc(field, field_type, &b);
467 list_add(&todump, node_alloc(field_type, b.data));
468 generate(" type => 'call:%s', ", b.data);
471 buffer_init(&b, 256);
472 export_cdesc(field, field_type, &b);
478 generate(" ctype => '%s',", value_name(field_type));
479 generate(" type => 'f%zu',", field_size);
482 if (TREE_CODE(field) == FIELD_DECL && DECL_BIT_FIELD(field)) {
483 generate(" ctype => 'bitfield',");
484 generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', value_size(DECL_SIZE(field)));
486 generate(" ctype => '%s',", value_name(field_type));
487 generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
492 const char *us = TREE_CODE(field_type) == RECORD_TYPE ? "struct" : "union";
494 if (TYPE_IDENTIFIER(field_type)) {
495 generate(" type => '%s:%s',", us, value_name(field_type));
497 char *name = stack_path(&context_stack, "_");
499 list_add(&todump, node_alloc(field_type, name));
500 generate(" type => '%s:%s',", us, name);
506 fprintf(stderr, "unknown param type: %s\n", ZTREE_CODE(field_type));
512 Export a chain of parameters
514 static void export_params(tree func) {
517 struct list args = { 0 };
519 struct node *fwd = hash_lookup_bytype(&forward_types, func);
522 // use the forward reference to find the names
524 fprintf(stderr, "found forward reference @ %p\n", fwd);
525 name = fwd->list.head;
527 // paramter names are in the paramters list
528 // but they are in reverse order
530 for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
531 tree param_type = TREE_VALUE(param);
533 if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
536 struct node *decl = stack_pull(¶meters);
539 fprintf(stderr, "(pull parameter '%s')\n", decl->name);
540 stack_push(&args, decl);
542 fprintf(stderr, "ERROR: parameter %d missing parameter declaration\n", id);
548 for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
549 tree param_type = TREE_VALUE(param);
550 const size_t data_size = value_size(TYPE_SIZE(param_type));
551 const char *names = NULL;
553 // non-varags functions end in VOID_TYPE
554 if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
559 // size: do we need it?
560 generate(" size => %zu,", data_size);
563 // this should be a parm_decl with an identifier of the name
566 // can one check it's a matching type?
570 if (!names || !names[0]) {
571 sprintf(nameb, "arg$%d", id);
575 generate(" name => '%s',", names);
576 stack_push(&context_stack, node_alloc(param, names));
579 export_param(param, param_type, data_size);
581 free(stack_pull(&context_stack));
591 Export a chain of fields.
593 static void export_fields(tree first_field, size_t base_offset, int indent) {
594 for (tree field = first_field; field; field = TREE_CHAIN(field)) {
595 gcc_assert(TREE_CODE(field) == FIELD_DECL);
597 tree field_type = TREE_TYPE(field);
598 const size_t field_size = value_size(DECL_SIZE(field));
599 size_t offset = base_offset + tree_to_uhwi(DECL_FIELD_OFFSET(field)) * 8 + tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field));
601 // name: if none, then inline
602 if (!DECL_NAME(field)) {
603 if (is_struct_or_union(field_type))
604 export_fields(TYPE_FIELDS(field_type), offset, indent);
606 const char *names = IDENTIFIER_POINTER(DECL_NAME(field));
609 fprintf(stderr, " field: %s\n", names);
610 print_spaces(indent+1);
611 generate("{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
612 stack_push(&context_stack, node_alloc(field, names));
615 export_param(field, field_type, field_size);
617 free(stack_pull(&context_stack));
625 Main entry point for exporting any type.
627 static void export_type(tree type, const char *names) {
633 fprintf(stderr, "export_type(%s, %s)\n", ZTREE_CODE(type), names);
635 switch (TREE_CODE(type)) {
638 name = DECL_NAME(type);
641 names = IDENTIFIER_POINTER(name);
643 if (hash_lookup(&dumped, names))
645 hash_put(&dumped, node_alloc(type, names));
648 fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
650 deftype = TREE_TYPE(type);
651 target = target_type(deftype);
652 switch (TREE_CODE(target)) {
653 case FUNCTION_TYPE: {
654 // function pointer typdef
655 // I don't know if i even want this
656 generate("'call:%s' => { name => '%s', type => 'call',", names, names);
658 // the deftype is always a pointer for a function_type
662 buffer_init(&b, 256);
663 export_desc(type, deftype, &b);
664 generate(" deref => '%s',", b.data);
667 generate(" ctype => '%s',", print_generic_expr_to_str(target));
671 tree result_type = TREE_TYPE(target);
672 const size_t data_size = value_size(TYPE_SIZE(result_type));
674 generate("\n\tresult => {");
675 export_param(target, result_type, data_size);
679 generate("\n\targuments => [\n");
680 export_params(target);
684 case ENUMERAL_TYPE: {
685 // TODO: typedef of anonymous enumerations may be repeated
686 // TODO: this could be detected in the frontend - e.g. don't include any
687 // TODO: anon enum values if any are already there
688 // TODO: or maybe duplicates could be detected here
689 size_t size = tree_to_uhwi(TYPE_SIZE(target));
692 fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
696 generate("'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
697 names, names, size, TYPE_UNSIGNED(target) ? 'u' : 'i', size);
699 for (tree v = TYPE_VALUES(target); v != NULL; v = TREE_CHAIN (v)) {
700 generate("\t{ label => '%s', value => '%ld' },\n",
701 IDENTIFIER_POINTER(TREE_PURPOSE(v)),
702 tree_to_shwi(TREE_VALUE(v)));
707 case RECORD_TYPE: // forward declaration or opaque types
713 fprintf(stderr, "ignored %s: %s\n", ZTREE_CODE(target), names);
716 fprintf(stderr, "unknown type def: %s\n", ZTREE_CODE(target));
722 case FUNCTION_DECL: {
724 name = DECL_NAME(type);
727 names = IDENTIFIER_POINTER(name);
729 if (hash_lookup(&dumped, names))
731 hash_put(&dumped, node_alloc(type, names));
734 fprintf(stderr, "export type func decl %s\n", names);
736 generate("'func:%s' => { name => '%s', type => 'func',", names, names);
738 // FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
739 type = TREE_TYPE(type);
741 generate(" ctype => '%s',", print_generic_expr_to_str(type));
744 debug_tree_helper(type, "function 1");
746 tree result_type = TREE_TYPE(type);
747 const size_t data_size = value_size(TYPE_SIZE(result_type));
749 generate("\n\tresult => {");
750 export_param(type, result_type, data_size);
754 generate("\n\targuments => [\n");
755 //export_decl_params(DECL_ARGUMENTS(type), 0);
760 case FUNCTION_TYPE: {
761 // This is called for un-typedef'd function pointers.
762 // WHY IS THIS DIFFERENT TO ABOVE?
764 name = TYPE_IDENTIFIER(type);
767 names = IDENTIFIER_POINTER(name);
769 if (hash_lookup(&dumped, names))
771 hash_put(&dumped, node_alloc(type, names));
774 fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
776 generate("'call:%s' => { name => '%s', type => 'call',", names, names);
777 generate(" ctype => '%s',", print_generic_expr_to_str(type));
779 debug_tree_helper(type, "function type");
782 // FUNCTION_TYPE -> RESULT_TYPE
784 tree result = TREE_TYPE(type);
785 //tree result_type = TREE_TYPE(result);
786 //printf(" result type type %s\n", ZTREE_CODE(result_type));
787 const size_t data_size = value_size(TYPE_SIZE(result));
790 fprintf(stderr, " result size %zu\n", data_size);
791 generate("\n\tresult => {");
792 export_param(type, result, data_size);
796 stack_push(&context_stack, node_alloc(type, names));
797 generate("\n\targuments => [\n");
799 free(stack_pull(&context_stack));
803 case RECORD_TYPE: // struct
805 const char *su = TREE_CODE(type) == RECORD_TYPE ? "struct" : "union";
807 // ignore empty types
808 if (!TYPE_FIELDS(type))
812 name = TYPE_IDENTIFIER(type);
815 names = IDENTIFIER_POINTER(name);
817 if (hash_lookup(&dumped, names))
819 hash_put(&dumped, node_alloc(type, names));
822 fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
824 generate("'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
825 su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
827 stack_push(&context_stack, node_alloc(type, names));
828 export_fields(TYPE_FIELDS(type), 0, 0);
829 free(stack_pull(&context_stack));
834 case ENUMERAL_TYPE: {
835 // FIXME: see ENUMERAL_TYPE above regarding duplicate anon enums
837 // ignore empty enums
838 if (!TYPE_VALUES(type))
841 // We can only assume a non-named enum type isn't repeatedly sent here
843 name = TYPE_IDENTIFIER(type);
845 names = IDENTIFIER_POINTER(name);
848 if (hash_lookup(&dumped, names))
850 hash_put(&dumped, node_alloc(type, names));
853 size_t size = tree_to_uhwi(TYPE_SIZE(type));
856 fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
860 // FIXME: choose a better anon name
865 sprintf(nameb, "enum$%d", namei++);
870 fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
872 generate("'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
873 names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
875 for (tree v = TYPE_VALUES(type); v != NULL; v = TREE_CHAIN (v)) {
876 generate("\t{ label => '%s', value => '%ld' },\n",
877 IDENTIFIER_POINTER(TREE_PURPOSE(v)),
878 tree_to_shwi(TREE_VALUE(v)));
886 // capture PARM_DECLs so they can be used at next function declaration
888 name = DECL_NAME(type);
890 names = IDENTIFIER_POINTER(name);
892 // if this is a function pointer typedef, need to suck out the arguments at this point
893 deftype = TREE_TYPE(type);
895 target = target_type(deftype);
897 //fprintf(stderr, "type is '%s\n", ZTREE_CODE(deftype));
898 //fprintf(stderr, "target is '%s\n", ZTREE_CODE(target));
900 if (TREE_CODE(target) == FUNCTION_TYPE) {
901 // We need to save the list of parameters for later,
902 // it's keyed on target
903 struct node *fwd = node_alloc(target, NULL);
906 fprintf(stderr, "save forward reference function type %p\n", target);
908 for (tree param = TYPE_ARG_TYPES(target); param != NULL; param = TREE_CHAIN(param)) {
909 tree param_type = TREE_VALUE(param);
911 if (TREE_CODE(param_type) == VOID_TYPE)
914 struct node *decl = stack_pull(¶meters);
917 fprintf(stderr, "(pull parameter '%s')\n", decl->name);
918 stack_push(&fwd->list, decl);
920 fprintf(stderr, "WARNING: stack is missing parameter name function %s\n", names);
923 hash_put_bytype(&forward_types, fwd);
927 fprintf(stderr, "(push parameter '%s')\n", names);
928 stack_push(¶meters, node_alloc(type, names));
932 // global external variables, might want these
933 // well, if there's a way to resolve them
936 fprintf(stderr, "unknown export: %s\n", ZTREE_CODE(type));
941 static void plugin_finish_type(void *event_data, void *user_data) {
942 tree type = (tree)event_data;
945 fprintf(stderr, "finish_type\n");
949 export_type(type, NULL);
952 static void plugin_finish_decl(void *event_data, void *user_data) {
953 tree type = (tree)event_data;
956 fprintf(stderr, "finish_decl %s\n", ZTREE_CODE(type));
960 export_type(type, NULL);
963 static void plugin_finish(void *event_data, void *user_data) {
965 fprintf(stderr, "plugin finish\n");
966 for (struct node *n = todump.head; n; n=n->next) {
967 if (COMPLETE_TYPE_P(n->type)) {
969 export_type(n->type, n->name);
971 export_type(n->type, value_name(n->type));
976 generate("# dumped structs:\n");
977 for (struct node *n = dumped.list.head; n; n=n->next)
978 generate("# %s\n", n->name);
984 fprintf(stderr, "unhandled paramters:\n");
985 list_clear(¶meters);
988 int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
989 const char *output = NULL;
991 for (int i = 0; i < plugin_info->argc; ++i) {
992 if (0 == strcmp(plugin_info->argv[i].key, "output")) {
993 output = plugin_info->argv[i].value;
994 } else if (0 == strcmp(plugin_info->argv[i].key, "debug")) {
995 debug_level = atoi(plugin_info->argv[i].value);
999 if (NULL == output) {
1000 fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
1004 output_file = fopen(output, "w");
1005 if (NULL == output_file) {
1010 generate("%%data = (\n");
1012 register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
1013 register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
1014 register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);