* export c types and prototypes to perl file.
*
* Copyright (c) 2019 Yonatan Goldschmidt
- * Copyright (c) 2020 Michael Zucchi
+ * Copyright (c) 2020,2021 Michael Zucchi
*
* The MIT License (MIT)
*
#include <gcc-plugin.h>
#include <tree.h>
#include <print-tree.h>
+#include <tree-pretty-print.h>
-#include "tree-codes.h"
#include "list.h"
#define D(x) do { x; } while(0)
+extern const char *tree_codes[];
+#define ZTREE_CODE(x) tree_codes[TREE_CODE(x)]
+
//Enums have a type, otherwise they're just integers
//#define TYPED_ENUMS
int plugin_is_GPL_compatible; // must be defined for the plugin to run
static FILE *output_file;
+static int debug_level = 0;
static void debug_tree_helper(tree t, const char *msg) {
#ifndef NDEBUG
- fflush(stdout);
- fprintf(stderr, "dumping tree: '%s'\n", msg);
- debug_tree(t);
- fprintf(stderr, "\n\n");
- fflush(stdout);
+ if (debug_level > 2) {
+ fflush(stdout);
+ fprintf(stderr, "dumping tree: '%s'\n", msg);
+ debug_tree(t);
+ fprintf(stderr, "\n\n");
+ fflush(stdout);
+ }
#endif
}
static struct hash dumped;
static struct list todump;
static struct list context_stack;
+static struct list parameters; // last list of params
+
+static struct hash forward_types;
/*
Join all names in the stack, in reverse order.
- */
+*/
static char *stack_path(struct list *stack, const char *sep) {
size_t total = 1;
return data;
}
+static void list_clear(struct list *list) {
+ struct node *node;
+
+ while ((node = list_remhead(list))) {
+ if (debug_level > 2)
+ fprintf(stderr, " free: %s\n", node->name);
+ free(node);
+ }
+}
+
// returns 0 if type has no size (i.e VOID_TYPE)
static bool is_struct_or_union(const_tree type) {
- return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
+ return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
}
static void print_spaces(int n) {
- for (int i = 0; i < n; ++i)
- fputc('\t', output_file);
+ for (int i = 0; i < n; ++i)
+ fputc('\t', output_file);
}
static int is_ref_type(tree type) {
/*
Find a non-ref type in the type chain, i.e. skip pointers/arrays.
- */
+*/
static tree simple_type(tree t) {
while (is_ref_type(t))
t = TREE_TYPE(t);
/*
Create a 'panama' signature for a single type.
- */
+*/
static void export_desc(tree field, tree field_type, struct buffer *b) {
const size_t data_size = value_size(TYPE_SIZE(field_type));
#else
buffer_room(b, 16);
b->pos += sprintf(b->data + b->pos, "%c%zu",
- TYPE_UNSIGNED(field_type) ? 'u' : 'i',
- data_size);
+ TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+ data_size);
+#endif
+ break;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ if (TYPE_IDENTIFIER(field_type)) {
+ list_add(&todump, node_alloc(field_type, NULL));
+
+ buffer_add(b, "${");
+ buffer_add(b, value_name(field_type));
+ buffer_add(b, "}");
+ } else {
+ char *name = stack_path(&context_stack, "_");
+
+ list_add(&todump, node_alloc(field_type, name));
+
+ buffer_add(b, "${");
+ buffer_add(b, name);
+ buffer_add(b, "}");
+
+ free(name);
+ }
+ break;
+ case REAL_TYPE:
+ buffer_room(b, 16);
+ b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
+ break;
+ case INTEGER_TYPE:
+ buffer_room(b, 16);
+ b->pos += sprintf(b->data + b->pos, "%c%zu",
+ TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+ data_size);
+ break;
+ default:
+ debug_tree_helper(field_type, "unknown type!");
+ gcc_unreachable();
+ break;
+ }
+}
+
+/*
+ Create a 'c' description of type.
+*/
+static void export_cdesc(tree field, tree field_type, struct buffer *b) {
+ const size_t data_size = value_size(TYPE_SIZE(field_type));
+
+ switch (TREE_CODE(field_type)) {
+ case VOID_TYPE:
+ buffer_add(b, "void");
+ break;
+ case VECTOR_TYPE:
+ case ARRAY_TYPE: {
+ const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
+ size_t num_elem;
+
+ if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
+ // it is a flexible array or empty types
+ num_elem = 0;
+ } else {
+ // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
+ num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
+ }
+
+ export_cdesc(field, TREE_TYPE(field_type), b);
+
+ buffer_room(b, 16);
+ buffer_add(b, "[");
+ if (num_elem)
+ b->pos += sprintf(b->data + b->pos, "%zu", num_elem);
+ buffer_add(b, "]");
+ break;
+ }
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ buffer_room(b, 16);
+ buffer_add(b, "*");
+ //b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
+ export_cdesc(field, TREE_TYPE(field_type), b);
+ break;
+ case FUNCTION_TYPE: {
+ // TODO: handle void f() type -> null TYPE_ARG_TYPES()
+ tree return_type = TREE_TYPE(field_type);
+ int args = 0;
+
+ export_cdesc(field, return_type, b);
+
+ buffer_add(b, "(*)");
+ buffer_add(b, "(");
+ for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
+ tree param_type = TREE_VALUE(param);
+
+ // TREE_TYPE might? point to a type that a previous decl has also referenced
+
+ if (args++)
+ buffer_add(b, ", ");
+
+ if (TREE_CODE(param_type) == VOID_TYPE) {
+ buffer_add(b, "void");
+ break;
+ }
+
+ export_cdesc(param, param_type, b);
+ }
+ buffer_add(b, ")");
+ break;
+ }
+ case ENUMERAL_TYPE:
+#if defined(TYPED_ENUMS)
+ buffer_add(b, "${");
+ buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
+ buffer_add(b, "}");
+#else
+ buffer_room(b, 16);
+ b->pos += sprintf(b->data + b->pos, "%c%zu",
+ TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+ data_size);
#endif
break;
case RECORD_TYPE:
case INTEGER_TYPE:
buffer_room(b, 16);
b->pos += sprintf(b->data + b->pos, "%c%zu",
- TYPE_UNSIGNED(field_type) ? 'u' : 'i',
- data_size);
+ TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+ data_size);
break;
default:
debug_tree_helper(field_type, "unknown type!");
/*
Print a single parameter or field.
- */
+*/
static void export_param(tree field, tree field_type, size_t field_size) {
switch (TREE_CODE(field_type)) {
case VECTOR_TYPE:
}
case VOID_TYPE:
fprintf(output_file, " type => 'void',");
+ fprintf(output_file, " ctype => 'void',");
break;
case ENUMERAL_TYPE: {
#if defined(TYPED_ENUMS)
#else
fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
#endif
+ fprintf(output_file, " ctype => 'enum %s',", value_name(field_type));
break;
}
case FUNCTION_TYPE: {
- // If the function is a typedef we could use the name
- // but it's a pain to find up the tree, so don't bother.
- // Use the signature instead. This is what jextract appears to use.
struct buffer b;
+ tree root_type = TREE_TYPE(field);
- //char *name = stack_path(&context_stack);
- //fprintf(output_file, " type => 'call:%s', ", name);
- //free(name);
+ // If this is a typedef we might have a name for the type, otherwise it's a signature based name
+ if (root_type && TYPE_IDENTIFIER(root_type)) {
+ fprintf(output_file, " type => 'call:%s', ", value_name(root_type));
+ } else {
+ fprintf(stderr, "save for later param type %p\n", field_type);
+ buffer_init(&b, 256);
+ export_desc(field, field_type, &b);
+ list_add(&todump, node_alloc(field_type, b.data));
+ fprintf(output_file, " type => 'call:%s', ", b.data);
+ }
buffer_init(&b, 256);
-
- export_desc(field, field_type, &b);
- list_add(&todump, node_alloc(field_type, b.data));
- fprintf(output_file, " type => 'call:%s', ", b.data);
-
+ export_cdesc(field, field_type, &b);
free(b.data);
break;
break;
}
default:
- printf("unknown param type: %s\n", ZTREE_CODE(field_type));
+ fprintf(stderr, "unknown param type: %s\n", ZTREE_CODE(field_type));
gcc_unreachable();
}
}
/*
Export a chain of parameters
*/
-static void export_params(tree first_param, size_t indent_level) {
+static void export_params(tree func) {
int id = 0;
- char nameb[16];
- const char *names = nameb;
+ char nameb[32];
+ struct list args = { 0 };
+ struct node *name;
+ struct node *fwd = hash_lookup_bytype(&forward_types, func);
+
+ if (fwd) {
+ // use the forward reference to find the names
+ fprintf(stderr, "found forward reference @ %p\n", fwd);
+ name = fwd->list.head;
+ } else {
+ // paramter names are in the paramters list
+ // but they are in reverse order
+ int id = 0;
+ for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
+ tree param_type = TREE_VALUE(param);
+
+ if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
+ break;
+
+ struct node *decl = stack_pull(¶meters);
+ if (decl) {
+ fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+ stack_push(&args, decl);
+ } else
+ fprintf(stderr, "ERROR: parameter %d missing parameter declaration\n", id);
+ id++;
+ }
+ name = args.head;
+ }
- for (tree param = first_param; param; param = TREE_CHAIN(param)) {
+ for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
tree param_type = TREE_VALUE(param);
const size_t data_size = value_size(TYPE_SIZE(param_type));
+ const char *names = NULL;
// non-varags functions end in VOID_TYPE
if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
// size: do we need it?
fprintf(output_file, " size => %zu,", data_size);
- // name: none available, use position
- sprintf(nameb, "arg_%d", id);
+ if (name) {
+ // this should be a parm_decl with an identifier of the name
+ names = name->name;
+
+ // can one check it's a matching type?
+ name = name->next;
+ }
+
+ if (!names || !names[0]) {
+ sprintf(nameb, "arg_%d", id);
+ names = nameb;
+ }
fprintf(output_file, " name => '%s',", names);
stack_push(&context_stack, node_alloc(param, names));
fprintf(output_file, "},\n");
id++;
}
+
+ list_clear(&args);
}
/*
} else {
const char *names = IDENTIFIER_POINTER(DECL_NAME(field));
- printf(" name=%s\n", names);
+ if (debug_level > 1)
+ fprintf(stderr, " field: %s\n", names);
print_spaces(indent+1);
fprintf(output_file, "{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
stack_push(&context_stack, node_alloc(field, names));
tree deftype;
tree target;
- D(printf("export: %s %s\n", names, ZTREE_CODE(type)));
+ if (debug_level > 1)
+ fprintf(stderr, "export_type(%s, %s)\n", ZTREE_CODE(type), names);
switch (TREE_CODE(type)) {
case TYPE_DECL: {
return;
hash_put(&dumped, node_alloc(type, names));
+ if (debug_level > 1)
+ fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
deftype = TREE_TYPE(type);
target = target_type(deftype);
switch (TREE_CODE(target)) {
case FUNCTION_TYPE: {
+ // function pointer typdef
// I don't know if i even want this
-
- fprintf(output_file, "'call:%s' => { name => '%s',", names, names);
+ fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
// the deftype is always a pointer for a function_type
fprintf(output_file, " deref => '%s',", b.data);
free(b.data);
+ fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(target));
+
// TODO: cleanup
{
tree result_type = TREE_TYPE(target);
}
fprintf(output_file, "\n\targuments => [\n");
- export_params(TYPE_ARG_TYPES(target), 0);
+ export_params(target);
fprintf(output_file, "]},\n");
break;
}
- case RECORD_TYPE:
- printf(" typedef record: %s\n", names);
- break;
- case UNION_TYPE:
- printf(" typedef union: %s\n", names);
- break;
case ENUMERAL_TYPE: {
// TODO: typedef of anonymous enumerations may be repeated
// TODO: this could be detected in the frontend - e.g. don't include any
fprintf(output_file, "]},\n");
break;
}
+ case RECORD_TYPE: // forward declaration or opaque types
+ case UNION_TYPE:
case VOID_TYPE:
case INTEGER_TYPE:
case REAL_TYPE:
+ if (debug_level)
+ fprintf(stderr, "ignored %s: %s\n", ZTREE_CODE(target), names);
break;
default:
- printf("unknown type def: %s\n", ZTREE_CODE(target));
+ fprintf(stderr, "unknown type def: %s\n", ZTREE_CODE(target));
gcc_unreachable();
}
if (hash_lookup(&dumped, names))
return;
hash_put(&dumped, node_alloc(type, names));
- printf("export type func decl %s\n", names);
+
+ if (debug_level > 1)
+ fprintf(stderr, "export type func decl %s\n", names);
fprintf(output_file, "'func:%s' => { name => '%s', type => 'func',", names, names);
// FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
type = TREE_TYPE(type);
+ fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
+
// TODO: cleanup
debug_tree_helper(type, "function 1");
{
fprintf(output_file, "\n\targuments => [\n");
//export_decl_params(DECL_ARGUMENTS(type), 0);
- export_params(TYPE_ARG_TYPES(type), 0);
+ export_params(type);
fprintf(output_file, "]},\n");
break;
}
case FUNCTION_TYPE: {
+ // This is called for un-typedef'd function pointers.
+ // WHY IS THIS DIFFERENT TO ABOVE?
if (!names) {
name = TYPE_IDENTIFIER(type);
if (!name)
if (hash_lookup(&dumped, names))
return;
hash_put(&dumped, node_alloc(type, names));
- printf("export type func %s\n", names);
+
+ if (debug_level > 1)
+ fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
+ fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
debug_tree_helper(type, "function type");
//printf(" result type type %s\n", ZTREE_CODE(result_type));
const size_t data_size = value_size(TYPE_SIZE(result));
- printf(" result size %zu\n", data_size);
+ if (debug_level > 2)
+ fprintf(stderr, " result size %zu\n", data_size);
fprintf(output_file, "\n\tresult => {");
export_param(type, result, data_size);
fprintf(output_file, " },");
stack_push(&context_stack, node_alloc(type, names));
fprintf(output_file, "\n\targuments => [\n");
- export_params(TYPE_ARG_TYPES(type), 0);
+ export_params(type);
free(stack_pull(&context_stack));
fprintf(output_file, "]},\n");
break;
return;
hash_put(&dumped, node_alloc(type, names));
+ if (debug_level > 1)
+ fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
fprintf(output_file, "'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
sprintf(nameb, "enum$%d", namei++);
names = nameb;
}
+
+ if (debug_level > 1)
+ fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
break;
}
case FIELD_DECL:
- case PARM_DECL:
break;
+ case PARM_DECL: {
+ // capture PARM_DECLs so they can be used at next function declaration
+ if (!names) {
+ name = DECL_NAME(type);
+ if (name)
+ names = IDENTIFIER_POINTER(name);
+ }
+ // if this is a function pointer typedef, need to suck out the arguments at this point
+ deftype = TREE_TYPE(type);
+
+ target = target_type(deftype);
+
+ //fprintf(stderr, "type is '%s\n", ZTREE_CODE(deftype));
+ //fprintf(stderr, "target is '%s\n", ZTREE_CODE(target));
+
+ if (TREE_CODE(target) == FUNCTION_TYPE) {
+ // We need to save the list of parameters for later,
+ // it's keyed on target
+ struct node *fwd = node_alloc(target, NULL);
+
+ fprintf(stderr, "save forward reference function type %p\n", target);
+
+ for (tree param = TYPE_ARG_TYPES(target); param != NULL; param = TREE_CHAIN(param)) {
+ tree param_type = TREE_VALUE(param);
+
+ if (TREE_CODE(param_type) == VOID_TYPE)
+ break;
+
+ struct node *decl = stack_pull(¶meters);
+ if (decl) {
+ fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+ stack_push(&fwd->list, decl);
+ } else
+ fprintf(stderr, " missing parameter name\n");
+ }
+
+ hash_put_bytype(&forward_types, fwd);
+ }
+
+ fprintf(stderr, "(push parameter '%s')\n", names);
+ stack_push(¶meters, node_alloc(type, names));
+
+ break; }
case VAR_DECL:
// global external variables, might want these
// well, if there's a way to resolve them
break;
default:
- printf("unknown export: %s\n", ZTREE_CODE(type));
+ fprintf(stderr, "unknown export: %s\n", ZTREE_CODE(type));
gcc_unreachable();
}
}
static void plugin_finish_type(void *event_data, void *user_data) {
tree type = (tree)event_data;
+ if (debug_level > 0)
+ fprintf(stderr, "finish_type\n");
+ if (debug_level > 1)
+ debug_tree(type);
+
export_type(type, NULL);
}
static void plugin_finish_decl(void *event_data, void *user_data) {
tree type = (tree)event_data;
+ if (debug_level > 0)
+ fprintf(stderr, "finish_decl %s\n", ZTREE_CODE(type));
+ if (debug_level > 1)
+ debug_tree(type);
+
export_type(type, NULL);
}
static void plugin_finish(void *event_data, void *user_data) {
+ fprintf(stderr, "plugin finish\n");
for (struct node *n = todump.head; n; n=n->next) {
if (COMPLETE_TYPE_P(n->type)) {
if (n->name[0]) {
fprintf(output_file, "# %s\n", n->name);
fprintf(output_file, ");\n");
- //fclose(output_file);
+ fclose(output_file);
+
+ fprintf(stderr, "unhandled paramters:\n");
+ list_clear(¶meters);
}
int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
- const char *output = NULL;
+ const char *output = NULL;
- for (int i = 0; i < plugin_info->argc; ++i) {
- if (0 == strcmp(plugin_info->argv[i].key, "output")) {
- output = plugin_info->argv[i].value;
- }
- }
+ for (int i = 0; i < plugin_info->argc; ++i) {
+ if (0 == strcmp(plugin_info->argv[i].key, "output")) {
+ output = plugin_info->argv[i].value;
+ } else if (0 == strcmp(plugin_info->argv[i].key, "debug")) {
+ debug_level = atoi(plugin_info->argv[i].value);
+ }
+ }
- if (NULL == output) {
- fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
- exit(EXIT_FAILURE);
- }
+ if (NULL == output) {
+ fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
+ exit(EXIT_FAILURE);
+ }
- output_file = fopen(output, "w");
- if (NULL == output_file) {
- perror(output);
- exit(EXIT_FAILURE);
- }
+ output_file = fopen(output, "w");
+ if (NULL == output_file) {
+ perror(output);
+ exit(EXIT_FAILURE);
+ }
- fprintf(output_file, "%%data = (\n");
+ fprintf(output_file, "%%data = (\n");
- register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
- register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
- register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
+ register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
+ register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
+ register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
- return 0;
+ return 0;
}