summaryrefslogtreecommitdiff
path: root/gcc/godump.c
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2014-10-28 17:29:40 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2014-10-28 17:29:40 +0000
commit4d099c1ce3953eab1056ee2161235426c98c26b8 (patch)
treeb48b4e78ca9ac203cf3205763cdd885ebe111c72 /gcc/godump.c
parent1140c305a273735f263f6ce9aa04c853af7912e6 (diff)
downloadgcc-4d099c1ce3953eab1056ee2161235426c98c26b8.tar.gz
gcc/:
* godump.c (precision_to_units): New helper function. (go_append_artificial_name): Ditto. (go_append_decl_name): Ditto. (go_append_bitfield): Ditto. (go_get_uinttype_for_precision): Ditto. (go_append_padding): Ditto. (go_force_record_alignment): Ditto. (go_format_type): Represent unions with an array of uints of the size of the alignment in go. This fixes the 'random' size of the union's representation using just the first field. (go_format_type): Add argument that indicates whether a record is nested (used for generation of artificial go names). (go_output_fndecl): Adapt to new go_format_type signature. (go_output_typedef): Ditto. (go_output_var): Ditto. (go_output_var): Prefer to output type as alias (typedef). (go_format_type): Bitfields in records are simulated as arrays of bytes in go. * godump.c (go_format_type): Fix handling of arrays with zero elements. gcc/testsuite/: * gcc.misc-tests/godump.exp: New. * gcc.misc-tests/godump-1.c: New. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@216806 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/godump.c')
-rw-r--r--gcc/godump.c361
1 files changed, 267 insertions, 94 deletions
diff --git a/gcc/godump.c b/gcc/godump.c
index 5e4b7fcb0e7..7a0566485f6 100644
--- a/gcc/godump.c
+++ b/gcc/godump.c
@@ -37,6 +37,8 @@ along with GCC; see the file COPYING3. If not see
#include "obstack.h"
#include "debug.h"
#include "wide-int-print.h"
+#include "stor-layout.h"
+#include "defaults.h"
/* We dump this information from the debug hooks. This gives us a
stable and maintainable API to hook into. In order to work
@@ -73,6 +75,15 @@ struct macro_hash_value
char *value;
};
+/* Returns the number of units necessary to represent an integer with the given
+ PRECISION (in bits). */
+
+static inline unsigned int
+precision_to_units (unsigned int precision)
+{
+ return (precision + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+}
+
/* Calculate the hash value for an entry in the macro hash table. */
static hashval_t
@@ -552,19 +563,132 @@ go_append_string (struct obstack *ob, tree id)
obstack_grow (ob, IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
}
+/* Given an integer PRECISION in bits, returns a constant string that is the
+ matching go int or uint type (depending on the IS_UNSIGNED flag). Returns a
+ NULL pointer if there is no matching go type. */
+
+static const char *
+go_get_uinttype_for_precision (unsigned int precision, bool is_unsigned)
+{
+ switch (precision)
+ {
+ case 8:
+ return is_unsigned ? "uint8" : "int8";
+ case 16:
+ return is_unsigned ? "uint16" : "int16";
+ case 32:
+ return is_unsigned ? "uint32" : "int32";
+ case 64:
+ return is_unsigned ? "uint64" : "int64";
+ default:
+ return NULL;
+ }
+}
+
+/* Append an artificial variable name with the suffix _INDEX to OB. Returns
+ INDEX + 1. */
+
+static unsigned int
+go_append_artificial_name (struct obstack *ob, unsigned int index)
+{
+ char buf[100];
+
+ /* FIXME: identifier may not be unique. */
+ obstack_grow (ob, "Godump_", 7);
+ snprintf (buf, sizeof buf, "%u", index);
+ obstack_grow (ob, buf, strlen (buf));
+
+ return index + 1;
+}
+
+/* Append the variable name from DECL to OB. If the name is in the
+ KEYWORD_HASH, prepend an '_'. */
+
+static void
+go_append_decl_name (struct obstack *ob, tree decl, htab_t keyword_hash)
+{
+ const char *var_name;
+ void **slot;
+
+ /* Start variable name with an underscore if a keyword. */
+ var_name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ slot = htab_find_slot (keyword_hash, var_name, NO_INSERT);
+ if (slot != NULL)
+ obstack_1grow (ob, '_');
+ go_append_string (ob, DECL_NAME (decl));
+}
+
+/* Appends a byte array with the necessary number of elements and the name
+ "Godump_INDEX_pad" to pad from FROM_OFFSET to TO_OFFSET to OB assuming that
+ the next field is automatically aligned to ALIGN_UNITS. Returns INDEX + 1,
+ or INDEX if no padding had to be appended. The resulting offset where the
+ next field is allocated is returned through RET_OFFSET. */
+
+static unsigned int
+go_append_padding (struct obstack *ob, unsigned int from_offset,
+ unsigned int to_offset, unsigned int align_units,
+ unsigned int index, unsigned int *ret_offset)
+{
+ if (from_offset % align_units > 0)
+ from_offset += align_units - (from_offset % align_units);
+ gcc_assert (to_offset >= from_offset);
+ if (to_offset > from_offset)
+ {
+ char buf[100];
+
+ index = go_append_artificial_name (ob, index);
+ snprintf (buf, sizeof buf, "_pad [%u]byte; ", to_offset - from_offset);
+ obstack_grow (ob, buf, strlen (buf));
+ }
+ *ret_offset = to_offset;
+
+ return index;
+}
+
+/* Appends an array of type TYPE_STRING with zero elements and the name
+ "Godump_INDEX_align" to OB. If TYPE_STRING is a null pointer, ERROR_STRING
+ is appended instead of the type. Returns INDEX + 1. */
+
+static unsigned int
+go_force_record_alignment (struct obstack *ob, const char *type_string,
+ unsigned int index, const char *error_string)
+{
+ index = go_append_artificial_name (ob, index);
+ obstack_grow (ob, "_align ", 7);
+ if (type_string == NULL)
+ obstack_grow (ob, error_string, strlen (error_string));
+ else
+ {
+ obstack_grow (ob, "[0]", 3);
+ obstack_grow (ob, type_string, strlen (type_string));
+ }
+ obstack_grow (ob, "; ", 2);
+
+ return index;
+}
+
/* Write the Go version of TYPE to CONTAINER->TYPE_OBSTACK.
USE_TYPE_NAME is true if we can simply use a type name here without
needing to define it. IS_FUNC_OK is true if we can output a func
- type here; the "func" keyword will already have been added. Return
- true if the type can be represented in Go, false otherwise. */
+ type here; the "func" keyword will already have been added.
+ Return true if the type can be represented in Go, false otherwise.
+ P_ART_I is used for indexing artificial elements in nested structures and
+ should always be a NULL pointer when called, except by certain recursive
+ calls from go_format_type() itself. */
static bool
go_format_type (struct godump_container *container, tree type,
- bool use_type_name, bool is_func_ok)
+ bool use_type_name, bool is_func_ok, unsigned int *p_art_i)
{
bool ret;
struct obstack *ob;
+ unsigned int art_i_dummy;
+ if (p_art_i == NULL)
+ {
+ art_i_dummy = 0;
+ p_art_i = &art_i_dummy;
+ }
ret = true;
ob = &container->type_obstack;
@@ -618,27 +742,15 @@ go_format_type (struct godump_container *container, tree type,
const char *s;
char buf[100];
- switch (TYPE_PRECISION (type))
+ s = go_get_uinttype_for_precision (TYPE_PRECISION (type),
+ TYPE_UNSIGNED (type));
+ if (s == NULL)
{
- case 8:
- s = TYPE_UNSIGNED (type) ? "uint8" : "int8";
- break;
- case 16:
- s = TYPE_UNSIGNED (type) ? "uint16" : "int16";
- break;
- case 32:
- s = TYPE_UNSIGNED (type) ? "uint32" : "int32";
- break;
- case 64:
- s = TYPE_UNSIGNED (type) ? "uint64" : "int64";
- break;
- default:
snprintf (buf, sizeof buf, "INVALID-int-%u%s",
TYPE_PRECISION (type),
TYPE_UNSIGNED (type) ? "u" : "");
s = buf;
ret = false;
- break;
}
obstack_grow (ob, s, strlen (s));
}
@@ -710,7 +822,7 @@ go_format_type (struct godump_container *container, tree type,
else
{
if (!go_format_type (container, TREE_TYPE (type), use_type_name,
- true))
+ true, NULL))
ret = false;
}
break;
@@ -732,64 +844,64 @@ go_format_type (struct godump_container *container, tree type,
tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (type))));
obstack_grow (ob, buf, strlen (buf));
}
+ else
+ obstack_1grow (ob, '0');
obstack_1grow (ob, ']');
- if (!go_format_type (container, TREE_TYPE (type), use_type_name, false))
+ if (!go_format_type (container, TREE_TYPE (type), use_type_name, false,
+ NULL))
ret = false;
break;
- case UNION_TYPE:
case RECORD_TYPE:
{
+ unsigned int prev_field_end;
+ unsigned int most_strict_known_alignment;
tree field;
- int i;
+ /* FIXME: Why is this necessary? Without it we can get a core
+ dump on the s390x headers, or from a file containing simply
+ "typedef struct S T;". */
+ layout_type (type);
+
+ prev_field_end = 0;
+ most_strict_known_alignment = 1;
obstack_grow (ob, "struct { ", 9);
- i = 0;
for (field = TYPE_FIELDS (type);
field != NULL_TREE;
field = TREE_CHAIN (field))
{
- struct obstack hold_type_obstack;
bool field_ok;
- if (TREE_CODE (type) == UNION_TYPE)
- {
- hold_type_obstack = container->type_obstack;
- obstack_init (&container->type_obstack);
- }
-
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
field_ok = true;
-
- if (DECL_NAME (field) == NULL)
- {
- char buf[100];
-
- obstack_grow (ob, "Godump_", 7);
- snprintf (buf, sizeof buf, "%d", i);
- obstack_grow (ob, buf, strlen (buf));
- i++;
- }
- else
- {
- const char *var_name;
- void **slot;
-
- /* Start variable name with an underscore if a keyword. */
- var_name = IDENTIFIER_POINTER (DECL_NAME (field));
- slot = htab_find_slot (container->keyword_hash, var_name,
- NO_INSERT);
- if (slot != NULL)
- obstack_1grow (ob, '_');
- go_append_string (ob, DECL_NAME (field));
- }
- obstack_1grow (ob, ' ');
if (DECL_BIT_FIELD (field))
- {
- obstack_grow (ob, "INVALID-bit-field", 17);
- field_ok = false;
- }
+ continue;
else
{
+ {
+ unsigned int decl_align_unit;
+ unsigned int decl_offset;
+
+ decl_align_unit = DECL_ALIGN_UNIT (field);
+ decl_offset =
+ TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field))
+ + precision_to_units
+ (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field)));
+ if (decl_align_unit > most_strict_known_alignment)
+ most_strict_known_alignment = decl_align_unit;
+ *p_art_i = go_append_padding
+ (ob, prev_field_end, decl_offset, decl_align_unit, *p_art_i,
+ &prev_field_end);
+ if (DECL_SIZE_UNIT (field))
+ prev_field_end += TREE_INT_CST_LOW (DECL_SIZE_UNIT (field));
+ }
+ if (DECL_NAME (field) == NULL)
+ *p_art_i = go_append_artificial_name (ob, *p_art_i);
+ else
+ go_append_decl_name (ob, field, container->keyword_hash);
+ obstack_1grow (ob, ' ');
+
/* Do not expand type if a record or union type or a
function pointer. */
if (TYPE_NAME (TREE_TYPE (field)) != NULL_TREE
@@ -815,40 +927,76 @@ go_format_type (struct godump_container *container, tree type,
else
{
if (!go_format_type (container, TREE_TYPE (field), true,
- false))
+ false, p_art_i))
field_ok = false;
}
+ obstack_grow (ob, "; ", 2);
}
- obstack_grow (ob, "; ", 2);
+ if (!field_ok)
+ ret = false;
+ }
+ /* Alignment and padding as necessary. */
+ {
+ unsigned int type_align_unit;
+
+ type_align_unit = TYPE_ALIGN_UNIT (type);
+ /* Padding. */
+ *p_art_i = go_append_padding
+ (ob, prev_field_end, TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type)),
+ type_align_unit, *p_art_i, &prev_field_end);
+ if (most_strict_known_alignment < type_align_unit)
+ {
+ const char *s;
+ char buf[100];
- /* Only output the first successful field of a union, and
- hope for the best. */
- if (TREE_CODE (type) == UNION_TYPE)
+ /* Enforce proper record alignment. */
+ s = go_get_uinttype_for_precision
+ (TYPE_ALIGN (type), TYPE_UNSIGNED (type));
+ if (s == NULL)
{
- if (!field_ok && TREE_CHAIN (field) == NULL_TREE)
- {
- field_ok = true;
- ret = false;
- }
- if (field_ok)
- {
- unsigned int sz;
-
- sz = obstack_object_size (&container->type_obstack);
- obstack_grow (&hold_type_obstack,
- obstack_base (&container->type_obstack),
- sz);
- }
- obstack_free (&container->type_obstack, NULL);
- container->type_obstack = hold_type_obstack;
- if (field_ok)
- break;
+ snprintf (buf, sizeof buf, "INVALID-int-%u%s",
+ TYPE_ALIGN (type), TYPE_UNSIGNED (type) ? "u" : "");
+ s = buf;
+ ret = false;
}
+ *p_art_i = go_force_record_alignment (ob, s, *p_art_i, buf);
+ }
+ }
+ obstack_1grow (ob, '}');
+ }
+ break;
+
+ case UNION_TYPE:
+ {
+ const char *s;
+ unsigned int sz_units;
+
+ layout_type (type);
+ sz_units = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type));
+ s = go_get_uinttype_for_precision (TYPE_ALIGN (type), true);
+ obstack_grow (ob, "struct { ", 9);
+ if (s == NULL)
+ {
+ ret = false;
+ s = "INVALID-union-alignment";
+ obstack_grow (ob, s, strlen (s));
+ }
+ else
+ {
+ char buf[100];
+ tree field;
+
+ field = TYPE_FIELDS (type);
+ /* Use the same index as the byte field's artificial name for
+ padding. */
+ if (field != NULL_TREE && DECL_NAME (field) != NULL)
+ go_append_decl_name (ob, field, container->keyword_hash);
else
- {
- if (!field_ok)
- ret = false;
- }
+ *p_art_i = go_append_artificial_name (ob, *p_art_i);
+ snprintf (buf, sizeof buf, " [%u]byte; ", sz_units);
+ obstack_grow (ob, buf, strlen (buf));
+ if (TYPE_ALIGN_UNIT (type) > 1)
+ *p_art_i = go_force_record_alignment (ob, s, *p_art_i, NULL);
}
obstack_1grow (ob, '}');
}
@@ -879,7 +1027,7 @@ go_format_type (struct godump_container *container, tree type,
break;
if (seen_arg)
obstack_grow (ob, ", ", 2);
- if (!go_format_type (container, arg_type, true, false))
+ if (!go_format_type (container, arg_type, true, false, NULL))
ret = false;
seen_arg = true;
}
@@ -895,7 +1043,7 @@ go_format_type (struct godump_container *container, tree type,
if (!VOID_TYPE_P (result))
{
obstack_1grow (ob, ' ');
- if (!go_format_type (container, result, use_type_name, false))
+ if (!go_format_type (container, result, use_type_name, false, NULL))
ret = false;
}
}
@@ -929,7 +1077,7 @@ go_output_type (struct godump_container *container)
static void
go_output_fndecl (struct godump_container *container, tree decl)
{
- if (!go_format_type (container, TREE_TYPE (decl), false, true))
+ if (!go_format_type (container, TREE_TYPE (decl), false, true, NULL))
fprintf (go_dump_file, "// ");
fprintf (go_dump_file, "func _%s ",
IDENTIFIER_POINTER (DECL_NAME (decl)));
@@ -1004,7 +1152,7 @@ go_output_typedef (struct godump_container *container, tree decl)
return;
*slot = CONST_CAST (void *, (const void *) type);
- if (!go_format_type (container, TREE_TYPE (decl), false, false))
+ if (!go_format_type (container, TREE_TYPE (decl), false, false, NULL))
{
fprintf (go_dump_file, "// ");
slot = htab_find_slot (container->invalid_hash, type, INSERT);
@@ -1040,7 +1188,7 @@ go_output_typedef (struct godump_container *container, tree decl)
return;
*slot = CONST_CAST (void *, (const void *) type);
- if (!go_format_type (container, TREE_TYPE (decl), false, false))
+ if (!go_format_type (container, TREE_TYPE (decl), false, false, NULL))
{
fprintf (go_dump_file, "// ");
slot = htab_find_slot (container->invalid_hash, type, INSERT);
@@ -1069,6 +1217,8 @@ static void
go_output_var (struct godump_container *container, tree decl)
{
bool is_valid;
+ tree type_name;
+ tree id;
if (container->decls_seen.contains (decl)
|| container->decls_seen.contains (DECL_NAME (decl)))
@@ -1076,7 +1226,32 @@ go_output_var (struct godump_container *container, tree decl)
container->decls_seen.add (decl);
container->decls_seen.add (DECL_NAME (decl));
- is_valid = go_format_type (container, TREE_TYPE (decl), true, false);
+ type_name = TYPE_NAME (TREE_TYPE (decl));
+ id = NULL_TREE;
+ if (type_name != NULL_TREE && TREE_CODE (type_name) == IDENTIFIER_NODE)
+ id = type_name;
+ else if (type_name != NULL_TREE && TREE_CODE (type_name) == TYPE_DECL
+ && DECL_SOURCE_LOCATION (type_name) != BUILTINS_LOCATION
+ && DECL_NAME (type_name))
+ id = DECL_NAME (type_name);
+ if (id != NULL_TREE
+ && (!htab_find_slot (container->type_hash, IDENTIFIER_POINTER (id),
+ NO_INSERT)
+ || htab_find_slot (container->invalid_hash, IDENTIFIER_POINTER (id),
+ NO_INSERT)))
+ id = NULL_TREE;
+ if (id != NULL_TREE)
+ {
+ struct obstack *ob;
+
+ ob = &container->type_obstack;
+ obstack_1grow (ob, '_');
+ go_append_string (ob, id);
+ is_valid = htab_find_slot (container->type_hash, IDENTIFIER_POINTER (id),
+ NO_INSERT) != NULL;
+ }
+ else
+ is_valid = go_format_type (container, TREE_TYPE (decl), true, false, NULL);
if (is_valid
&& htab_find_slot (container->type_hash,
IDENTIFIER_POINTER (DECL_NAME (decl)),
@@ -1096,10 +1271,8 @@ go_output_var (struct godump_container *container, tree decl)
/* Sometimes an extern variable is declared with an unknown struct
type. */
- if (TYPE_NAME (TREE_TYPE (decl)) != NULL_TREE
- && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
+ if (type_name != NULL_TREE && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
{
- tree type_name = TYPE_NAME (TREE_TYPE (decl));
if (TREE_CODE (type_name) == IDENTIFIER_NODE)
container->pot_dummy_types.add (IDENTIFIER_POINTER (type_name));
else if (TREE_CODE (type_name) == TYPE_DECL)