- FILE *sym_ld_script = NULL;
- struct elf_nds32_link_hash_table *table;
-
- table = nds32_elf_hash_table (info);
- sym_ld_script = table->sym_ld_script;
-
- if (check_start_export_sym)
- fprintf (sym_ld_script, "}\n");
-
- return TRUE;
-}
-
-static enum elf_reloc_type_class
-nds32_elf_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
- const asection *rel_sec ATTRIBUTE_UNUSED,
- const Elf_Internal_Rela *rela)
-{
- switch ((int) ELF32_R_TYPE (rela->r_info))
- {
- case R_NDS32_RELATIVE:
- return reloc_class_relative;
- case R_NDS32_JMP_SLOT:
- return reloc_class_plt;
- case R_NDS32_COPY:
- return reloc_class_copy;
- default:
- return reloc_class_normal;
- }
-}
-
-/* Put target dependent option into info hash table. */
-void
-bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info,
- int relax_fp_as_gp,
- int eliminate_gc_relocs,
- FILE * sym_ld_script, int load_store_relax,
- int target_optimize, int relax_status,
- int relax_round, FILE * ex9_export_file,
- FILE * ex9_import_file,
- int update_ex9_table, int ex9_limit,
- bfd_boolean ex9_loop_aware,
- bfd_boolean ifc_loop_aware)
-{
- struct elf_nds32_link_hash_table *table;
-
- table = nds32_elf_hash_table (link_info);
- if (table == NULL)
- return;
-
- table->relax_fp_as_gp = relax_fp_as_gp;
- table->eliminate_gc_relocs = eliminate_gc_relocs;
- table->sym_ld_script = sym_ld_script;
- table ->load_store_relax = load_store_relax;
- table->target_optimize = target_optimize;
- table->relax_status = relax_status;
- table->relax_round = relax_round;
- table->ex9_export_file = ex9_export_file;
- table->ex9_import_file = ex9_import_file;
- table->update_ex9_table = update_ex9_table;
- table->ex9_limit = ex9_limit;
- table->ex9_loop_aware = ex9_loop_aware;
- table->ifc_loop_aware = ifc_loop_aware;
-}
-\f
-/* These functions and data-structures are used for fp-as-gp
- optimization. */
-
-#define FAG_THRESHOLD 3 /* At least 3 gp-access. */
-#define FAG_BUMPER 8 /* Leave some space to avoid aligment issues. */
-#define FAG_WINDOW (512 - FAG_BUMPER) /* lwi37.fp covers 512 bytes. */
-
-/* An nds32_fag represent a gp-relative access.
- We find best fp-base by using a sliding window
- to find a base address which can cover most gp-access. */
-struct nds32_fag
-{
- struct nds32_fag *next; /* NULL-teminated linked list. */
- bfd_vma addr; /* The address of this fag. */
- Elf_Internal_Rela **relas; /* The relocations associated with this fag.
- It is used for applying FP7U2_FLAG. */
- int count; /* How many times this address is referred.
- There should be exactly `count' relocations
- in relas. */
- int relas_capcity; /* The buffer size of relas.
- We use an array instead of linked-list,
- and realloc is used to adjust buffer size. */
-};
-
-static void
-nds32_fag_init (struct nds32_fag *head)
-{
- memset (head, 0, sizeof (struct nds32_fag));
-}
-
-static void
-nds32_fag_verify (struct nds32_fag *head)
-{
- struct nds32_fag *iter;
- struct nds32_fag *prev;
-
- prev = NULL;
- iter = head->next;
- while (iter)
- {
- if (prev && prev->addr >= iter->addr)
- puts ("Bug in fp-as-gp insertion.");
- prev = iter;
- iter = iter->next;
- }
-}
-
-/* Insert a fag in ascending order.
- If a fag of the same address already exists,
- they are chained by relas array. */
-
-static void
-nds32_fag_insert (struct nds32_fag *head, bfd_vma addr,
- Elf_Internal_Rela * rel)
-{
- struct nds32_fag *iter;
- struct nds32_fag *new_fag;
- const int INIT_RELAS_CAP = 4;
-
- for (iter = head;
- iter->next && iter->next->addr <= addr;
- iter = iter->next)
- /* Find somewhere to insert. */ ;
-
- /* `iter' will be equal to `head' if the list is empty. */
- if (iter != head && iter->addr == addr)
- {
- /* The address exists in the list.
- Insert `rel' into relocation list, relas. */
-
- /* Check whether relas is big enough. */
- if (iter->count >= iter->relas_capcity)
- {
- iter->relas_capcity *= 2;
- iter->relas = bfd_realloc
- (iter->relas, iter->relas_capcity * sizeof (void *));
- }
- iter->relas[iter->count++] = rel;
- return;
- }
-
- /* This is a new address. Create a fag node for it. */
- new_fag = bfd_malloc (sizeof (struct nds32_fag));
- memset (new_fag, 0, sizeof (*new_fag));
- new_fag->addr = addr;
- new_fag->count = 1;
- new_fag->next = iter->next;
- new_fag->relas_capcity = INIT_RELAS_CAP;
- new_fag->relas = (Elf_Internal_Rela **)
- bfd_malloc (new_fag->relas_capcity * sizeof (void *));
- new_fag->relas[0] = rel;
- iter->next = new_fag;
-
- nds32_fag_verify (head);
-}
-
-static void
-nds32_fag_free_list (struct nds32_fag *head)
-{
- struct nds32_fag *iter;
-
- iter = head->next;
- while (iter)
- {
- struct nds32_fag *tmp = iter;
- iter = iter->next;
- free (tmp->relas);
- tmp->relas = NULL;
- free (tmp);
- }
-}
-
-static bfd_boolean
-nds32_fag_isempty (struct nds32_fag *head)
-{
- return head->next == NULL;
-}
-
-/* Find the best fp-base address.
- The relocation associated with that address is returned,
- so we can track the symbol instead of a fixed address.
-
- When relaxation, the address of an datum may change,
- because a text section is shrinked, so the data section
- moves forward. If the aligments of text and data section
- are different, their distance may change too.
- Therefore, tracking a fixed address is not appriate. */
-
-static int
-nds32_fag_find_base (struct nds32_fag *head, struct nds32_fag **bestpp)
-{
- struct nds32_fag *base; /* First fag in the window. */
- struct nds32_fag *last; /* First fag outside the window. */
- int accu = 0; /* Usage accumulation. */
- struct nds32_fag *best; /* Best fag. */
- int baccu = 0; /* Best accumulation. */
-
- /* Use first fag for initial, and find the last fag in the window.
-
- In each iteration, we could simply subtract previous fag
- and accumulate following fags which are inside the window,
- untill we each the end. */
-
- if (nds32_fag_isempty (head))
- return 0;
-
- /* Initialize base. */
- base = head->next;
- best = base;
- for (last = base;
- last && last->addr < base->addr + FAG_WINDOW;
- last = last->next)
- accu += last->count;
-
- baccu = accu;
-
- /* Record the best base in each iteration. */
- while (base->next)
- {
- accu -= base->count;
- base = base->next;
- /* Account fags in window. */
- for (/* Nothing. */;
- last && last->addr < base->addr + FAG_WINDOW;
- last = last->next)
- accu += last->count;
-
- /* A better fp-base? */
- if (accu > baccu)
- {
- best = base;
- baccu = accu;
- }
- }
-
- if (bestpp)
- *bestpp = best;
- return baccu;
-}
-
-/* Apply R_NDS32_INSN16_FP7U2_FLAG on gp-relative accesses,
- so we can convert it fo fp-relative access later.
- `best_fag' is the best fp-base. Only those inside the window
- of best_fag is applied the flag. */
-
-static bfd_boolean
-nds32_fag_mark_relax (struct bfd_link_info *link_info,
- bfd *abfd, struct nds32_fag *best_fag,
- Elf_Internal_Rela *internal_relocs,
- Elf_Internal_Rela *irelend)
-{
- struct nds32_fag *ifag;
- bfd_vma best_fpbase, gp;
- bfd *output_bfd;
-
- output_bfd = abfd->sections->output_section->owner;
- nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE);
- best_fpbase = best_fag->addr;
-
- if (best_fpbase > gp + sdata_range[4][1]
- || best_fpbase < gp - sdata_range[4][0])
- return FALSE;
-
- /* Mark these inside the window R_NDS32_INSN16_FP7U2_FLAG flag,
- so we know they can be converted to lwi37.fp. */
- for (ifag = best_fag;
- ifag && ifag->addr < best_fpbase + FAG_WINDOW; ifag = ifag->next)
- {
- int i;
-
- for (i = 0; i < ifag->count; i++)
- {
- Elf_Internal_Rela *insn16_rel;
- Elf_Internal_Rela *fag_rel;
-
- fag_rel = ifag->relas[i];
-
- /* Only if this is within the WINDOWS, FP7U2_FLAG
- is applied. */
-
- insn16_rel = find_relocs_at_address
- (fag_rel, internal_relocs, irelend, R_NDS32_INSN16);
-
- if (insn16_rel != irelend)
- insn16_rel->r_addend = R_NDS32_INSN16_FP7U2_FLAG;
- }
- }
- return TRUE;
-}
-
-/* This is the main function of fp-as-gp optimization.
- It should be called by relax_section. */
-
-static bfd_boolean
-nds32_relax_fp_as_gp (struct bfd_link_info *link_info,
- bfd *abfd, asection *sec,
- Elf_Internal_Rela *internal_relocs,
- Elf_Internal_Rela *irelend,
- Elf_Internal_Sym *isymbuf)
-{
- Elf_Internal_Rela *begin_rel = NULL;
- Elf_Internal_Rela *irel;
- struct nds32_fag fag_head;
- Elf_Internal_Shdr *symtab_hdr;
- bfd_byte *contents;
-
- /* FIXME: Can we bfd_elf_link_read_relocs for the relocs? */
-
- /* Per-function fp-base selection.
- 1. Create a list for all the gp-relative access.
- 2. Base on those gp-relative address,
- find a fp-base which can cover most access.
- 3. Use the fp-base for fp-as-gp relaxation.
-
- NOTE: If fp-as-gp is not worth to do, (e.g., less than 3 times),
- we should
- 1. delete the `la $fp, _FP_BASE_' instruction and
- 2. not convert lwi.gp to lwi37.fp.
-
- To delete the _FP_BASE_ instruction, we simply apply
- R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG flag in the r_addend to disable it.
-
- To suppress the conversion, we simply NOT to apply
- R_NDS32_INSN16_FP7U2_FLAG flag. */
-
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-
- if (!nds32_get_section_contents (abfd, sec, &contents)
- || !nds32_get_local_syms (abfd, sec, &isymbuf))
- return FALSE;
-
- /* Check whether it is worth for fp-as-gp optimization,
- i.e., at least 3 gp-load.
-
- Set R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG if we should NOT
- apply this optimization. */
-
- for (irel = internal_relocs; irel < irelend; irel++)
- {
- /* We recognize R_NDS32_RELAX_REGION_BEGIN/_END for the region.
- One we enter the begin of the region, we track all the LW/ST
- instructions, so when we leave the region, we try to find
- the best fp-base address for those LW/ST instructions. */
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN
- && (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
- {
- /* Begin of the region. */
- if (begin_rel)
- (*_bfd_error_handler) (_("%B: Nested OMIT_FP in %A."), abfd, sec);
-
- begin_rel = irel;
- nds32_fag_init (&fag_head);
- }
- else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END
- && (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG))
- {
- int accu;
- struct nds32_fag *best_fag;
- int dist;
-
- /* End of the region.
- Check whether it is worth to do fp-as-gp. */
-
- if (begin_rel == NULL)
- {
- (*_bfd_error_handler) (_("%B: Unmatched OMIT_FP in %A."), abfd, sec);
- continue;
- }
-
- accu = nds32_fag_find_base (&fag_head, &best_fag);
-
- /* Check if it is worth, and FP_BASE is near enough to SDA_BASE. */
- if (accu < FAG_THRESHOLD
- || !nds32_fag_mark_relax (link_info, abfd, best_fag,
- internal_relocs, irelend))
- {
- /* Not worth to do fp-as-gp. */
- begin_rel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG;
- begin_rel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
- irel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG;
- irel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
- nds32_fag_free_list (&fag_head);
- begin_rel = NULL;
- continue;
- }
-
- /* R_SYM of R_NDS32_RELAX_REGION_BEGIN is not used by assembler,
- so we use it to record the distance to the reloction of best
- fp-base. */
- dist = best_fag->relas[0] - begin_rel;
- BFD_ASSERT (dist > 0 && dist < 0xffffff);
- /* Use high 16 bits of addend to record the _FP_BASE_ matched
- relocation. And get the base value when relocating. */
- begin_rel->r_addend |= dist << 16;
-
- nds32_fag_free_list (&fag_head);
- begin_rel = NULL;
- }
-
- if (begin_rel == NULL)
- /* Skip if we are not in the region of fp-as-gp. */
- continue;
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S2_RELA
- || ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA17S2_RELA)
- {
- bfd_vma addr;
- uint32_t insn;
-
- /* A gp-relative access is found. Insert it to the fag-list. */
-
- /* Rt is necessary an RT3, so it can be converted to lwi37.fp. */
- insn = bfd_getb32 (contents + irel->r_offset);
- if (!N32_IS_RT3 (insn))
- continue;
-
- addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
- nds32_fag_insert (&fag_head, addr, irel);
- }
- else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA_FP7U2_RELA)
- {
- begin_rel = NULL;
- }
- }
-
- return TRUE;
-}
-
-/* Remove unused `la $fp, _FD_BASE_' instruction. */
-
-static bfd_boolean
-nds32_fag_remove_unused_fpbase (bfd *abfd, asection *sec,
- Elf_Internal_Rela *internal_relocs,
- Elf_Internal_Rela *irelend)
-{
- Elf_Internal_Rela *irel;
- Elf_Internal_Shdr *symtab_hdr;
- bfd_byte *contents = NULL;
- nds32_elf_blank_t *relax_blank_list = NULL;
- bfd_boolean result = TRUE;
- bfd_boolean unused_region = FALSE;
-
- /*
- NOTE: Disable fp-as-gp if we encounter ifcall relocations.
- * R_NDS32_17IFC_PCREL_RELA
- * R_NDS32_10IFCU_PCREL_RELA
-
- CASE??????????????
- */
-
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
- nds32_get_section_contents (abfd, sec, &contents);
-
- for (irel = internal_relocs; irel < irelend; irel++)
- {
- /* To remove unused fp-base, we simply find the REGION_NOT_OMIT_FP
- we marked to in previous pass.
- DO NOT scan relocations again, since we've alreadly decided it
- and set the flag. */
- const char *syname;
- int syndx;
- uint32_t insn;
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN
- && (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG))
- unused_region = TRUE;
- else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END
- && (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG))
- unused_region = FALSE;
-
- /* We're not in the region. */
- if (!unused_region)
- continue;
-
- /* _FP_BASE_ must be a GLOBAL symbol. */
- syndx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
- if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
- continue;
-
- /* The symbol name must be _FP_BASE_. */
- syname = elf_sym_hashes (abfd)[syndx]->root.root.string;
- if (strcmp (syname, FP_BASE_NAME) != 0)
- continue;
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA19S0_RELA)
- {
- /* addi.gp $fp, -256 */
- insn = bfd_getb32 (contents + irel->r_offset);
- if (insn != INSN_ADDIGP_TO_FP)
- continue;
- }
- else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S0_RELA)
- {
- /* addi $fp, $gp, -256 */
- insn = bfd_getb32 (contents + irel->r_offset);
- if (insn != INSN_ADDI_GP_TO_FP)
- continue;
- }
- else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_20_RELA)
- {
- /* movi $fp, FP_BASE */
- insn = bfd_getb32 (contents + irel->r_offset);
- if (insn != INSN_MOVI_TO_FP)
- continue;
- }
- else
- continue;
-
- /* We got here because a FP_BASE instruction is found. */
- if (!insert_nds32_elf_blank_recalc_total
- (&relax_blank_list, irel->r_offset, 4))
- goto error_return;
- }
-
-finish:
- if (relax_blank_list)
- {
- nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list);
- relax_blank_list = NULL;
- }
- return result;
-
-error_return:
- result = FALSE;
- goto finish;
-}
-\f
-/* Link-time IFC relaxation.
- In this optimization, we chains jump instructions
- of the same destination with ifcall. */
-
-
-/* List to save jal and j relocation. */
-struct elf_nds32_ifc_symbol_entry
-{
- asection *sec;
- struct elf_link_hash_entry *h;
- struct elf_nds32_ifc_irel_list *irel_head;
- unsigned long insn;
- int times;
- int enable; /* Apply ifc. */
- int ex9_enable; /* Apply ifc after ex9. */
- struct elf_nds32_ifc_symbol_entry *next;
-};
-
-struct elf_nds32_ifc_irel_list
-{
- Elf_Internal_Rela *irel;
- asection *sec;
- bfd_vma addr;
- /* If this is set, then it is the last instruction for
- ifc-chain, so it must be keep for the actual branching. */
- int keep;
- struct elf_nds32_ifc_irel_list *next;
-};
-
-static struct elf_nds32_ifc_symbol_entry *ifc_symbol_head = NULL;
-
-/* Insert symbol of jal and j for ifc. */
-
-static void
-nds32_elf_ifc_insert_symbol (asection *sec,
- struct elf_link_hash_entry *h,
- Elf_Internal_Rela *irel,
- unsigned long insn)
-{
- struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head;
-
- /* Check there is target of existing entry the same as the new one. */
- while (ptr != NULL)
- {
- if (((h == NULL && ptr->sec == sec
- && ELF32_R_SYM (ptr->irel_head->irel->r_info) == ELF32_R_SYM (irel->r_info)
- && ptr->irel_head->irel->r_addend == irel->r_addend)
- || h != NULL)
- && ptr->h == h
- && ptr->insn == insn)
- {
- /* The same target exist, so insert into list. */
- struct elf_nds32_ifc_irel_list *irel_list = ptr->irel_head;
-
- while (irel_list->next != NULL)
- irel_list = irel_list->next;
- irel_list->next = bfd_malloc (sizeof (struct elf_nds32_ifc_irel_list));
- irel_list = irel_list->next;
- irel_list->irel = irel;
- irel_list->keep = 1;
-
- if (h == NULL)
- irel_list->sec = NULL;
- else
- irel_list->sec = sec;
- irel_list->next = NULL;
- return;
- }
- if (ptr->next == NULL)
- break;
- ptr = ptr->next;
- }
-
- /* There is no same target entry, so build a new one. */
- if (ifc_symbol_head == NULL)
- {
- ifc_symbol_head = bfd_malloc (sizeof (struct elf_nds32_ifc_symbol_entry));
- ptr = ifc_symbol_head;
- }
- else
- {
- ptr->next = bfd_malloc (sizeof (struct elf_nds32_ifc_symbol_entry));
- ptr = ptr->next;
- }
-
- ptr->h = h;
- ptr->irel_head = bfd_malloc (sizeof (struct elf_nds32_ifc_irel_list));
- ptr->irel_head->irel = irel;
- ptr->insn = insn;
- ptr->irel_head->keep = 1;
-
- if (h == NULL)
- {
- /* Local symbols. */
- ptr->sec = sec;
- ptr->irel_head->sec = NULL;
- }
- else
- {
- /* Global symbol. */
- ptr->sec = NULL;
- ptr->irel_head->sec = sec;
- }
-
- ptr->irel_head->next = NULL;
- ptr->times = 0;
- ptr->enable = 0;
- ptr->ex9_enable = 0;
- ptr->next = NULL;
-}
-
-/* Gather all jal and j instructions. */
-
-static bfd_boolean
-nds32_elf_ifc_calc (struct bfd_link_info *info,
- bfd *abfd, asection *sec)
-{
- Elf_Internal_Rela *internal_relocs;
- Elf_Internal_Rela *irelend;
- Elf_Internal_Rela *irel;
- Elf_Internal_Shdr *symtab_hdr;
- bfd_byte *contents = NULL;
- unsigned long insn, insn_with_reg;
- unsigned long r_symndx;
- struct elf_link_hash_entry *h;
- struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
- struct elf_nds32_link_hash_table *table;
- bfd_boolean ifc_loop_aware;
-
- internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
- TRUE /* keep_memory */);
- irelend = internal_relocs + sec->reloc_count;
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-
- /* Check if the object enable ifc. */
- irel = find_relocs_at_address (internal_relocs, internal_relocs, irelend,
- R_NDS32_RELAX_ENTRY);
-
- if (irel == NULL
- || irel >= irelend
- || ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY
- || (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY
- && !(irel->r_addend & R_NDS32_RELAX_ENTRY_IFC_FLAG)))
- return TRUE;
-
- if (!nds32_get_section_contents (abfd, sec, &contents))
- return FALSE;
-
- table = nds32_elf_hash_table (info);
- ifc_loop_aware = table->ifc_loop_aware;
- while (irel != NULL && irel < irelend)
- {
- /* Traverse all relocation and gather all of them to build the list. */
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN)
- {
- if (ifc_loop_aware == 1
- && (irel->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG) != 0)
- {
- /* Check the region if loop or not. If it is true and
- ifc-loop-aware is true, ignore the region till region end. */
- while (irel != NULL
- && irel < irelend
- && (ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_END
- || (irel->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG) != 0))
- irel++;
- }
- }
-
- if (ELF32_R_TYPE (irel->r_info) == R_NDS32_25_PCREL_RELA)
- {
- insn = bfd_getb32 (contents + irel->r_offset);
- nds32_elf_get_insn_with_reg (irel, insn, &insn_with_reg);
- r_symndx = ELF32_R_SYM (irel->r_info);
- if (r_symndx < symtab_hdr->sh_info)
- {
- /* Local symbol. */
- nds32_elf_ifc_insert_symbol (sec, NULL, irel, insn_with_reg);
- }
- else
- {
- /* External symbol. */
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- nds32_elf_ifc_insert_symbol (sec, h, irel, insn_with_reg);
- }
- }
- irel++;
- }
- return TRUE;
-}
-
-/* Determine whether j and jal should be substituted. */
-
-static void
-nds32_elf_ifc_filter (struct bfd_link_info *info)
-{
- struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head;
- struct elf_nds32_ifc_irel_list *irel_ptr = NULL;
- struct elf_nds32_ifc_irel_list *irel_keeper = NULL;
- struct elf_nds32_link_hash_table *table;
- int target_optimize;
- bfd_vma address;
-
- table = nds32_elf_hash_table (info);
- target_optimize = table->target_optimize;
- while (ptr)
- {
- irel_ptr = ptr->irel_head;
- if (ptr->h == NULL)
- {
- /* Local symbol. */
- irel_keeper = irel_ptr;
- while (irel_ptr && irel_ptr->next)
- {
- /* Check there is jump target can be used. */
- if ((irel_ptr->next->irel->r_offset
- - irel_keeper->irel->r_offset) > 1022)
- irel_keeper = irel_ptr->next;
- else
- {
- ptr->enable = 1;
- irel_ptr->keep = 0;
- }
- irel_ptr = irel_ptr->next;
- }
- }
- else
- {
- /* Global symbol. We have to get the absolute address
- and decide whether to keep it or not.*/
-
- while (irel_ptr)
- {
- address = (irel_ptr->irel->r_offset
- + irel_ptr->sec->output_section->vma
- + irel_ptr->sec->output_offset);
- irel_ptr->addr = address;
- irel_ptr = irel_ptr->next;
- }
-
- irel_ptr = ptr->irel_head;
- while (irel_ptr)
- {
- struct elf_nds32_ifc_irel_list *irel_dest = irel_ptr;
- struct elf_nds32_ifc_irel_list *irel_temp = irel_ptr;
- struct elf_nds32_ifc_irel_list *irel_ptr_prev = NULL;
- struct elf_nds32_ifc_irel_list *irel_dest_prev = NULL;
-
- while (irel_temp->next)
- {
- if (irel_temp->next->addr < irel_dest->addr)
- {
- irel_dest_prev = irel_temp;
- irel_dest = irel_temp->next;
- }
- irel_temp = irel_temp->next;
- }
- if (irel_dest != irel_ptr)
- {
- if (irel_ptr_prev)
- irel_ptr_prev->next = irel_dest;
- if (irel_dest_prev)
- irel_dest_prev->next = irel_ptr;
- irel_temp = irel_ptr->next;
- irel_ptr->next = irel_dest->next;
- irel_dest->next = irel_temp;
- }
- irel_ptr_prev = irel_ptr;
- irel_ptr = irel_ptr->next;
- }
-
- irel_ptr = ptr->irel_head;
- irel_keeper = irel_ptr;
- while (irel_ptr && irel_ptr->next)
- {
- if ((irel_ptr->next->addr - irel_keeper->addr) > 1022)
- irel_keeper = irel_ptr->next;
- else
- {
- ptr->enable = 1;
- irel_ptr->keep = 0;
- }
- irel_ptr = irel_ptr->next;
- }
- }
-
- /* Ex9 enable. Reserve it for ex9. */
- if ((target_optimize & NDS32_RELAX_EX9_ON)
- && ptr->irel_head != irel_keeper)
- ptr->enable = 0;
- ptr = ptr->next;
- }
-}
-
-/* Determine whether j and jal should be substituted after ex9 done. */
-
-static void
-nds32_elf_ifc_filter_after_ex9 (void)
-{
- struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head;
- struct elf_nds32_ifc_irel_list *irel_ptr = NULL;
-
- while (ptr)
- {
- if (ptr->enable == 0)
- {
- /* Check whether ifc is applied or not. */
- irel_ptr = ptr->irel_head;
- ptr->ex9_enable = 1;
- while (irel_ptr)
- {
- if (ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN)
- {
- /* Ex9 already. */
- ptr->ex9_enable = 0;
- break;
- }
- irel_ptr = irel_ptr->next;
- }
- }
- ptr = ptr->next;
- }
-}
-
-/* Wrapper to do ifc relaxation. */
-
-bfd_boolean
-nds32_elf_ifc_finish (struct bfd_link_info *info)
-{
- int relax_status;
- struct elf_nds32_link_hash_table *table;
-
- table = nds32_elf_hash_table (info);
- relax_status = table->relax_status;
-
- if (!(relax_status & NDS32_RELAX_JUMP_IFC_DONE))
- nds32_elf_ifc_filter (info);
- else
- nds32_elf_ifc_filter_after_ex9 ();
-
- if (!nds32_elf_ifc_replace (info))
- return FALSE;
-
- if (table)
- table->relax_status |= NDS32_RELAX_JUMP_IFC_DONE;
- return TRUE;
-}
-
-/* Traverse the result of ifc filter and replace it with ifcall9. */
-
-static bfd_boolean
-nds32_elf_ifc_replace (struct bfd_link_info *info)
-{
- struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head;
- struct elf_nds32_ifc_irel_list *irel_ptr = NULL;
- nds32_elf_blank_t *relax_blank_list = NULL;
- bfd_byte *contents = NULL;
- Elf_Internal_Rela *internal_relocs;
- Elf_Internal_Rela *irel;
- Elf_Internal_Rela *irelend;
- unsigned short insn16 = INSN_IFCALL9;
- struct elf_nds32_link_hash_table *table;
- int relax_status;
-
- table = nds32_elf_hash_table (info);
- relax_status = table->relax_status;
-
- while (ptr)
- {
- /* Traverse the ifc gather list, and replace the
- filter entries by ifcall9. */
- if ((!(relax_status & NDS32_RELAX_JUMP_IFC_DONE) && ptr->enable == 1)
- || ((relax_status & NDS32_RELAX_JUMP_IFC_DONE) && ptr->ex9_enable == 1))
- {
- irel_ptr = ptr->irel_head;
- if (ptr->h == NULL)
- {
- /* Local symbol. */
- internal_relocs = _bfd_elf_link_read_relocs
- (ptr->sec->owner, ptr->sec, NULL, NULL, TRUE /* keep_memory */);
- irelend = internal_relocs + ptr->sec->reloc_count;
-
- if (!nds32_get_section_contents (ptr->sec->owner, ptr->sec, &contents))
- return FALSE;
-
- while (irel_ptr)
- {
- if (irel_ptr->keep == 0 && irel_ptr->next)
- {
- /* The one can be replaced. We have to check whether
- there is any alignment point in the region. */
- irel = irel_ptr->irel;
- while (((irel_ptr->next->keep == 0 && irel < irel_ptr->next->irel)
- || (irel_ptr->next->keep == 1 && irel < irelend))
- && !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
- && (irel->r_addend & 0x1f) == 2))
- irel++;
- if (irel >= irelend
- || !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
- && (irel->r_addend & 0x1f) == 2
- && ((irel->r_offset
- - get_nds32_elf_blank_total
- (&relax_blank_list, irel->r_offset, 1)) & 0x02) == 0))
- {
- /* Replace by ifcall9. */
- bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset);
- if (!insert_nds32_elf_blank_recalc_total
- (&relax_blank_list, irel_ptr->irel->r_offset + 2, 2))
- return FALSE;
- irel_ptr->irel->r_info =
- ELF32_R_INFO (ELF32_R_SYM (irel_ptr->irel->r_info), R_NDS32_TRAN);
- }
- }
- irel_ptr = irel_ptr->next;
- }
-
- /* Delete the redundant code. */
- if (relax_blank_list)
- {
- nds32_elf_relax_delete_blanks (ptr->sec->owner, ptr->sec,
- relax_blank_list);
- relax_blank_list = NULL;
- }
- }
- else
- {
- /* Global symbol. */
- while (irel_ptr)
- {
- if (irel_ptr->keep == 0 && irel_ptr->next)
- {
- /* The one can be replaced, and we have to check
- whether there is any alignment point in the region. */
- internal_relocs = _bfd_elf_link_read_relocs
- (irel_ptr->sec->owner, irel_ptr->sec, NULL, NULL,
- TRUE /* keep_memory */);
- irelend = internal_relocs + irel_ptr->sec->reloc_count;
- if (!nds32_get_section_contents
- (irel_ptr->sec->owner, irel_ptr->sec, &contents))
- return FALSE;
-
- irel = irel_ptr->irel;
- while (((irel_ptr->sec == irel_ptr->next->sec
- && irel_ptr->next->keep == 0
- && irel < irel_ptr->next->irel)
- || ((irel_ptr->sec != irel_ptr->next->sec
- || irel_ptr->next->keep == 1)
- && irel < irelend))
- && !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
- && (irel->r_addend & 0x1f) == 2))
- irel++;
- if (irel >= irelend
- || !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
- && (irel->r_addend & 0x1f) == 2
- && ((irel->r_offset
- - get_nds32_elf_blank_total (&relax_blank_list,
- irel->r_offset, 1)) & 0x02) == 0))
- {
- /* Replace by ifcall9. */
- bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset);
- if (!insert_nds32_elf_blank_recalc_total
- (&relax_blank_list, irel_ptr->irel->r_offset + 2, 2))
- return FALSE;
-
- /* Delete the redundant code, and clear the relocation. */
- nds32_elf_relax_delete_blanks (irel_ptr->sec->owner,
- irel_ptr->sec,
- relax_blank_list);
- irel_ptr->irel->r_info =
- ELF32_R_INFO (ELF32_R_SYM (irel_ptr->irel->r_info), R_NDS32_TRAN);
- relax_blank_list = NULL;
- }
- }
-
- irel_ptr = irel_ptr->next;
- }
- }
- }
- ptr = ptr->next;
- }
-
- return TRUE;
-}
-
-/* Relocate ifcall. */
-
-bfd_boolean
-nds32_elf_ifc_reloc (void)
-{
- struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head;
- struct elf_nds32_ifc_irel_list *irel_ptr = NULL;
- struct elf_nds32_ifc_irel_list *irel_keeper = NULL;
- bfd_vma relocation, address;
- unsigned short insn16;
-
- bfd_byte *contents = NULL;
-
- while (ptr)
- {
- if (ptr->enable == 1 || ptr->ex9_enable == 1)
- {
- /* Check the entry is enable ifcall. */
- irel_ptr = ptr->irel_head;
- while (irel_ptr)
- {
- if (irel_ptr->keep == 1)
- {
- irel_keeper = irel_ptr;
- break;
- }
- irel_ptr = irel_ptr->next;
- }
-
- irel_ptr = ptr->irel_head;
- if (ptr->h == NULL)
- {
- /* Local symbol. */
- if (!nds32_get_section_contents (ptr->sec->owner, ptr->sec, &contents))
- return FALSE;
-
- while (irel_ptr)
- {
- if (irel_ptr->keep == 0
- && ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN)
- {
- relocation = irel_keeper->irel->r_offset;
- relocation = relocation - irel_ptr->irel->r_offset;
- while (irel_keeper && relocation > 1022)
- {
- irel_keeper = irel_keeper->next;
- if (irel_keeper && irel_keeper->keep == 1)
- {
- relocation = irel_keeper->irel->r_offset;
- relocation = relocation - irel_ptr->irel->r_offset;
- }
- }
- if (relocation > 1022)
- {
- /* Double check. */
- irel_keeper = ptr->irel_head;
- while (irel_keeper)
- {
- if (irel_keeper->keep == 1)
- {
- relocation = irel_keeper->irel->r_offset;
- relocation = relocation - irel_ptr->irel->r_offset;
- }
- if (relocation <= 1022)
- break;
- irel_keeper = irel_keeper->next;
- }
- if (!irel_keeper)
- return FALSE;
- }
-
- insn16 = INSN_IFCALL9 | (relocation >> 1);
- bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset);
- }
- irel_ptr = irel_ptr->next;
- }
- }
- else
- {
- /* Global symbol. */
- while (irel_ptr)
- {
- if (irel_ptr->keep == 0
- && ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN)
- {
- relocation = (irel_keeper->irel->r_offset
- + irel_keeper->sec->output_section->vma
- + irel_keeper->sec->output_offset);
- address = (irel_ptr->irel->r_offset
- + irel_ptr->sec->output_section->vma
- + irel_ptr->sec->output_offset);
- relocation = relocation - address;
- while (irel_keeper && relocation > 1022)
- {
- irel_keeper = irel_keeper->next;
- if (irel_keeper && irel_keeper->keep ==1)
- {
- relocation = (irel_keeper->irel->r_offset
- + irel_keeper->sec->output_section->vma
- + irel_keeper->sec->output_offset);
- relocation = relocation - address;
- }
- }
-
- if (relocation > 1022)
- {
- /* Double check. */
- irel_keeper = ptr->irel_head;
- while (irel_keeper)
- {
- if (irel_keeper->keep == 1)
- {
-
- relocation = (irel_keeper->irel->r_offset
- + irel_keeper->sec->output_section->vma
- + irel_keeper->sec->output_offset);
- relocation = relocation - address;
- }
- if (relocation <= 1022)
- break;
- irel_keeper = irel_keeper->next;
- }
- if (!irel_keeper)
- return FALSE;
- }
- if (!nds32_get_section_contents
- (irel_ptr->sec->owner, irel_ptr->sec, &contents))
- return FALSE;
- insn16 = INSN_IFCALL9 | (relocation >> 1);
- bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset);
- }
- irel_ptr =irel_ptr->next;
- }
- }
- }
- ptr = ptr->next;
- }