2 * export c types and prototypes to perl file.
4 * Copyright (c) 2019 Yonatan Goldschmidt
5 * Copyright (c) 2020 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
37 function declarations, i think
39 chain param type name size
40 TYPE_DECL TYPE_ARG_TYPES TYPE_VALUE(item):TREE_LIST <empty> TYPE_SIZE(item_type)
43 FUNCTION_DECL DECL_ARGUENTS TREE_TYPE(item):PARM_DECL DECL_NAME(item) DECL_SIZE(item)
50 #include <gcc-plugin.h>
52 #include <print-tree.h>
54 #include "tree-codes.h"
57 #define D(x) do { x; } while(0)
59 //Enums have a type, otherwise they're just integers
62 // remove some of the debug
65 int plugin_is_GPL_compatible; // must be defined for the plugin to run
67 static FILE *output_file;
69 static void debug_tree_helper(tree t, const char *msg) {
72 fprintf(stderr, "dumping tree: '%s'\n", msg);
74 fprintf(stderr, "\n\n");
79 static struct hash dumped;
80 static struct list todump;
81 static struct list context_stack;
84 Join all names in the stack, in reverse order.
86 static char *stack_path(struct list *stack, const char *sep) {
89 for (struct node *n = stack->tail; n; n=n->link)
90 total += strlen(n->name)+strlen(sep);
92 char *data = (char *)xmalloc(total);
95 // FIXME: some other context name
96 for (struct node *n = stack->tail; n; n=n->link) {
97 p = stpcpy(p, n->name);
105 // returns 0 if type has no size (i.e VOID_TYPE)
106 static bool is_struct_or_union(const_tree type) {
107 return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
110 static void print_spaces(int n) {
111 for (int i = 0; i < n; ++i)
112 fputc('\t', output_file);
115 static int is_ref_type(tree type) {
116 switch (TREE_CODE(type)) {
127 static tree target_type(tree type) {
128 while (is_ref_type(type))
129 type = TREE_TYPE(type);
133 static size_t value_size(tree n) {
134 return n ? tree_to_uhwi(n) : 0;
137 static const char *value_name(tree type) {
138 tree test = TYPE_IDENTIFIER(type);
139 const char *value = test ? IDENTIFIER_POINTER(test) : NULL;
141 // __va_list_tag is the final type beneath __builtin_va_list.
142 // it behaves different from other types - it has a TYPE_MAIN_VARIANT, but the main TYPE_NAME seems to give
143 // an unexpected tree, and therefore ORIG_TYPE_NAME returns a garbage value.
144 // I think this patch is good enough.
145 if (value && 0 == strcmp("__va_list_tag", value))
146 return "__va_list_tag";
148 test = TYPE_MAIN_VARIANT(type);
149 test = test ? TYPE_NAME(test) : test;
150 test = test && TREE_CODE(test) == TYPE_DECL ? DECL_NAME(test) : test;
152 return test ? IDENTIFIER_POINTER(test) : value;
156 Find a non-ref type in the type chain, i.e. skip pointers/arrays.
158 static tree simple_type(tree t) {
159 while (is_ref_type(t))
165 Create a 'panama' signature for a single type.
167 static void export_desc(tree field, tree field_type, struct buffer *b) {
168 const size_t data_size = value_size(TYPE_SIZE(field_type));
170 switch (TREE_CODE(field_type)) {
176 const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
179 if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
180 // it is a flexible array or empty types
183 // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
184 num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
188 b->pos += sprintf(b->data + b->pos, "[%zu", num_elem);
189 export_desc(field, TREE_TYPE(field_type), b);
196 b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
197 export_desc(field, TREE_TYPE(field_type), b);
199 case FUNCTION_TYPE: {
200 // TODO: handle void f() type -> null TYPE_ARG_TYPES()
201 tree return_type = TREE_TYPE(field_type);
204 for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
205 tree param_type = TREE_VALUE(param);
207 if (TREE_CODE(param_type) == VOID_TYPE)
210 export_desc(param, param_type, b);
213 export_desc(field, return_type, b);
217 #if defined(TYPED_ENUMS)
219 buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
223 b->pos += sprintf(b->data + b->pos, "%c%zu",
224 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
230 if (TYPE_IDENTIFIER(field_type)) {
231 list_add(&todump, node_alloc(field_type, NULL));
234 buffer_add(b, value_name(field_type));
237 char *name = stack_path(&context_stack, "_");
239 list_add(&todump, node_alloc(field_type, name));
250 b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
254 b->pos += sprintf(b->data + b->pos, "%c%zu",
255 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
259 debug_tree_helper(field_type, "unknown type!");
266 Print a single parameter or field.
268 static void export_param(tree field, tree field_type, size_t field_size) {
269 switch (TREE_CODE(field_type)) {
273 case REFERENCE_TYPE: {
276 buffer_init(&b, 256);
277 export_desc(field, field_type, &b);
278 fprintf(output_file, " deref => '%s',", b.data);
281 field_type = simple_type(field_type);
282 field_size = value_size(TYPE_SIZE(field_type));
284 export_param(field, field_type, field_size);
288 fprintf(output_file, " type => 'void',");
290 case ENUMERAL_TYPE: {
291 #if defined(TYPED_ENUMS)
292 const char *names = TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum";
293 fprintf(output_file, " type => 'enum:%s',", names);
295 fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
299 case FUNCTION_TYPE: {
300 // If the function is a typedef we could use the name
301 // but it's a pain to find up the tree, so don't bother.
302 // Use the signature instead. This is what jextract appears to use.
305 //char *name = stack_path(&context_stack);
306 //fprintf(output_file, " type => 'call:%s', ", name);
309 buffer_init(&b, 256);
311 export_desc(field, field_type, &b);
312 list_add(&todump, node_alloc(field_type, b.data));
313 fprintf(output_file, " type => 'call:%s', ", b.data);
320 fprintf(output_file, " ctype => '%s',", value_name(field_type));
321 fprintf(output_file, " type => 'f%zu',", field_size);
324 if (TREE_CODE(field) == FIELD_DECL && DECL_BIT_FIELD(field)) {
325 fprintf(output_file, " ctype => 'bitfield',");
326 fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', value_size(DECL_SIZE(field)));
328 fprintf(output_file, " ctype => '%s',", value_name(field_type));
329 fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
334 const char *us = TREE_CODE(field_type) == RECORD_TYPE ? "struct" : "union";
336 if (TYPE_IDENTIFIER(field_type)) {
337 fprintf(output_file, " type => '%s:%s',", us, value_name(field_type));
339 char *name = stack_path(&context_stack, "_");
341 list_add(&todump, node_alloc(field_type, name));
342 fprintf(output_file, " type => '%s:%s',", us, name);
348 printf("unknown param type: %s\n", ZTREE_CODE(field_type));
354 Export a chain of parameters
356 static void export_params(tree first_param, size_t indent_level) {
359 const char *names = nameb;
361 for (tree param = first_param; param; param = TREE_CHAIN(param)) {
362 tree param_type = TREE_VALUE(param);
363 const size_t data_size = value_size(TYPE_SIZE(param_type));
365 // non-varags functions end in VOID_TYPE
366 if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
369 fprintf(output_file, "\t\t{");
371 // size: do we need it?
372 fprintf(output_file, " size => %zu,", data_size);
374 // name: none available, use position
375 sprintf(nameb, "arg_%d", id);
377 fprintf(output_file, " name => '%s',", names);
378 stack_push(&context_stack, node_alloc(param, names));
381 export_param(param, param_type, data_size);
383 free(stack_pull(&context_stack));
385 fprintf(output_file, "},\n");
391 Export a chain of fields.
393 static void export_fields(tree first_field, size_t base_offset, int indent) {
394 for (tree field = first_field; field; field = TREE_CHAIN(field)) {
395 gcc_assert(TREE_CODE(field) == FIELD_DECL);
397 tree field_type = TREE_TYPE(field);
398 const size_t field_size = value_size(DECL_SIZE(field));
399 size_t offset = base_offset + tree_to_uhwi(DECL_FIELD_OFFSET(field)) * 8 + tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field));
401 // name: if none, then inline
402 if (!DECL_NAME(field)) {
403 if (is_struct_or_union(field_type))
404 export_fields(TYPE_FIELDS(field_type), offset, indent);
406 const char *names = IDENTIFIER_POINTER(DECL_NAME(field));
408 printf(" name=%s\n", names);
409 print_spaces(indent+1);
410 fprintf(output_file, "{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
411 stack_push(&context_stack, node_alloc(field, names));
414 export_param(field, field_type, field_size);
416 free(stack_pull(&context_stack));
418 fprintf(output_file, "},\n");
424 Main entry point for exporting any type.
426 static void export_type(tree type, const char *names) {
431 D(printf("export: %s %s\n", names, ZTREE_CODE(type)));
433 switch (TREE_CODE(type)) {
436 name = DECL_NAME(type);
439 names = IDENTIFIER_POINTER(name);
441 if (hash_lookup(&dumped, names))
443 hash_put(&dumped, node_alloc(type, names));
445 deftype = TREE_TYPE(type);
446 target = target_type(deftype);
447 switch (TREE_CODE(target)) {
448 case FUNCTION_TYPE: {
449 // I don't know if i even want this
451 fprintf(output_file, "'call:%s' => { name => '%s',", names, names);
453 // the deftype is always a pointer for a function_type
457 buffer_init(&b, 256);
458 export_desc(type, deftype, &b);
459 fprintf(output_file, " deref => '%s',", b.data);
464 tree result_type = TREE_TYPE(target);
465 const size_t data_size = value_size(TYPE_SIZE(result_type));
467 fprintf(output_file, "\n\tresult => {");
468 export_param(target, result_type, data_size);
469 fprintf(output_file, " },");
472 fprintf(output_file, "\n\targuments => [\n");
473 export_params(TYPE_ARG_TYPES(target), 0);
474 fprintf(output_file, "]},\n");
478 printf(" typedef record: %s\n", names);
481 printf(" typedef union: %s\n", names);
483 case ENUMERAL_TYPE: {
484 // TODO: typedef of anonymous enumerations may be repeated
485 // TODO: this could be detected in the frontend - e.g. don't include any
486 // TODO: anon enum values if any are already there
487 // TODO: or maybe duplicates could be detected here
488 size_t size = tree_to_uhwi(TYPE_SIZE(target));
491 fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
495 fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
496 names, names, size, TYPE_UNSIGNED(target) ? 'u' : 'i', size);
498 for (tree v = TYPE_VALUES(target); v != NULL; v = TREE_CHAIN (v)) {
499 fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
500 IDENTIFIER_POINTER(TREE_PURPOSE(v)),
501 tree_to_shwi(TREE_VALUE(v)));
503 fprintf(output_file, "]},\n");
511 printf("unknown type def: %s\n", ZTREE_CODE(target));
517 case FUNCTION_DECL: {
519 name = DECL_NAME(type);
522 names = IDENTIFIER_POINTER(name);
524 if (hash_lookup(&dumped, names))
526 hash_put(&dumped, node_alloc(type, names));
527 printf("export type func decl %s\n", names);
529 fprintf(output_file, "'func:%s' => { name => '%s', type => 'func',", names, names);
531 // FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
532 type = TREE_TYPE(type);
535 debug_tree_helper(type, "function 1");
537 tree result_type = TREE_TYPE(type);
538 const size_t data_size = value_size(TYPE_SIZE(result_type));
540 fprintf(output_file, "\n\tresult => {");
541 export_param(type, result_type, data_size);
542 fprintf(output_file, " },");
545 fprintf(output_file, "\n\targuments => [\n");
546 //export_decl_params(DECL_ARGUMENTS(type), 0);
547 export_params(TYPE_ARG_TYPES(type), 0);
548 fprintf(output_file, "]},\n");
551 case FUNCTION_TYPE: {
553 name = TYPE_IDENTIFIER(type);
556 names = IDENTIFIER_POINTER(name);
558 if (hash_lookup(&dumped, names))
560 hash_put(&dumped, node_alloc(type, names));
561 printf("export type func %s\n", names);
563 fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
565 debug_tree_helper(type, "function type");
568 // FUNCTION_TYPE -> RESULT_TYPE
570 tree result = TREE_TYPE(type);
571 //tree result_type = TREE_TYPE(result);
572 //printf(" result type type %s\n", ZTREE_CODE(result_type));
573 const size_t data_size = value_size(TYPE_SIZE(result));
575 printf(" result size %zu\n", data_size);
576 fprintf(output_file, "\n\tresult => {");
577 export_param(type, result, data_size);
578 fprintf(output_file, " },");
581 stack_push(&context_stack, node_alloc(type, names));
582 fprintf(output_file, "\n\targuments => [\n");
583 export_params(TYPE_ARG_TYPES(type), 0);
584 free(stack_pull(&context_stack));
585 fprintf(output_file, "]},\n");
588 case RECORD_TYPE: // struct
590 const char *su = TREE_CODE(type) == RECORD_TYPE ? "struct" : "union";
592 // ignore empty types
593 if (!TYPE_FIELDS(type))
597 name = TYPE_IDENTIFIER(type);
600 names = IDENTIFIER_POINTER(name);
602 if (hash_lookup(&dumped, names))
604 hash_put(&dumped, node_alloc(type, names));
606 fprintf(output_file, "'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
607 su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
609 stack_push(&context_stack, node_alloc(type, names));
610 export_fields(TYPE_FIELDS(type), 0, 0);
611 free(stack_pull(&context_stack));
613 fprintf(output_file, "]},\n");
616 case ENUMERAL_TYPE: {
617 // FIXME: see ENUMERAL_TYPE above regarding duplicate anon enums
619 // ignore empty enums
620 if (!TYPE_VALUES(type))
623 // We can only assume a non-named enum type isn't repeatedly sent here
625 name = TYPE_IDENTIFIER(type);
627 names = IDENTIFIER_POINTER(name);
630 if (hash_lookup(&dumped, names))
632 hash_put(&dumped, node_alloc(type, names));
635 size_t size = tree_to_uhwi(TYPE_SIZE(type));
638 fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
642 // FIXME: choose a better anon name
647 sprintf(nameb, "enum$%d", namei++);
650 fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
651 names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
653 for (tree v = TYPE_VALUES(type); v != NULL; v = TREE_CHAIN (v)) {
654 fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
655 IDENTIFIER_POINTER(TREE_PURPOSE(v)),
656 tree_to_shwi(TREE_VALUE(v)));
658 fprintf(output_file, "]},\n");
665 // global external variables, might want these
666 // well, if there's a way to resolve them
669 printf("unknown export: %s\n", ZTREE_CODE(type));
674 static void plugin_finish_type(void *event_data, void *user_data) {
675 tree type = (tree)event_data;
677 export_type(type, NULL);
680 static void plugin_finish_decl(void *event_data, void *user_data) {
681 tree type = (tree)event_data;
683 export_type(type, NULL);
686 static void plugin_finish(void *event_data, void *user_data) {
687 for (struct node *n = todump.head; n; n=n->next) {
688 if (COMPLETE_TYPE_P(n->type)) {
690 export_type(n->type, n->name);
692 export_type(n->type, value_name(n->type));
697 fprintf(output_file, "# dumped structs:\n");
698 for (struct node *n = dumped.list.head; n; n=n->next)
699 fprintf(output_file, "# %s\n", n->name);
701 fprintf(output_file, ");\n");
702 //fclose(output_file);
705 int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
706 const char *output = NULL;
708 for (int i = 0; i < plugin_info->argc; ++i) {
709 if (0 == strcmp(plugin_info->argv[i].key, "output")) {
710 output = plugin_info->argv[i].value;
714 if (NULL == output) {
715 fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
719 output_file = fopen(output, "w");
720 if (NULL == output_file) {
725 fprintf(output_file, "%%data = (\n");
727 register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
728 register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
729 register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);