@@ -167,6 +167,33 @@ struct file_hash_entry_pool
struct cpp_file_hash_entry pool[FILE_HASH_POOL_SIZE];
};
+/* A set of attributes designed to quickly identify obviously different files
+ in a hashtable. Just in case there are collisions, we still maintain a
+ list. These sub-lists can then be checked for #pragma once rather than
+ interating through all_files. */
+struct file_quick_idempotency_attrs
+{
+ file_quick_idempotency_attrs(const _cpp_file *f)
+ : mtime(f->st.st_mtime), size(f->st.st_size) {}
+
+ time_t mtime;
+ off_t size;
+
+ static hashval_t hash (/* _cpp_file* */ const void *p);
+};
+
+/* Sub-list of very similar files kept in a hashtable to check for #pragma
+ once. */
+struct file_sublist
+{
+ _cpp_file *f;
+ file_sublist *next;
+
+ static int eq (/* _cpp_file* */ const void *p,
+ /* file_sublist* */ const void *q);
+ static void del (/* file_sublist* */ void *p);
+};
+
static bool open_file (_cpp_file *file);
static bool pch_open_file (cpp_reader *pfile, _cpp_file *file,
bool *invalid_pch);
@@ -849,17 +876,18 @@ has_unique_contents (cpp_reader *pfile, _cpp_file *file, bool import,
if (!pfile->seen_once_only)
return true;
- /* We may have read the file under a different name. Look
- for likely candidates and compare file contents to be sure. */
- for (_cpp_file *f = pfile->all_files; f; f = f->next_file)
+ /* We may have read the file under a different name. We've kept
+ similar looking files in this lists under this hash table, so
+ check those more thoroughly. */
+ void* ent = htab_find(pfile->pragma_once_files, file);
+ for (file_sublist *e = reinterpret_cast<file_sublist*> (ent); e; e = e->next)
{
+ _cpp_file *f = e->f;
if (f == file)
continue; /* It'sa me! */
if ((import || f->once_only)
- && f->err_no == 0
- && f->st.st_mtime == file->st.st_mtime
- && f->st.st_size == file->st.st_size)
+ && f->err_no == 0)
{
_cpp_file *ref_file;
@@ -895,6 +923,36 @@ has_unique_contents (cpp_reader *pfile, _cpp_file *file, bool import,
return true;
}
+/* Add the given file to the #pragma once table so it can be
+ quickly identified and excluded the next time it's seen. */
+static void
+update_pragma_once_table (cpp_reader *pfile, _cpp_file *file)
+{
+ void **slot = htab_find_slot (pfile->pragma_once_files, file, INSERT);
+ if (slot)
+ {
+ if (!*slot)
+ *slot = xcalloc(1, sizeof(file_sublist));
+
+ file_sublist *e = reinterpret_cast<file_sublist*> (*slot);
+ while (e->f)
+ {
+ if (!e->next)
+ e->next = reinterpret_cast<file_sublist*> (
+ xcalloc(1, sizeof(file_sublist)));
+ e = e->next;
+ }
+
+ e->f = file;
+ }
+ else
+ {
+ cpp_error (pfile, CPP_DL_ERROR,
+ "Unable to create #pragma once table space for %s",
+ _cpp_get_file_name(file));
+ }
+}
+
/* Place the file referenced by FILE into a new buffer on the buffer
stack if possible. Returns true if a buffer is stacked. Use LOC
for any diagnostics. */
@@ -950,6 +1008,9 @@ _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
if (!has_unique_contents (pfile, file, type == IT_IMPORT, loc))
return false;
+ if (pfile->seen_once_only && file->once_only)
+ update_pragma_once_table(pfile, file);
+
if (pfile->buffer && file->dir)
sysp = MAX (pfile->buffer->sysp, file->dir->sysp);
@@ -1434,6 +1495,41 @@ nonexistent_file_hash_eq (const void *p, const void *q)
return filename_cmp ((const char *) p, (const char *) q) == 0;
}
+/* Hasher for the #pragma once hash table. */
+hashval_t
+file_quick_idempotency_attrs::hash (const void *p)
+{
+ const _cpp_file *f = reinterpret_cast<const _cpp_file*> (p);
+ file_quick_idempotency_attrs kh (f);
+ return iterative_hash_object (kh, 0);
+}
+
+/* Equality checker for the #pragma once hash table. */
+int
+file_sublist::eq (const void *p, const void *q)
+{
+ /* Just check if the file q would be in the list p. Every
+ file in the list should have these attributes the same,
+ so we don't need to traverse. */
+ const file_sublist *e = reinterpret_cast<const file_sublist*> (p);
+ const _cpp_file *f = reinterpret_cast<const _cpp_file*> (q);
+ return f->st.st_mtime == e->f->st.st_mtime
+ && f->st.st_size == e->f->st.st_size;
+}
+
+/* Cleanup for a file sub-list. Does not free the _cpp_file
+ structures within. */
+void
+file_sublist::del (void *p)
+{
+ file_sublist *e = reinterpret_cast<file_sublist*> (p);
+ if (e->next)
+ {
+ file_sublist::del (e->next);
+ free (e->next);
+ }
+}
+
/* Initialize everything in this source file. */
void
_cpp_init_files (cpp_reader *pfile)
@@ -1442,6 +1538,10 @@ _cpp_init_files (cpp_reader *pfile)
NULL, xcalloc, free);
pfile->dir_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
NULL, xcalloc, free);
+ pfile->pragma_once_files = htab_create_alloc (127,
+ file_quick_idempotency_attrs::hash,
+ file_sublist::eq, file_sublist::del,
+ xcalloc, free);
allocate_file_hash_entries (pfile);
pfile->nonexistent_file_hash = htab_create_alloc (127, htab_hash_string,
nonexistent_file_hash_eq,
@@ -1456,6 +1556,7 @@ _cpp_cleanup_files (cpp_reader *pfile)
{
htab_delete (pfile->file_hash);
htab_delete (pfile->dir_hash);
+ htab_delete (pfile->pragma_once_files);
htab_delete (pfile->nonexistent_file_hash);
obstack_free (&pfile->nonexistent_file_ob, 0);
free_file_hash_entries (pfile);
@@ -485,6 +485,9 @@ struct cpp_reader
been used. */
bool seen_once_only;
+ /* Optimization for #pragma once. */
+ struct htab *pragma_once_files;
+
/* Multiple include optimization. */
const cpp_hashnode *mi_cmacro;
const cpp_hashnode *mi_ind_cmacro;