From patchwork Sat Mar 20 16:26:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Harmstone X-Patchwork-Id: 1456149 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=qJWzvBGK; dkim-atps=neutral Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4F2mQd5cymz9sSC for ; Sun, 21 Mar 2021 03:28:09 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6D065386F826; Sat, 20 Mar 2021 16:27:17 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by sourceware.org (Postfix) with ESMTPS id 1CBC3385700D for ; Sat, 20 Mar 2021 16:27:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 1CBC3385700D Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=harmstone.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=mark.harmstone@gmail.com Received: by mail-wm1-x335.google.com with SMTP id 12so7063390wmf.5 for ; Sat, 20 Mar 2021 09:27:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=h26PqW7JARfQnKFLVWu16OFBQK2Sj4hRKiFcrVshAgQ=; b=qJWzvBGK3IcupgyULZjEcIKYvFo5DPGzeE+6Y29wB753apuR3ggfo86GEbxWOmCC3O ENViCX1HMKyUZXbs/A9RAMi0YXm6D4hls8piFktLitoR08ROf74OncfoWyI5hw6raYaH FTg2xveEZXYDc2WsIdtyxRzh1oObIcKofTYq9rKINYi4aifx3TmFzFhhHo+ymkcrCQF7 2t3VqqwnthieMPO9Ulyj0OXD64aGylNrh3tQ6qDrau9Pi8LmoeGXxroT2WPE7tAEx5xs Js0+c/yQNWfFIA+ezAlYPotDtRX75/8oKFpBoSrqeH8+tEGnxbzrLQqKkK0ZV3aItF3g B+2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; bh=h26PqW7JARfQnKFLVWu16OFBQK2Sj4hRKiFcrVshAgQ=; b=pFQBN/2XUqWV94gwnHZ32N4gJzLLat4kAtoJGlmI4CyngLrRcCYAQoogEEN6X7k8A+ pXrvVWknxlEb+nAjeV5j7qGiLvma1ZgMLbKJddFnYKAL8iN7N22T1u8CarvBGLR9Nn17 rbvZXYNjf6IdCUylULfIlU4gao/gn4E3ykm/Emrzp0VU1Brdm9Dd+NnpW9zrEycNIQuQ Eh4bR0CK2YJTABhCJNXoFxm3ddks4jPXvKS3l82abNu2TkeFt+qzY7+Dki3fWr7h68bY E2/hcA+0fxJN89v4dO7Y8GG2p+eaNyjk30PcdYP7jbbeMsNtfi98TT96xWe6lUvAGYlR quyg== X-Gm-Message-State: AOAM533S0VW3si3CxnqVfqjNElLU5Xid3JCsgAl+eY6r7uPq/VATn+mu sO1CYjhL1rglCWYid4GFcRKzUicTZeg= X-Google-Smtp-Source: ABdhPJziBBzUTLe0gDNOFqb2jZp3kLNXHCfl0Ze0VuUJU9cyzmZj+2djjtLj0/3kgELHMlZHiGkvlg== X-Received: by 2002:a1c:5416:: with SMTP id i22mr8384902wmb.146.1616257630704; Sat, 20 Mar 2021 09:27:10 -0700 (PDT) Received: from localhost.localdomain ([2a02:8010:64ea:0:fad1:11ff:fead:57db]) by smtp.gmail.com with ESMTPSA id v18sm14200785wrf.41.2021.03.20.09.27.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 20 Mar 2021 09:27:10 -0700 (PDT) From: Mark Harmstone To: gcc-patches@gcc.gnu.org Subject: [PATCH 14/24] pdbout: Output definitions of structs and classes. Date: Sat, 20 Mar 2021 16:26:42 +0000 Message-Id: <20210320162652.23346-14-mark@harmstone.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20210320162652.23346-1-mark@harmstone.com> References: <20210320162652.23346-1-mark@harmstone.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_EF, FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, UNWANTED_LANGUAGE_BODY autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Harmstone Errors-To: gcc-patches-bounces@gcc.gnu.org Sender: "Gcc-patches" --- gcc/pdbout.c | 469 ++++++++++++++++++++++++++++++++++++++++++++++++++- gcc/pdbout.h | 51 ++++++ 2 files changed, 517 insertions(+), 3 deletions(-) diff --git a/gcc/pdbout.c b/gcc/pdbout.c index 9701aaf8902..3bfec519877 100644 --- a/gcc/pdbout.c +++ b/gcc/pdbout.c @@ -83,6 +83,7 @@ static struct pdb_type *pointer_types = NULL; static struct pdb_type *proc_types = NULL; static struct pdb_type *modifier_types = NULL; static struct pdb_type *fieldlist_types = NULL; +static struct pdb_type *struct_types = NULL, *last_struct_type = NULL; static struct pdb_type *enum_types = NULL; static struct pdb_type *array_types = NULL; static struct pdb_alias *aliases = NULL; @@ -105,6 +106,7 @@ static struct pdb_type *complex16_type, *complex32_type, *complex48_type, static struct pdb_type *void_type, *nullptr_type; static bool builtins_initialized = false; static hash_table alias_hash_table (31); +static hash_table struct_hash_table (31); const struct gcc_debug_hooks pdb_debug_hooks = { pdbout_init, @@ -825,6 +827,17 @@ free_type (struct pdb_type *t) break; } + case LF_CLASS: + case LF_STRUCTURE: + { + struct pdb_struct *str = (struct pdb_struct *) t->data; + + if (str->name) + free (str->name); + + break; + } + case LF_ENUM: { struct pdb_enum *en = (struct pdb_enum *) t->data; @@ -839,7 +852,8 @@ free_type (struct pdb_type *t) free (t); } -/* Output a lfFieldlist structure, which describes the values of an enum. */ +/* Output a lfFieldlist structure, which describes the fields of a struct, + * class, or union, or the values of an enum. */ static void write_fieldlist (struct pdb_fieldlist *fl) { @@ -851,6 +865,10 @@ write_fieldlist (struct pdb_fieldlist *fl) switch (fl->entries[i].cv_type) { + case LF_MEMBER: + len += 9 + (fl->entries[i].name ? strlen (fl->entries[i].name) : 0); + break; + case LF_ENUMERATE: len += 5; @@ -904,6 +922,43 @@ write_fieldlist (struct pdb_fieldlist *fl) switch (fl->entries[i].cv_type) { + case LF_MEMBER: + { + size_t name_len = + fl->entries[i].name ? strlen (fl->entries[i].name) : 0; + unsigned int align; + + fprintf (asm_out_file, "\t.short\t0x%x\n", + fl->entries[i].fld_attr); + fprintf (asm_out_file, "\t.short\t0x%x\n", + fl->entries[i].type ? fl->entries[i].type->id : 0); + fprintf (asm_out_file, "\t.short\t0\n"); // padding + fprintf (asm_out_file, "\t.short\t0x%x\n", fl->entries[i].offset); + + if (fl->entries[i].name) + ASM_OUTPUT_ASCII (asm_out_file, fl->entries[i].name, + name_len + 1); + else + fprintf (asm_out_file, "\t.byte\t0\n"); + + // handle alignment padding + + align = 4 - ((3 + name_len) % 4); + + if (align != 4) + { + if (align == 3) + fprintf (asm_out_file, "\t.byte\t0xf3\n"); + + if (align >= 2) + fprintf (asm_out_file, "\t.byte\t0xf2\n"); + + fprintf (asm_out_file, "\t.byte\t0xf1\n"); + } + + break; + } + case LF_ENUMERATE: { size_t name_len = @@ -1000,6 +1055,48 @@ write_fieldlist (struct pdb_fieldlist *fl) } } +/* Output a lfClass / lfStructure struct. */ +static void +write_struct (uint16_t type, struct pdb_struct *str) +{ + size_t name_len = str->name ? strlen (str->name) : (sizeof (unnamed) - 1); + unsigned int len = 23 + name_len, align; + + if (len % 4 != 0) + len += 4 - (len % 4); + + fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2); + fprintf (asm_out_file, "\t.short\t0x%x\n", type); + fprintf (asm_out_file, "\t.short\t0x%x\n", str->count); + fprintf (asm_out_file, "\t.short\t0x%x\n", str->property.value); + fprintf (asm_out_file, "\t.short\t0x%x\n", + str->field_type ? str->field_type->id : 0); + fprintf (asm_out_file, "\t.short\t0\n"); // derived + fprintf (asm_out_file, "\t.short\t0\n"); // vshape + fprintf (asm_out_file, "\t.short\t0\n"); + fprintf (asm_out_file, "\t.short\t0\n"); + fprintf (asm_out_file, "\t.short\t0\n"); + fprintf (asm_out_file, "\t.short\t0x%x\n", str->size); + + if (str->name) + ASM_OUTPUT_ASCII (asm_out_file, str->name, name_len + 1); + else + ASM_OUTPUT_ASCII (asm_out_file, unnamed, sizeof (unnamed)); + + align = 4 - ((3 + name_len) % 4); + + if (align != 4) + { + if (align == 3) + fprintf (asm_out_file, "\t.byte\t0xf3\n"); + + if (align >= 2) + fprintf (asm_out_file, "\t.byte\t0xf2\n"); + + fprintf (asm_out_file, "\t.byte\t0xf1\n"); + } +} + /* Output a lfEnum structure. */ static void write_enum (struct pdb_enum *en) @@ -1186,6 +1283,11 @@ write_type (struct pdb_type *t) write_fieldlist ((struct pdb_fieldlist *) t->data); break; + case LF_CLASS: + case LF_STRUCTURE: + write_struct (t->cv_type, (struct pdb_struct *) t->data); + break; + case LF_ENUM: write_enum ((struct pdb_enum *) t->data); break; @@ -1326,6 +1428,13 @@ get_tree_name (tree t) if (TREE_CODE (t) == FUNCTION_DECL) name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t))); + else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE) + name = xstrdup (IDENTIFIER_POINTER (TYPE_NAME (t))); + else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == TYPE_DECL + && IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)))[0] != '.') + name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)))); + else if (DECL_NAME (t) && TREE_CODE (DECL_NAME (t)) == IDENTIFIER_NODE) + name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t))); else return NULL; @@ -1396,7 +1505,73 @@ pdbout_late_global_decl (tree var) global_vars = v; } -/* Add a fieldlist, which is the basis of enums. */ +/* Allocate a pdb_type for a forward declaration for a struct. The debugger + * will resolve this automatically, by searching for a substantive + * struct definition with the same name. */ +static struct pdb_type * +add_struct_forward_declaration (tree t, const char *name) +{ + struct pdb_type *strtype; + struct pdb_struct *str; + + if (name) + { + strtype = + struct_hash_table.find_with_hash (name, struct_hasher::hash (name)); + + if (strtype) + return strtype; + } + + strtype = + (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) + + sizeof (struct pdb_struct)); + + if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t)) + strtype->cv_type = LF_CLASS; + else + strtype->cv_type = LF_STRUCTURE; + + strtype->tree = NULL; + strtype->next = strtype->next2 = NULL; + strtype->id = 0; + + str = (struct pdb_struct *) strtype->data; + str->count = 0; + str->field_type = NULL; + str->size = 0; + str->property.value = 0; + str->property.s.fwdref = 1; + str->name = name ? xstrdup (name) : NULL; + + if (last_struct_type) + last_struct_type->next2 = strtype; + else + struct_types = strtype; + + last_struct_type = strtype; + + if (last_type) + last_type->next = strtype; + else + types = strtype; + + last_type = strtype; + + if (name) + { + struct pdb_type **slot = + struct_hash_table.find_slot_with_hash (name, + struct_hasher::hash (name), + INSERT); + *slot = strtype; + } + + return strtype; +} + +/* Add a fieldlist - this type does double duty as the basis + * of both enums and structs. */ static struct pdb_type * add_type_fieldlist (struct pdb_type *t) { @@ -1425,7 +1600,20 @@ add_type_fieldlist (struct pdb_type *t) break; } - if (pfe1->cv_type == LF_ENUMERATE) + if (pfe1->cv_type == LF_MEMBER) + { + if (pfe1->type != pfe2->type || + pfe1->offset != pfe2->offset || + pfe1->fld_attr != pfe2->fld_attr || + ((pfe1->name || pfe2->name) && + (!pfe1->name || !pfe2->name || + strcmp (pfe1->name, pfe2->name)))) + { + same = false; + break; + } + } + else if (pfe1->cv_type == LF_ENUMERATE) { if (pfe1->value != pfe2->value || ((pfe1->name || pfe2->name) && @@ -1477,6 +1665,278 @@ add_type_fieldlist (struct pdb_type *t) return t; } +inline hashval_t +struct_hasher::hash (struct_hasher::compare_type name) +{ + return htab_hash_string (name); +} + +inline bool +struct_hasher::equal (const value_type type, compare_type name) +{ + struct pdb_struct *str = (struct pdb_struct *) type->data; + + return !strcmp (str->name, name); +} + +/* For a given struct or class, allocate a new pdb_type and + * add it to the type list. */ +static struct pdb_type * +find_type_struct (tree t) +{ + tree f; + struct pdb_type *fltype = NULL, *strtype, *fwddef = NULL, + *last_entry = NULL; + struct pdb_fieldlist *fieldlist; + struct pdb_fieldlist_entry *ent; + struct pdb_struct *str; + unsigned int num_entries = 0; + bool new_fwddef = false; + char *name = get_tree_name (t); + uint16_t size = + TYPE_SIZE (t) ? (TREE_INT_CST_ELT (TYPE_SIZE (t), 0) / 8) : 0; + union pdb_property prop; + struct pdb_type **slot; + + if (name) + { + strtype = + struct_hash_table.find_with_hash (name, struct_hasher::hash (name)); + + /* Use type found in hash map, unless this is a substantive definition + * replacing a forward declaration. */ + + if (strtype) + { + str = (struct pdb_struct *) strtype->data; + + if (TYPE_SIZE (t) == 0 || str->property.s.fwdref != 1) + return strtype; + } + } + + f = TYPE_FIELDS (t); + + while (f) + { + if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f)) + { + if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f))) + num_entries++; + else + { // anonymous field + struct pdb_type *type = find_type (TREE_TYPE (f)); + + if (type + && (type->cv_type == LF_CLASS + || type->cv_type == LF_STRUCTURE)) + { + struct pdb_struct *str2 = (struct pdb_struct *) type->data; + + if (str2->field_type) + { + struct pdb_fieldlist *fl = + (struct pdb_fieldlist *) str2->field_type->data; + + // count fields of anonymous struct or union as our own + + num_entries += fl->count; + } + } + } + } + + f = TREE_CHAIN (f); + } + + if (TYPE_SIZE (t) != 0) // not forward declaration + { + fwddef = add_struct_forward_declaration (t, name); + + if (!fwddef->tree) + { + new_fwddef = true; + fwddef->tree = t; + + slot = + tree_hash_table.find_slot_with_hash (t, + pdb_type_tree_hasher:: + hash (t), INSERT); + *slot = fwddef; + } + } + + if (num_entries > 0) + { + // add fieldlist type + + fltype = + (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) + + offsetof (struct pdb_fieldlist, + entries) + + (num_entries * + sizeof (struct pdb_fieldlist_entry))); + fltype->cv_type = LF_FIELDLIST; + fltype->tree = NULL; + + fieldlist = (struct pdb_fieldlist *) fltype->data; + fieldlist->count = num_entries; + + ent = fieldlist->entries; + f = TYPE_FIELDS (t); + + while (f) + { + if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f)) + { + unsigned int bit_offset = + (TREE_INT_CST_ELT (DECL_FIELD_OFFSET (f), 0) * 8) + + TREE_INT_CST_ELT (DECL_FIELD_BIT_OFFSET (f), 0); + + if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f))) + { + + ent->cv_type = LF_MEMBER; + ent->fld_attr = CV_FLDATTR_PUBLIC; + ent->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (f))); + + ent->type = find_type (TREE_TYPE (f)); + ent->offset = bit_offset / 8; + + ent++; + } + else // anonymous field + { + struct pdb_type *type = find_type (TREE_TYPE (f)); + + if (type + && (type->cv_type == LF_CLASS + || type->cv_type == LF_STRUCTURE)) + { + struct pdb_struct *str2 = + (struct pdb_struct *) type->data; + + if (str2->field_type) + { + struct pdb_fieldlist *fl = + (struct pdb_fieldlist *) str2->field_type->data; + + // treat fields of anonymous struct or union + // as our own + + for (unsigned int i = 0; i < fl->count; i++) + { + ent->cv_type = fl->entries[i].cv_type; + ent->type = fl->entries[i].type; + ent->offset = + (bit_offset / 8) + fl->entries[i].offset; + ent->fld_attr = fl->entries[i].fld_attr; + ent->name = + fl->entries[i].name ? + xstrdup (fl->entries[i].name) : NULL; + + ent++; + } + } + } + } + } + + f = TREE_CHAIN (f); + } + + fltype = add_type_fieldlist (fltype); + } + + // add type for struct + + prop.value = 0; + + if (!TYPE_SIZE (t)) // forward declaration + prop.s.fwdref = 1; + + if (!name) + { + strtype = struct_types; + while (strtype) + { + str = (struct pdb_struct *) strtype->data; + + if (str->count == num_entries && + str->field_type == fltype && + str->size == size && + str->property.value == prop.value && + !str->name) + return strtype; + + last_entry = strtype; + strtype = strtype->next2; + } + } + else + last_entry = last_struct_type; + + strtype = + (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) + + sizeof (struct pdb_struct)); + + if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t)) + strtype->cv_type = LF_CLASS; + else + strtype->cv_type = LF_STRUCTURE; + + if (TYPE_SIZE (t) != 0) // not forward declaration + strtype->tree = t; + else + strtype->tree = NULL; + + strtype->next = strtype->next2 = NULL; + strtype->id = 0; + + str = (struct pdb_struct *) strtype->data; + str->count = num_entries; + str->field_type = fltype; + str->size = size; + str->property.value = prop.value; + str->name = name; + + if (last_entry) + last_entry->next2 = strtype; + else + struct_types = strtype; + + last_struct_type = strtype; + + if (last_type) + last_type->next = strtype; + else + types = strtype; + + last_type = strtype; + + if (new_fwddef) + fwddef->tree = NULL; + + if (strtype->tree) + { + slot = + tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), + INSERT); + *slot = strtype; + } + + if (name) + { + slot = + struct_hash_table.find_slot_with_hash (name, + struct_hasher::hash (name), + INSERT); + *slot = strtype; + } + + return strtype; +} + /* Given an array type t, allocate a new pdb_type and add it to the * type list. */ static struct pdb_type * @@ -2283,6 +2743,9 @@ find_type (tree t) case ARRAY_TYPE: return find_type_array (t); + case RECORD_TYPE: + return find_type_struct (t); + case ENUMERAL_TYPE: return find_type_enum (t); diff --git a/gcc/pdbout.h b/gcc/pdbout.h index 85ce1f68ee5..96288d235ea 100644 --- a/gcc/pdbout.h +++ b/gcc/pdbout.h @@ -39,7 +39,10 @@ #define LF_FIELDLIST 0x1203 #define LF_ENUMERATE 0x1502 #define LF_ARRAY 0x1503 +#define LF_CLASS 0x1504 +#define LF_STRUCTURE 0x1505 #define LF_ENUM 0x1507 +#define LF_MEMBER 0x150d #define LF_CHAR 0x8000 #define LF_SHORT 0x8001 #define LF_USHORT 0x8002 @@ -169,6 +172,37 @@ struct pdb_fieldlist struct pdb_fieldlist_entry entries[1]; }; +union pdb_property +{ // CV_prop_t in cvdump + struct + { + uint16_t packed:1; + uint16_t ctor:1; + uint16_t ovlops:1; + uint16_t isnested:1; + uint16_t cnested:1; + uint16_t opassign:1; + uint16_t opcast:1; + uint16_t fwdref:1; + uint16_t scoped:1; + uint16_t hasuniquename:1; + uint16_t sealed:1; + uint16_t hfa:2; + uint16_t intrinsic:1; + uint16_t mocom:2; + } s; + uint16_t value; +}; + +struct pdb_struct +{ + unsigned int count; + struct pdb_type *field_type; + uint16_t size; + union pdb_property property; + char *name; +}; + struct pdb_enum { unsigned int count; @@ -1287,4 +1321,21 @@ struct alias_hasher : nofree_ptr_hash static inline bool equal (const value_type, compare_type); }; +struct struct_hasher : nofree_ptr_hash +{ + typedef struct pdb_type *value_type; + typedef const char *compare_type; + + static inline hashval_t hash (compare_type); + + static inline hashval_t hash (const value_type t) + { + struct pdb_struct *str = (struct pdb_struct *) t->data; + + return hash (str->name); + } + + static inline bool equal (const value_type, compare_type); +}; + #endif