unsigned int size;
};
+struct line_sequence
+{
+ bfd_vma low_pc;
+ struct line_sequence* prev_sequence;
+ struct line_info* last_line; /* Largest VMA. */
+};
+
struct line_info_table
{
- bfd* abfd;
- unsigned int num_files;
- unsigned int num_dirs;
- char *comp_dir;
- char **dirs;
- struct fileinfo* files;
- struct line_info* last_line; /* largest VMA */
- struct line_info* lcl_head; /* local head; used in 'add_line_info' */
+ bfd* abfd;
+ unsigned int num_files;
+ unsigned int num_dirs;
+ unsigned int num_sequences;
+ char * comp_dir;
+ char ** dirs;
+ struct fileinfo* files;
+ struct line_sequence* sequences;
+ struct line_info* lcl_head; /* Local head; used in 'add_line_info'. */
};
/* Remember some information about each function. If the function is
int end_sequence)
{
bfd_size_type amt = sizeof (struct line_info);
+ struct line_sequence* seq = table->sequences;
struct line_info* info = (struct line_info *) bfd_alloc (table->abfd, amt);
/* Set member data of 'info'. */
p...z a...j (where a < j < p < z)
Note: table->lcl_head is used to head an *actual* or *possible*
- sequence within the list (such as a...j) that is not directly
+ sub-sequence within the list (such as a...j) that is not directly
headed by table->last_line
Note: we may receive duplicate entries from 'decode_line_info'. */
- if (table->last_line
- && table->last_line->address == address
- && table->last_line->end_sequence == end_sequence)
+ if (seq
+ && seq->last_line->address == address
+ && seq->last_line->end_sequence == end_sequence)
{
/* We only keep the last entry with the same address and end
sequence. See PR ld/4986. */
- if (table->lcl_head == table->last_line)
+ if (table->lcl_head == seq->last_line)
table->lcl_head = info;
- info->prev_line = table->last_line->prev_line;
- table->last_line = info;
+ info->prev_line = seq->last_line->prev_line;
+ seq->last_line = info;
+ }
+ else if (!seq || seq->last_line->end_sequence)
+ {
+ /* Start a new line sequence. */
+ amt = sizeof (struct line_sequence);
+ seq = (struct line_sequence *) bfd_malloc (amt);
+ seq->low_pc = address;
+ seq->prev_sequence = table->sequences;
+ seq->last_line = info;
+ table->lcl_head = info;
+ table->sequences = seq;
+ table->num_sequences++;
}
- else if (!table->last_line
- || new_line_sorts_after (info, table->last_line))
+ else if (new_line_sorts_after (info, seq->last_line))
{
- /* Normal case: add 'info' to the beginning of the list */
- info->prev_line = table->last_line;
- table->last_line = info;
+ /* Normal case: add 'info' to the beginning of the current sequence. */
+ info->prev_line = seq->last_line;
+ seq->last_line = info;
/* lcl_head: initialize to head a *possible* sequence at the end. */
if (!table->lcl_head)
}
else
{
- /* Abnormal and hard: Neither 'last_line' nor 'lcl_head' are valid
- heads for 'info'. Reset 'lcl_head'. */
- struct line_info* li2 = table->last_line; /* always non-NULL */
+ /* Abnormal and hard: Neither 'last_line' nor 'lcl_head'
+ are valid heads for 'info'. Reset 'lcl_head'. */
+ struct line_info* li2 = seq->last_line; /* Always non-NULL. */
struct line_info* li1 = li2->prev_line;
while (li1)
table->lcl_head = li2;
info->prev_line = table->lcl_head->prev_line;
table->lcl_head->prev_line = info;
+ if (address < seq->low_pc)
+ seq->low_pc = address;
}
}
first_arange->next = arange;
}
+/* Compare function for line sequences. */
+
+static int
+compare_sequences (const void* a, const void* b)
+{
+ const struct line_sequence* seq1 = a;
+ const struct line_sequence* seq2 = b;
+
+ /* Sort by low_pc as the primary key. */
+ if (seq1->low_pc < seq2->low_pc)
+ return -1;
+ if (seq1->low_pc > seq2->low_pc)
+ return 1;
+
+ /* If low_pc values are equal, sort in reverse order of
+ high_pc, so that the largest region comes first. */
+ if (seq1->last_line->address < seq2->last_line->address)
+ return 1;
+ if (seq1->last_line->address > seq2->last_line->address)
+ return -1;
+
+ return 0;
+}
+
+/* Sort the line sequences for quick lookup. */
+
+static void
+sort_line_sequences (struct line_info_table* table)
+{
+ bfd_size_type amt;
+ struct line_sequence* sequences;
+ struct line_sequence* seq;
+ unsigned int n = 0;
+ unsigned int num_sequences = table->num_sequences;
+ bfd_vma last_high_pc;
+
+ if (num_sequences == 0)
+ return;
+
+ /* Allocate space for an array of sequences. */
+ amt = sizeof (struct line_sequence) * num_sequences;
+ sequences = (struct line_sequence *) bfd_alloc (table->abfd, amt);
+
+ /* Copy the linked list into the array, freeing the original nodes. */
+ seq = table->sequences;
+ for (n = 0; n < num_sequences; n++)
+ {
+ struct line_sequence* last_seq = seq;
+
+ BFD_ASSERT (seq);
+ sequences[n].low_pc = seq->low_pc;
+ sequences[n].prev_sequence = NULL;
+ sequences[n].last_line = seq->last_line;
+ seq = seq->prev_sequence;
+ free (last_seq);
+ }
+ BFD_ASSERT (seq == NULL);
+
+ qsort (sequences, n, sizeof (struct line_sequence), compare_sequences);
+
+ /* Make the list binary-searchable by trimming overlapping entries
+ and removing nested entries. */
+ num_sequences = 1;
+ last_high_pc = sequences[0].last_line->address;
+ for (n = 1; n < table->num_sequences; n++)
+ {
+ if (sequences[n].low_pc < last_high_pc)
+ {
+ if (sequences[n].last_line->address <= last_high_pc)
+ /* Skip nested entries. */
+ continue;
+
+ /* Trim overlapping entries. */
+ sequences[n].low_pc = last_high_pc;
+ }
+ last_high_pc = sequences[n].last_line->address;
+ if (n > num_sequences)
+ {
+ /* Close up the gap. */
+ sequences[num_sequences].low_pc = sequences[n].low_pc;
+ sequences[num_sequences].last_line = sequences[n].last_line;
+ }
+ num_sequences++;
+ }
+
+ table->sequences = sequences;
+ table->num_sequences = num_sequences;
+}
+
/* Decode the line number information for UNIT. */
static struct line_info_table*
table->num_dirs = 0;
table->dirs = NULL;
- table->files = NULL;
- table->last_line = NULL;
+ table->num_sequences = 0;
+ table->sequences = NULL;
+
table->lcl_head = NULL;
line_ptr = stash->dwarf_line_buffer + unit->line_offset;
free (filename);
}
+ sort_line_sequences (table);
+
return table;
}
const char **filename_ptr,
unsigned int *linenumber_ptr)
{
- /* Note: table->last_line should be a descendingly sorted list. */
+ struct line_sequence *seq = NULL;
struct line_info *each_line;
+ int low, high, mid;
+
+ /* Binary search the array of sequences. */
+ low = 0;
+ high = table->num_sequences;
+ while (low < high)
+ {
+ mid = (low + high) / 2;
+ seq = &table->sequences[mid];
+ if (addr < seq->low_pc)
+ high = mid;
+ else if (addr >= seq->last_line->address)
+ low = mid + 1;
+ else
+ break;
+ }
- for (each_line = table->last_line;
- each_line;
- each_line = each_line->prev_line)
- if (addr >= each_line->address)
- break;
-
- if (each_line
- && !(each_line->end_sequence || each_line == table->last_line))
+ if (seq && addr >= seq->low_pc && addr < seq->last_line->address)
{
- *filename_ptr = each_line->filename;
- *linenumber_ptr = each_line->line;
- return TRUE;
+ /* Note: seq->last_line should be a descendingly sorted list. */
+ for (each_line = seq->last_line;
+ each_line;
+ each_line = each_line->prev_line)
+ if (addr >= each_line->address)
+ break;
+
+ if (each_line
+ && !(each_line->end_sequence || each_line == seq->last_line))
+ {
+ *filename_ptr = each_line->filename;
+ *linenumber_ptr = each_line->line;
+ return TRUE;
+ }
}
*filename_ptr = NULL;
return FALSE;
}
-/* Read in the .debug_ranges section for future reference */
+/* Read in the .debug_ranges section for future reference. */
static bfd_boolean
read_debug_ranges (struct comp_unit *unit)