+
+#if DEBUG
+
+static void
+print_action_list (FILE *fp, text_action_list *action_list)
+{
+ text_action *r;
+
+ fprintf (fp, "Text Action\n");
+ for (r = action_list->head; r != NULL; r = r->next)
+ {
+ const char *t = "unknown";
+ switch (r->action)
+ {
+ case ta_remove_insn:
+ t = "remove_insn"; break;
+ case ta_remove_longcall:
+ t = "remove_longcall"; break;
+ case ta_convert_longcall:
+ t = "convert_longcall"; break;
+ case ta_narrow_insn:
+ t = "narrow_insn"; break;
+ case ta_widen_insn:
+ t = "widen_insn"; break;
+ case ta_fill:
+ t = "fill"; break;
+ case ta_none:
+ t = "none"; break;
+ case ta_remove_literal:
+ t = "remove_literal"; break;
+ case ta_add_literal:
+ t = "add_literal"; break;
+ }
+
+ fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
+ r->sec->owner->filename,
+ r->sec->name, (unsigned long) r->offset, t, r->removed_bytes);
+ }
+}
+
+#endif /* DEBUG */
+
+\f
+/* Lists of literals being coalesced or removed. */
+
+/* In the usual case, the literal identified by "from" is being
+ coalesced with another literal identified by "to". If the literal is
+ unused and is being removed altogether, "to.abfd" will be NULL.
+ The removed_literal entries are kept on a per-section list, sorted
+ by the "from" offset field. */
+
+typedef struct removed_literal_struct removed_literal;
+typedef struct removed_literal_list_struct removed_literal_list;
+
+struct removed_literal_struct
+{
+ r_reloc from;
+ r_reloc to;
+ removed_literal *next;
+};
+
+struct removed_literal_list_struct
+{
+ removed_literal *head;
+ removed_literal *tail;
+};
+
+
+/* Record that the literal at "from" is being removed. If "to" is not
+ NULL, the "from" literal is being coalesced with the "to" literal. */
+
+static void
+add_removed_literal (removed_literal_list *removed_list,
+ const r_reloc *from,
+ const r_reloc *to)
+{
+ removed_literal *r, *new_r, *next_r;
+
+ new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal));
+
+ new_r->from = *from;
+ if (to)
+ new_r->to = *to;
+ else
+ new_r->to.abfd = NULL;
+ new_r->next = NULL;
+
+ r = removed_list->head;
+ if (r == NULL)
+ {
+ removed_list->head = new_r;
+ removed_list->tail = new_r;
+ }
+ /* Special check for common case of append. */
+ else if (removed_list->tail->from.target_offset < from->target_offset)
+ {
+ removed_list->tail->next = new_r;
+ removed_list->tail = new_r;
+ }
+ else
+ {
+ while (r->from.target_offset < from->target_offset && r->next)
+ {
+ r = r->next;
+ }
+ next_r = r->next;
+ r->next = new_r;
+ new_r->next = next_r;
+ if (next_r == NULL)
+ removed_list->tail = new_r;
+ }
+}
+
+
+/* Check if the list of removed literals contains an entry for the
+ given address. Return the entry if found. */
+
+static removed_literal *
+find_removed_literal (removed_literal_list *removed_list, bfd_vma addr)
+{
+ removed_literal *r = removed_list->head;
+ while (r && r->from.target_offset < addr)
+ r = r->next;
+ if (r && r->from.target_offset == addr)
+ return r;
+ return NULL;
+}
+
+
+#if DEBUG
+
+static void
+print_removed_literals (FILE *fp, removed_literal_list *removed_list)
+{
+ removed_literal *r;
+ r = removed_list->head;
+ if (r)
+ fprintf (fp, "Removed Literals\n");
+ for (; r != NULL; r = r->next)
+ {
+ print_r_reloc (fp, &r->from);
+ fprintf (fp, " => ");
+ if (r->to.abfd == NULL)
+ fprintf (fp, "REMOVED");
+ else
+ print_r_reloc (fp, &r->to);
+ fprintf (fp, "\n");
+ }
+}
+
+#endif /* DEBUG */
+
+\f
+/* Per-section data for relaxation. */
+
+typedef struct reloc_bfd_fix_struct reloc_bfd_fix;
+
+struct xtensa_relax_info_struct
+{
+ bfd_boolean is_relaxable_literal_section;
+ bfd_boolean is_relaxable_asm_section;
+ int visited; /* Number of times visited. */
+
+ source_reloc *src_relocs; /* Array[src_count]. */
+ int src_count;
+ int src_next; /* Next src_relocs entry to assign. */
+
+ removed_literal_list removed_list;
+ text_action_list action_list;
+
+ reloc_bfd_fix *fix_list;
+ reloc_bfd_fix *fix_array;
+ unsigned fix_array_count;
+
+ /* Support for expanding the reloc array that is stored
+ in the section structure. If the relocations have been
+ reallocated, the newly allocated relocations will be referenced
+ here along with the actual size allocated. The relocation
+ count will always be found in the section structure. */
+ Elf_Internal_Rela *allocated_relocs;
+ unsigned relocs_count;
+ unsigned allocated_relocs_count;
+};
+
+struct elf_xtensa_section_data
+{
+ struct bfd_elf_section_data elf;
+ xtensa_relax_info relax_info;
+};
+
+
+static bfd_boolean
+elf_xtensa_new_section_hook (bfd *abfd, asection *sec)
+{
+ if (!sec->used_by_bfd)
+ {
+ struct elf_xtensa_section_data *sdata;
+ bfd_size_type amt = sizeof (*sdata);
+
+ sdata = bfd_zalloc (abfd, amt);
+ if (sdata == NULL)
+ return FALSE;
+ sec->used_by_bfd = sdata;
+ }
+
+ return _bfd_elf_new_section_hook (abfd, sec);
+}
+
+
+static xtensa_relax_info *
+get_xtensa_relax_info (asection *sec)
+{
+ struct elf_xtensa_section_data *section_data;
+
+ /* No info available if no section or if it is an output section. */
+ if (!sec || sec == sec->output_section)
+ return NULL;
+
+ section_data = (struct elf_xtensa_section_data *) elf_section_data (sec);
+ return §ion_data->relax_info;
+}
+
+
+static void
+init_xtensa_relax_info (asection *sec)
+{
+ xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+
+ relax_info->is_relaxable_literal_section = FALSE;
+ relax_info->is_relaxable_asm_section = FALSE;
+ relax_info->visited = 0;
+
+ relax_info->src_relocs = NULL;
+ relax_info->src_count = 0;
+ relax_info->src_next = 0;
+
+ relax_info->removed_list.head = NULL;
+ relax_info->removed_list.tail = NULL;
+
+ relax_info->action_list.head = NULL;
+
+ relax_info->fix_list = NULL;
+ relax_info->fix_array = NULL;
+ relax_info->fix_array_count = 0;
+
+ relax_info->allocated_relocs = NULL;
+ relax_info->relocs_count = 0;
+ relax_info->allocated_relocs_count = 0;
+}
+
+\f
+/* Coalescing literals may require a relocation to refer to a section in
+ a different input file, but the standard relocation information
+ cannot express that. Instead, the reloc_bfd_fix structures are used
+ to "fix" the relocations that refer to sections in other input files.
+ These structures are kept on per-section lists. The "src_type" field
+ records the relocation type in case there are multiple relocations on
+ the same location. FIXME: This is ugly; an alternative might be to
+ add new symbols with the "owner" field to some other input file. */
+
+struct reloc_bfd_fix_struct
+{
+ asection *src_sec;
+ bfd_vma src_offset;
+ unsigned src_type; /* Relocation type. */
+
+ asection *target_sec;
+ bfd_vma target_offset;
+ bfd_boolean translated;
+
+ reloc_bfd_fix *next;
+};
+
+
+static reloc_bfd_fix *
+reloc_bfd_fix_init (asection *src_sec,
+ bfd_vma src_offset,
+ unsigned src_type,
+ asection *target_sec,
+ bfd_vma target_offset,
+ bfd_boolean translated)
+{
+ reloc_bfd_fix *fix;
+
+ fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix));
+ fix->src_sec = src_sec;
+ fix->src_offset = src_offset;
+ fix->src_type = src_type;
+ fix->target_sec = target_sec;
+ fix->target_offset = target_offset;
+ fix->translated = translated;
+
+ return fix;
+}
+
+
+static void
+add_fix (asection *src_sec, reloc_bfd_fix *fix)
+{
+ xtensa_relax_info *relax_info;
+
+ relax_info = get_xtensa_relax_info (src_sec);
+ fix->next = relax_info->fix_list;
+ relax_info->fix_list = fix;
+}
+
+
+static int
+fix_compare (const void *ap, const void *bp)
+{
+ const reloc_bfd_fix *a = (const reloc_bfd_fix *) ap;
+ const reloc_bfd_fix *b = (const reloc_bfd_fix *) bp;
+
+ if (a->src_offset != b->src_offset)
+ return (a->src_offset - b->src_offset);
+ return (a->src_type - b->src_type);
+}
+
+
+static void
+cache_fix_array (asection *sec)
+{
+ unsigned i, count = 0;
+ reloc_bfd_fix *r;
+ xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+
+ if (relax_info == NULL)
+ return;
+ if (relax_info->fix_list == NULL)
+ return;
+
+ for (r = relax_info->fix_list; r != NULL; r = r->next)
+ count++;
+
+ relax_info->fix_array =
+ (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix) * count);
+ relax_info->fix_array_count = count;
+
+ r = relax_info->fix_list;
+ for (i = 0; i < count; i++, r = r->next)
+ {
+ relax_info->fix_array[count - 1 - i] = *r;
+ relax_info->fix_array[count - 1 - i].next = NULL;
+ }
+
+ qsort (relax_info->fix_array, relax_info->fix_array_count,
+ sizeof (reloc_bfd_fix), fix_compare);
+}
+
+
+static reloc_bfd_fix *
+get_bfd_fix (asection *sec, bfd_vma offset, unsigned type)
+{
+ xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+ reloc_bfd_fix *rv;
+ reloc_bfd_fix key;
+
+ if (relax_info == NULL)
+ return NULL;
+ if (relax_info->fix_list == NULL)
+ return NULL;
+
+ if (relax_info->fix_array == NULL)
+ cache_fix_array (sec);
+
+ key.src_offset = offset;
+ key.src_type = type;
+ rv = bsearch (&key, relax_info->fix_array, relax_info->fix_array_count,
+ sizeof (reloc_bfd_fix), fix_compare);
+ return rv;
+}
+
+\f
+/* Section caching. */
+
+typedef struct section_cache_struct section_cache_t;
+
+struct section_cache_struct
+{
+ asection *sec;
+
+ bfd_byte *contents; /* Cache of the section contents. */
+ bfd_size_type content_length;
+
+ property_table_entry *ptbl; /* Cache of the section property table. */
+ unsigned pte_count;
+
+ Elf_Internal_Rela *relocs; /* Cache of the section relocations. */
+ unsigned reloc_count;
+};
+
+
+static void
+init_section_cache (section_cache_t *sec_cache)
+{
+ memset (sec_cache, 0, sizeof (*sec_cache));
+}
+
+
+static void
+clear_section_cache (section_cache_t *sec_cache)
+{
+ if (sec_cache->sec)
+ {
+ release_contents (sec_cache->sec, sec_cache->contents);
+ release_internal_relocs (sec_cache->sec, sec_cache->relocs);
+ if (sec_cache->ptbl)
+ free (sec_cache->ptbl);
+ memset (sec_cache, 0, sizeof (sec_cache));
+ }
+}
+
+
+static bfd_boolean
+section_cache_section (section_cache_t *sec_cache,
+ asection *sec,
+ struct bfd_link_info *link_info)
+{
+ bfd *abfd;
+ property_table_entry *prop_table = NULL;
+ int ptblsize = 0;
+ bfd_byte *contents = NULL;
+ Elf_Internal_Rela *internal_relocs = NULL;
+ bfd_size_type sec_size;
+
+ if (sec == NULL)
+ return FALSE;
+ if (sec == sec_cache->sec)
+ return TRUE;
+
+ abfd = sec->owner;
+ sec_size = bfd_get_section_limit (abfd, sec);
+
+ /* Get the contents. */
+ contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+ if (contents == NULL && sec_size != 0)
+ goto err;
+
+ /* Get the relocations. */
+ internal_relocs = retrieve_internal_relocs (abfd, sec,
+ link_info->keep_memory);
+
+ /* Get the entry table. */
+ ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table,
+ XTENSA_PROP_SEC_NAME, FALSE);
+ if (ptblsize < 0)
+ goto err;
+
+ /* Fill in the new section cache. */
+ clear_section_cache (sec_cache);
+ memset (sec_cache, 0, sizeof (sec_cache));
+
+ sec_cache->sec = sec;
+ sec_cache->contents = contents;
+ sec_cache->content_length = sec_size;
+ sec_cache->relocs = internal_relocs;
+ sec_cache->reloc_count = sec->reloc_count;
+ sec_cache->pte_count = ptblsize;
+ sec_cache->ptbl = prop_table;
+
+ return TRUE;
+
+ err:
+ release_contents (sec, contents);
+ release_internal_relocs (sec, internal_relocs);
+ if (prop_table)
+ free (prop_table);
+ return FALSE;
+}
+
+\f
+/* Extended basic blocks. */
+
+/* An ebb_struct represents an Extended Basic Block. Within this
+ range, we guarantee that all instructions are decodable, the
+ property table entries are contiguous, and no property table
+ specifies a segment that cannot have instructions moved. This
+ structure contains caches of the contents, property table and
+ relocations for the specified section for easy use. The range is
+ specified by ranges of indices for the byte offset, property table
+ offsets and relocation offsets. These must be consistent. */
+
+typedef struct ebb_struct ebb_t;
+
+struct ebb_struct
+{
+ asection *sec;
+
+ bfd_byte *contents; /* Cache of the section contents. */
+ bfd_size_type content_length;
+
+ property_table_entry *ptbl; /* Cache of the section property table. */
+ unsigned pte_count;
+
+ Elf_Internal_Rela *relocs; /* Cache of the section relocations. */
+ unsigned reloc_count;
+
+ bfd_vma start_offset; /* Offset in section. */
+ unsigned start_ptbl_idx; /* Offset in the property table. */
+ unsigned start_reloc_idx; /* Offset in the relocations. */
+
+ bfd_vma end_offset;
+ unsigned end_ptbl_idx;
+ unsigned end_reloc_idx;
+
+ bfd_boolean ends_section; /* Is this the last ebb in a section? */
+
+ /* The unreachable property table at the end of this set of blocks;
+ NULL if the end is not an unreachable block. */
+ property_table_entry *ends_unreachable;
+};
+
+
+enum ebb_target_enum
+{
+ EBB_NO_ALIGN = 0,
+ EBB_DESIRE_TGT_ALIGN,
+ EBB_REQUIRE_TGT_ALIGN,
+ EBB_REQUIRE_LOOP_ALIGN,
+ EBB_REQUIRE_ALIGN
+};
+
+
+/* proposed_action_struct is similar to the text_action_struct except
+ that is represents a potential transformation, not one that will
+ occur. We build a list of these for an extended basic block
+ and use them to compute the actual actions desired. We must be
+ careful that the entire set of actual actions we perform do not
+ break any relocations that would fit if the actions were not
+ performed. */
+
+typedef struct proposed_action_struct proposed_action;
+
+struct proposed_action_struct
+{
+ enum ebb_target_enum align_type; /* for the target alignment */
+ bfd_vma alignment_pow;
+ text_action_t action;
+ bfd_vma offset;
+ int removed_bytes;
+ bfd_boolean do_action; /* If false, then we will not perform the action. */
+};
+
+
+/* The ebb_constraint_struct keeps a set of proposed actions for an
+ extended basic block. */
+
+typedef struct ebb_constraint_struct ebb_constraint;
+
+struct ebb_constraint_struct
+{
+ ebb_t ebb;
+ bfd_boolean start_movable;
+
+ /* Bytes of extra space at the beginning if movable. */
+ int start_extra_space;
+
+ enum ebb_target_enum start_align;
+
+ bfd_boolean end_movable;
+
+ /* Bytes of extra space at the end if movable. */
+ int end_extra_space;
+
+ unsigned action_count;
+ unsigned action_allocated;
+
+ /* Array of proposed actions. */
+ proposed_action *actions;
+
+ /* Action alignments -- one for each proposed action. */
+ enum ebb_target_enum *action_aligns;
+};
+
+
+static void
+init_ebb_constraint (ebb_constraint *c)
+{
+ memset (c, 0, sizeof (ebb_constraint));
+}
+
+
+static void
+free_ebb_constraint (ebb_constraint *c)
+{
+ if (c->actions)
+ free (c->actions);
+}
+
+
+static void
+init_ebb (ebb_t *ebb,
+ asection *sec,
+ bfd_byte *contents,
+ bfd_size_type content_length,
+ property_table_entry *prop_table,
+ unsigned ptblsize,
+ Elf_Internal_Rela *internal_relocs,
+ unsigned reloc_count)
+{
+ memset (ebb, 0, sizeof (ebb_t));
+ ebb->sec = sec;
+ ebb->contents = contents;
+ ebb->content_length = content_length;
+ ebb->ptbl = prop_table;
+ ebb->pte_count = ptblsize;
+ ebb->relocs = internal_relocs;
+ ebb->reloc_count = reloc_count;
+ ebb->start_offset = 0;
+ ebb->end_offset = ebb->content_length - 1;
+ ebb->start_ptbl_idx = 0;
+ ebb->end_ptbl_idx = ptblsize;
+ ebb->start_reloc_idx = 0;
+ ebb->end_reloc_idx = reloc_count;
+}
+
+
+/* Extend the ebb to all decodable contiguous sections. The algorithm
+ for building a basic block around an instruction is to push it
+ forward until we hit the end of a section, an unreachable block or
+ a block that cannot be transformed. Then we push it backwards
+ searching for similar conditions. */
+
+static bfd_boolean extend_ebb_bounds_forward (ebb_t *);
+static bfd_boolean extend_ebb_bounds_backward (ebb_t *);
+static bfd_size_type insn_block_decodable_len
+ (bfd_byte *, bfd_size_type, bfd_vma, bfd_size_type);
+
+static bfd_boolean
+extend_ebb_bounds (ebb_t *ebb)
+{
+ if (!extend_ebb_bounds_forward (ebb))
+ return FALSE;
+ if (!extend_ebb_bounds_backward (ebb))
+ return FALSE;
+ return TRUE;
+}
+
+
+static bfd_boolean
+extend_ebb_bounds_forward (ebb_t *ebb)
+{
+ property_table_entry *the_entry, *new_entry;
+
+ the_entry = &ebb->ptbl[ebb->end_ptbl_idx];
+
+ /* Stop when (1) we cannot decode an instruction, (2) we are at
+ the end of the property tables, (3) we hit a non-contiguous property
+ table entry, (4) we hit a NO_TRANSFORM region. */
+
+ while (1)
+ {
+ bfd_vma entry_end;
+ bfd_size_type insn_block_len;
+
+ entry_end = the_entry->address - ebb->sec->vma + the_entry->size;
+ insn_block_len =
+ insn_block_decodable_len (ebb->contents, ebb->content_length,
+ ebb->end_offset,
+ entry_end - ebb->end_offset);
+ if (insn_block_len != (entry_end - ebb->end_offset))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
+ ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len);
+ return FALSE;
+ }
+ ebb->end_offset += insn_block_len;
+
+ if (ebb->end_offset == ebb->sec->size)
+ ebb->ends_section = TRUE;
+
+ /* Update the reloc counter. */
+ while (ebb->end_reloc_idx + 1 < ebb->reloc_count
+ && (ebb->relocs[ebb->end_reloc_idx + 1].r_offset
+ < ebb->end_offset))
+ {
+ ebb->end_reloc_idx++;
+ }
+
+ if (ebb->end_ptbl_idx + 1 == ebb->pte_count)
+ return TRUE;
+
+ new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1];
+ if (((new_entry->flags & XTENSA_PROP_INSN) == 0)
+ || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
+ || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0))
+ break;
+
+ if (the_entry->address + the_entry->size != new_entry->address)
+ break;
+
+ the_entry = new_entry;
+ ebb->end_ptbl_idx++;
+ }
+
+ /* Quick check for an unreachable or end of file just at the end. */
+ if (ebb->end_ptbl_idx + 1 == ebb->pte_count)
+ {
+ if (ebb->end_offset == ebb->content_length)
+ ebb->ends_section = TRUE;
+ }
+ else
+ {
+ new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1];
+ if ((new_entry->flags & XTENSA_PROP_UNREACHABLE) != 0
+ && the_entry->address + the_entry->size == new_entry->address)
+ ebb->ends_unreachable = new_entry;
+ }
+
+ /* Any other ending requires exact alignment. */
+ return TRUE;
+}
+
+
+static bfd_boolean
+extend_ebb_bounds_backward (ebb_t *ebb)
+{
+ property_table_entry *the_entry, *new_entry;
+
+ the_entry = &ebb->ptbl[ebb->start_ptbl_idx];
+
+ /* Stop when (1) we cannot decode the instructions in the current entry.
+ (2) we are at the beginning of the property tables, (3) we hit a
+ non-contiguous property table entry, (4) we hit a NO_TRANSFORM region. */
+
+ while (1)
+ {
+ bfd_vma block_begin;
+ bfd_size_type insn_block_len;
+
+ block_begin = the_entry->address - ebb->sec->vma;
+ insn_block_len =
+ insn_block_decodable_len (ebb->contents, ebb->content_length,
+ block_begin,
+ ebb->start_offset - block_begin);
+ if (insn_block_len != ebb->start_offset - block_begin)
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
+ ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len);
+ return FALSE;
+ }
+ ebb->start_offset -= insn_block_len;
+
+ /* Update the reloc counter. */
+ while (ebb->start_reloc_idx > 0
+ && (ebb->relocs[ebb->start_reloc_idx - 1].r_offset
+ >= ebb->start_offset))
+ {
+ ebb->start_reloc_idx--;
+ }
+
+ if (ebb->start_ptbl_idx == 0)
+ return TRUE;
+
+ new_entry = &ebb->ptbl[ebb->start_ptbl_idx - 1];
+ if ((new_entry->flags & XTENSA_PROP_INSN) == 0
+ || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
+ || ((new_entry->flags & XTENSA_PROP_ALIGN) != 0))
+ return TRUE;
+ if (new_entry->address + new_entry->size != the_entry->address)
+ return TRUE;
+
+ the_entry = new_entry;
+ ebb->start_ptbl_idx--;
+ }
+ return TRUE;
+}
+
+
+static bfd_size_type
+insn_block_decodable_len (bfd_byte *contents,
+ bfd_size_type content_len,
+ bfd_vma block_offset,
+ bfd_size_type block_len)
+{
+ bfd_vma offset = block_offset;
+
+ while (offset < block_offset + block_len)
+ {
+ bfd_size_type insn_len = 0;
+
+ insn_len = insn_decode_len (contents, content_len, offset);
+ if (insn_len == 0)
+ return (offset - block_offset);
+ offset += insn_len;
+ }
+ return (offset - block_offset);
+}
+
+
+static void
+ebb_propose_action (ebb_constraint *c,
+ enum ebb_target_enum align_type,
+ bfd_vma alignment_pow,
+ text_action_t action,
+ bfd_vma offset,
+ int removed_bytes,
+ bfd_boolean do_action)
+{
+ proposed_action *act;
+
+ if (c->action_allocated <= c->action_count)
+ {
+ unsigned new_allocated, i;
+ proposed_action *new_actions;
+
+ new_allocated = (c->action_count + 2) * 2;
+ new_actions = (proposed_action *)
+ bfd_zmalloc (sizeof (proposed_action) * new_allocated);
+
+ for (i = 0; i < c->action_count; i++)
+ new_actions[i] = c->actions[i];
+ if (c->actions)
+ free (c->actions);
+ c->actions = new_actions;
+ c->action_allocated = new_allocated;
+ }
+
+ act = &c->actions[c->action_count];
+ act->align_type = align_type;
+ act->alignment_pow = alignment_pow;
+ act->action = action;
+ act->offset = offset;
+ act->removed_bytes = removed_bytes;
+ act->do_action = do_action;
+
+ c->action_count++;
+}
+
+\f
+/* Access to internal relocations, section contents and symbols. */
+
+/* During relaxation, we need to modify relocations, section contents,
+ and symbol definitions, and we need to keep the original values from
+ being reloaded from the input files, i.e., we need to "pin" the
+ modified values in memory. We also want to continue to observe the
+ setting of the "keep-memory" flag. The following functions wrap the
+ standard BFD functions to take care of this for us. */
+
+static Elf_Internal_Rela *
+retrieve_internal_relocs (bfd *abfd, asection *sec, bfd_boolean keep_memory)
+{
+ Elf_Internal_Rela *internal_relocs;
+
+ if ((sec->flags & SEC_LINKER_CREATED) != 0)
+ return NULL;
+
+ internal_relocs = elf_section_data (sec)->relocs;
+ if (internal_relocs == NULL)
+ internal_relocs = (_bfd_elf_link_read_relocs
+ (abfd, sec, NULL, NULL, keep_memory));
+ return internal_relocs;
+}
+
+
+static void
+pin_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
+{
+ elf_section_data (sec)->relocs = internal_relocs;
+}
+
+
+static void
+release_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
+{
+ if (internal_relocs
+ && elf_section_data (sec)->relocs != internal_relocs)
+ free (internal_relocs);
+}
+
+
+static bfd_byte *
+retrieve_contents (bfd *abfd, asection *sec, bfd_boolean keep_memory)
+{
+ bfd_byte *contents;
+ bfd_size_type sec_size;
+
+ sec_size = bfd_get_section_limit (abfd, sec);
+ contents = elf_section_data (sec)->this_hdr.contents;
+
+ if (contents == NULL && sec_size != 0)
+ {
+ if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+ {
+ if (contents)
+ free (contents);
+ return NULL;
+ }
+ if (keep_memory)
+ elf_section_data (sec)->this_hdr.contents = contents;
+ }
+ return contents;
+}
+
+
+static void
+pin_contents (asection *sec, bfd_byte *contents)
+{
+ elf_section_data (sec)->this_hdr.contents = contents;
+}
+
+
+static void
+release_contents (asection *sec, bfd_byte *contents)
+{
+ if (contents && elf_section_data (sec)->this_hdr.contents != contents)
+ free (contents);
+}
+
+
+static Elf_Internal_Sym *
+retrieve_local_syms (bfd *input_bfd)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Sym *isymbuf;
+ size_t locsymcount;
+
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ locsymcount = symtab_hdr->sh_info;
+
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL && locsymcount != 0)
+ isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
+ NULL, NULL, NULL);
+
+ /* Save the symbols for this input file so they won't be read again. */
+ if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents)
+ symtab_hdr->contents = (unsigned char *) isymbuf;
+
+ return isymbuf;
+}
+
+\f
+/* Code for link-time relaxation. */
+
+/* Initialization for relaxation: */
+static bfd_boolean analyze_relocations (struct bfd_link_info *);
+static bfd_boolean find_relaxable_sections
+ (bfd *, asection *, struct bfd_link_info *, bfd_boolean *);
+static bfd_boolean collect_source_relocs
+ (bfd *, asection *, struct bfd_link_info *);
+static bfd_boolean is_resolvable_asm_expansion
+ (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, struct bfd_link_info *,
+ bfd_boolean *);
+static Elf_Internal_Rela *find_associated_l32r_irel
+ (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Rela *);
+static bfd_boolean compute_text_actions
+ (bfd *, asection *, struct bfd_link_info *);
+static bfd_boolean compute_ebb_proposed_actions (ebb_constraint *);
+static bfd_boolean compute_ebb_actions (ebb_constraint *);
+static bfd_boolean check_section_ebb_pcrels_fit
+ (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, const ebb_constraint *,
+ const xtensa_opcode *);
+static bfd_boolean check_section_ebb_reduces (const ebb_constraint *);
+static void text_action_add_proposed
+ (text_action_list *, const ebb_constraint *, asection *);
+static int compute_fill_extra_space (property_table_entry *);
+
+/* First pass: */
+static bfd_boolean compute_removed_literals
+ (bfd *, asection *, struct bfd_link_info *, value_map_hash_table *);
+static Elf_Internal_Rela *get_irel_at_offset
+ (asection *, Elf_Internal_Rela *, bfd_vma);
+static bfd_boolean is_removable_literal
+ (const source_reloc *, int, const source_reloc *, int, asection *,
+ property_table_entry *, int);
+static bfd_boolean remove_dead_literal
+ (bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *,
+ Elf_Internal_Rela *, source_reloc *, property_table_entry *, int);
+static bfd_boolean identify_literal_placement
+ (bfd *, asection *, bfd_byte *, struct bfd_link_info *,
+ value_map_hash_table *, bfd_boolean *, Elf_Internal_Rela *, int,
+ source_reloc *, property_table_entry *, int, section_cache_t *,
+ bfd_boolean);
+static bfd_boolean relocations_reach (source_reloc *, int, const r_reloc *);
+static bfd_boolean coalesce_shared_literal
+ (asection *, source_reloc *, property_table_entry *, int, value_map *);
+static bfd_boolean move_shared_literal
+ (asection *, struct bfd_link_info *, source_reloc *, property_table_entry *,
+ int, const r_reloc *, const literal_value *, section_cache_t *);
+
+/* Second pass: */
+static bfd_boolean relax_section (bfd *, asection *, struct bfd_link_info *);
+static bfd_boolean translate_section_fixes (asection *);
+static bfd_boolean translate_reloc_bfd_fix (reloc_bfd_fix *);
+static asection *translate_reloc (const r_reloc *, r_reloc *, asection *);
+static void shrink_dynamic_reloc_sections
+ (struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *);
+static bfd_boolean move_literal
+ (bfd *, struct bfd_link_info *, asection *, bfd_vma, bfd_byte *,
+ xtensa_relax_info *, Elf_Internal_Rela **, const literal_value *);
+static bfd_boolean relax_property_section
+ (bfd *, asection *, struct bfd_link_info *);
+
+/* Third pass: */
+static bfd_boolean relax_section_symbols (bfd *, asection *);
+
+
+static bfd_boolean
+elf_xtensa_relax_section (bfd *abfd,
+ asection *sec,
+ struct bfd_link_info *link_info,
+ bfd_boolean *again)
+{
+ static value_map_hash_table *values = NULL;
+ static bfd_boolean relocations_analyzed = FALSE;
+ xtensa_relax_info *relax_info;
+
+ if (!relocations_analyzed)
+ {
+ /* Do some overall initialization for relaxation. */
+ values = value_map_hash_table_init ();
+ if (values == NULL)
+ return FALSE;
+ relaxing_section = TRUE;
+ if (!analyze_relocations (link_info))
+ return FALSE;
+ relocations_analyzed = TRUE;
+ }
+ *again = FALSE;
+
+ /* Don't mess with linker-created sections. */
+ if ((sec->flags & SEC_LINKER_CREATED) != 0)
+ return TRUE;
+
+ relax_info = get_xtensa_relax_info (sec);
+ BFD_ASSERT (relax_info != NULL);
+
+ switch (relax_info->visited)
+ {
+ case 0:
+ /* Note: It would be nice to fold this pass into
+ analyze_relocations, but it is important for this step that the
+ sections be examined in link order. */
+ if (!compute_removed_literals (abfd, sec, link_info, values))
+ return FALSE;
+ *again = TRUE;
+ break;
+
+ case 1:
+ if (values)
+ value_map_hash_table_delete (values);
+ values = NULL;
+ if (!relax_section (abfd, sec, link_info))
+ return FALSE;
+ *again = TRUE;
+ break;
+
+ case 2:
+ if (!relax_section_symbols (abfd, sec))
+ return FALSE;
+ break;
+ }
+
+ relax_info->visited++;
+ return TRUE;
+}
+
+\f
+/* Initialization for relaxation. */
+
+/* This function is called once at the start of relaxation. It scans
+ all the input sections and marks the ones that are relaxable (i.e.,
+ literal sections with L32R relocations against them), and then
+ collects source_reloc information for all the relocations against
+ those relaxable sections. During this process, it also detects
+ longcalls, i.e., calls relaxed by the assembler into indirect
+ calls, that can be optimized back into direct calls. Within each
+ extended basic block (ebb) containing an optimized longcall, it
+ computes a set of "text actions" that can be performed to remove
+ the L32R associated with the longcall while optionally preserving
+ branch target alignments. */
+
+static bfd_boolean
+analyze_relocations (struct bfd_link_info *link_info)
+{
+ bfd *abfd;
+ asection *sec;
+ bfd_boolean is_relaxable = FALSE;
+
+ /* Initialize the per-section relaxation info. */
+ for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ init_xtensa_relax_info (sec);
+ }
+
+ /* Mark relaxable sections (and count relocations against each one). */
+ for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable))
+ return FALSE;
+ }
+
+ /* Bail out if there are no relaxable sections. */
+ if (!is_relaxable)
+ return TRUE;
+
+ /* Allocate space for source_relocs. */
+ for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ xtensa_relax_info *relax_info;
+
+ relax_info = get_xtensa_relax_info (sec);
+ if (relax_info->is_relaxable_literal_section
+ || relax_info->is_relaxable_asm_section)
+ {
+ relax_info->src_relocs = (source_reloc *)
+ bfd_malloc (relax_info->src_count * sizeof (source_reloc));
+ }
+ else
+ relax_info->src_count = 0;
+ }
+
+ /* Collect info on relocations against each relaxable section. */
+ for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ if (!collect_source_relocs (abfd, sec, link_info))
+ return FALSE;
+ }
+
+ /* Compute the text actions. */
+ for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ if (!compute_text_actions (abfd, sec, link_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Find all the sections that might be relaxed. The motivation for
+ this pass is that collect_source_relocs() needs to record _all_ the
+ relocations that target each relaxable section. That is expensive
+ and unnecessary unless the target section is actually going to be
+ relaxed. This pass identifies all such sections by checking if
+ they have L32Rs pointing to them. In the process, the total number
+ of relocations targeting each section is also counted so that we
+ know how much space to allocate for source_relocs against each
+ relaxable literal section. */
+
+static bfd_boolean
+find_relaxable_sections (bfd *abfd,
+ asection *sec,
+ struct bfd_link_info *link_info,
+ bfd_boolean *is_relaxable_p)
+{
+ Elf_Internal_Rela *internal_relocs;
+ bfd_byte *contents;
+ bfd_boolean ok = TRUE;
+ unsigned i;
+ xtensa_relax_info *source_relax_info;
+ bfd_boolean is_l32r_reloc;
+
+ internal_relocs = retrieve_internal_relocs (abfd, sec,
+ link_info->keep_memory);
+ if (internal_relocs == NULL)
+ return ok;
+
+ contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+ if (contents == NULL && sec->size != 0)
+ {
+ ok = FALSE;
+ goto error_return;
+ }
+
+ source_relax_info = get_xtensa_relax_info (sec);
+ for (i = 0; i < sec->reloc_count; i++)
+ {
+ Elf_Internal_Rela *irel = &internal_relocs[i];
+ r_reloc r_rel;
+ asection *target_sec;
+ xtensa_relax_info *target_relax_info;
+
+ /* If this section has not already been marked as "relaxable", and
+ if it contains any ASM_EXPAND relocations (marking expanded
+ longcalls) that can be optimized into direct calls, then mark
+ the section as "relaxable". */
+ if (source_relax_info
+ && !source_relax_info->is_relaxable_asm_section
+ && ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_EXPAND)
+ {
+ bfd_boolean is_reachable = FALSE;
+ if (is_resolvable_asm_expansion (abfd, sec, contents, irel,
+ link_info, &is_reachable)
+ && is_reachable)
+ {
+ source_relax_info->is_relaxable_asm_section = TRUE;
+ *is_relaxable_p = TRUE;
+ }
+ }
+
+ r_reloc_init (&r_rel, abfd, irel, contents,
+ bfd_get_section_limit (abfd, sec));
+
+ target_sec = r_reloc_get_section (&r_rel);
+ target_relax_info = get_xtensa_relax_info (target_sec);
+ if (!target_relax_info)
+ continue;
+
+ /* Count PC-relative operand relocations against the target section.
+ Note: The conditions tested here must match the conditions under
+ which init_source_reloc is called in collect_source_relocs(). */
+ is_l32r_reloc = FALSE;
+ if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
+ {
+ xtensa_opcode opcode =
+ get_relocation_opcode (abfd, sec, contents, irel);
+ if (opcode != XTENSA_UNDEFINED)
+ {
+ is_l32r_reloc = (opcode == get_l32r_opcode ());
+ if (!is_alt_relocation (ELF32_R_TYPE (irel->r_info))
+ || is_l32r_reloc)
+ target_relax_info->src_count++;
+ }
+ }
+
+ if (is_l32r_reloc && r_reloc_is_defined (&r_rel))
+ {
+ /* Mark the target section as relaxable. */
+ target_relax_info->is_relaxable_literal_section = TRUE;
+ *is_relaxable_p = TRUE;
+ }
+ }
+
+ error_return:
+ release_contents (sec, contents);
+ release_internal_relocs (sec, internal_relocs);
+ return ok;
+}
+
+
+/* Record _all_ the relocations that point to relaxable sections, and
+ get rid of ASM_EXPAND relocs by either converting them to
+ ASM_SIMPLIFY or by removing them. */
+
+static bfd_boolean
+collect_source_relocs (bfd *abfd,
+ asection *sec,
+ struct bfd_link_info *link_info)
+{
+ Elf_Internal_Rela *internal_relocs;
+ bfd_byte *contents;
+ bfd_boolean ok = TRUE;
+ unsigned i;
+ bfd_size_type sec_size;
+
+ internal_relocs = retrieve_internal_relocs (abfd, sec,
+ link_info->keep_memory);
+ if (internal_relocs == NULL)
+ return ok;
+
+ sec_size = bfd_get_section_limit (abfd, sec);
+ contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+ if (contents == NULL && sec_size != 0)
+ {
+ ok = FALSE;
+ goto error_return;
+ }
+
+ /* Record relocations against relaxable literal sections. */
+ for (i = 0; i < sec->reloc_count; i++)
+ {
+ Elf_Internal_Rela *irel = &internal_relocs[i];
+ r_reloc r_rel;
+ asection *target_sec;
+ xtensa_relax_info *target_relax_info;
+
+ r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
+
+ target_sec = r_reloc_get_section (&r_rel);
+ target_relax_info = get_xtensa_relax_info (target_sec);
+
+ if (target_relax_info
+ && (target_relax_info->is_relaxable_literal_section
+ || target_relax_info->is_relaxable_asm_section))
+ {
+ xtensa_opcode opcode = XTENSA_UNDEFINED;
+ int opnd = -1;
+ bfd_boolean is_abs_literal = FALSE;
+
+ if (is_alt_relocation (ELF32_R_TYPE (irel->r_info)))
+ {
+ /* None of the current alternate relocs are PC-relative,
+ and only PC-relative relocs matter here. However, we
+ still need to record the opcode for literal
+ coalescing. */
+ opcode = get_relocation_opcode (abfd, sec, contents, irel);
+ if (opcode == get_l32r_opcode ())
+ {
+ is_abs_literal = TRUE;
+ opnd = 1;
+ }
+ else
+ opcode = XTENSA_UNDEFINED;
+ }
+ else if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
+ {
+ opcode = get_relocation_opcode (abfd, sec, contents, irel);
+ opnd = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+ }
+
+ if (opcode != XTENSA_UNDEFINED)
+ {
+ int src_next = target_relax_info->src_next++;
+ source_reloc *s_reloc = &target_relax_info->src_relocs[src_next];
+
+ init_source_reloc (s_reloc, sec, &r_rel, opcode, opnd,
+ is_abs_literal);
+ }
+ }
+ }
+
+ /* Now get rid of ASM_EXPAND relocations. At this point, the
+ src_relocs array for the target literal section may still be
+ incomplete, but it must at least contain the entries for the L32R