bfd/
authorAlan Modra <amodra@gmail.com>
Mon, 12 Jan 2009 00:23:58 +0000 (00:23 +0000)
committerAlan Modra <amodra@gmail.com>
Mon, 12 Jan 2009 00:23:58 +0000 (00:23 +0000)
* elf32-spu.c (struct spu_link_hash_table): Add init, line_size_log2,
num_lines_log2.
(struct got_entry): Add br_addr.
(struct call_info): Add priority.
(struct function_info): Add lr_store and sp_adjust.
(spu_elf_setup): Init line_size_log2 and num_lines_log2.
(spu_elf_find_overlays): For soft-icache, mark any section within cache
area as an overlay, and check that no other overlays exist.  Look up
icache overlay manager entry sym.
(BRA_STUBS, BRA, BRASL): Define.
(enum _stub_type): Replace ovl_stub with call_ovl_stub and br*_ovl_stub.
(needs_ovl_stub): Adjust for soft-icache.  Return priority encoded
in branch insn.
(count_stub, build_stub): Support soft-icache.
(build_spuear_stubs, process_stubs): Adjust build_stub call.
(spu_elf_size_stubs): Size soft-icache stubs.
(overlay_index): New function.
(spu_elf_build_stubs): Make static.  Support soft-icache.
(spu_elf_check_vma): Don't turn off auto_overlay if soft-icache.
(find_function_stack_adjust): Save lr store and stack adjust insn
offsets.
(maybe_insert_function): Adjust find_function_stack_adjust call.
(mark_functions_via_relocs): Retrieve priority.
(remove_cycles): Only warn about pruned arcs when stack_analysis.
(sort_calls): Sort by priority first.
(mark_overlay_section): Ignore .ovl.init.
(sum_stack): Only print when stack_analysis.
(print_one_overlay_section): New function, extracted from..
(spu_elf_auto_overlay): ..here.  Support soft-icache overlays.
(spu_elf_stack_analysis): Only print when htab->stack_analysis.
(spu_elf_final_link): Call spu_elf_stack_analysis for lrlive
analysis.  Call spu_elf_build_stubs.
(spu_elf_relocate_section): For soft-icache encode overlay index
into addresses.
(spu_elf_output_symbol_hook): Support soft-icache.
(spu_elf_modify_program_headers: Likewise.
* elf32-spu.h (struct spu_elf_params): Add lrlive_analysis.  Rename
num_regions to num_lines.  Add line_size and max_branch.
(enum _ovly_flavour): Add ovly_soft_icache.
(spu_elf_build_stubs): Delete.
gas/
* config/tc-spu.c (md_pseudo_table): Add "brinfo".
(brinfo): New var.
(md_assemble): Poke brinfo into branch instructions.
(spu_brinfo): New function.
(md_apply_fix): Don't assume insn fields start off at zero, mask
them to remove possible brinfo.
ld/
* emultempl/spuelf.em (params): Init new fields.
(num_lines_set, line_size_set, icache_mgr, icache_mgr_stream): New vars.
(spu_place_special_section): Adjust placement for soft-icache.  Pad
soft-icache section to a fixed size.  Clear addr_tree.
(spu_elf_load_ovl_mgr): Support soft-icache.  Map overlay manager
sections a little more intelligently.
(gld${EMULATION_NAME}_finish): Don't call spu_elf_build_stubs.
(OPTION_SPU_NUM_LINES): Rename from OPTION_SPU_NUM_REGIONS.
(OPTION_SPU_SOFT_ICACHE, OPTION_SPU_LINE_SIZE): Define.
(OPTION_SPU_LRLIVE): Define.
(PARSE_AND_LIST_LONGOPTS): Add new soft-icache options.
(PARSE_AND_LIST_OPTIONS): Likewise.
(PARSE_AND_LIST_ARGS_CASES): Handle them.
* emultempl/spu_icache.S: Dummy file.
* emultempl/spu_icache.o_c: Regenerate.
* Makefile.am (eelf32_spu.c): Depend on spu_icache.o_c.
(spu_icache.o_c): Add rule to build.
(CLEANFILES): Zap temp files.
(EXTRA_DIST): Add spu_icache.o_c.
* Makefile.in: Regenerate.
ld/testsuite/
* ld-spu/ovl.d: Allow for absolute branches in stubs.
* ld-spu/ovl2.d: Likewise.

14 files changed:
bfd/ChangeLog
bfd/elf32-spu.c
bfd/elf32-spu.h
gas/ChangeLog
gas/config/tc-spu.c
ld/ChangeLog
ld/Makefile.am
ld/Makefile.in
ld/emultempl/spu_icache.S [new file with mode: 0644]
ld/emultempl/spu_icache.o_c [new file with mode: 0644]
ld/emultempl/spuelf.em
ld/testsuite/ChangeLog
ld/testsuite/ld-spu/ovl.d
ld/testsuite/ld-spu/ovl2.d

index 477cad0938b53e02f3b35f87abf7521bebf5942c..83d6a20f4fe3b253def58bab6069f79360bf6fcc 100644 (file)
@@ -1,3 +1,46 @@
+2009-01-12  Alan Modra  <amodra@bigpond.net.au>
+
+       * elf32-spu.c (struct spu_link_hash_table): Add init, line_size_log2,
+       num_lines_log2.
+       (struct got_entry): Add br_addr.
+       (struct call_info): Add priority.
+       (struct function_info): Add lr_store and sp_adjust.
+       (spu_elf_setup): Init line_size_log2 and num_lines_log2.
+       (spu_elf_find_overlays): For soft-icache, mark any section within cache
+       area as an overlay, and check that no other overlays exist.  Look up
+       icache overlay manager entry sym.
+       (BRA_STUBS, BRA, BRASL): Define.
+       (enum _stub_type): Replace ovl_stub with call_ovl_stub and br*_ovl_stub.
+       (needs_ovl_stub): Adjust for soft-icache.  Return priority encoded
+       in branch insn.
+       (count_stub, build_stub): Support soft-icache.
+       (build_spuear_stubs, process_stubs): Adjust build_stub call.
+       (spu_elf_size_stubs): Size soft-icache stubs.
+       (overlay_index): New function.
+       (spu_elf_build_stubs): Make static.  Support soft-icache.
+       (spu_elf_check_vma): Don't turn off auto_overlay if soft-icache.
+       (find_function_stack_adjust): Save lr store and stack adjust insn
+       offsets.
+       (maybe_insert_function): Adjust find_function_stack_adjust call.
+       (mark_functions_via_relocs): Retrieve priority.
+       (remove_cycles): Only warn about pruned arcs when stack_analysis.
+       (sort_calls): Sort by priority first.
+       (mark_overlay_section): Ignore .ovl.init.
+       (sum_stack): Only print when stack_analysis.
+       (print_one_overlay_section): New function, extracted from..
+       (spu_elf_auto_overlay): ..here.  Support soft-icache overlays.
+       (spu_elf_stack_analysis): Only print when htab->stack_analysis.
+       (spu_elf_final_link): Call spu_elf_stack_analysis for lrlive
+       analysis.  Call spu_elf_build_stubs.
+       (spu_elf_relocate_section): For soft-icache encode overlay index
+       into addresses.
+       (spu_elf_output_symbol_hook): Support soft-icache.
+       (spu_elf_modify_program_headers: Likewise.
+       * elf32-spu.h (struct spu_elf_params): Add lrlive_analysis.  Rename
+       num_regions to num_lines.  Add line_size and max_branch.
+       (enum _ovly_flavour): Add ovly_soft_icache.
+       (spu_elf_build_stubs): Delete.
+
 2009-01-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * elflink.c (_bfd_elf_section_already_linked): Handle g++-3.4
index 447aa8da0a0deb4aa7b07ef75084fbb3a2a0427d..1592c3bbc48d8815e87f39c412b6385ab06d95ca 100644 (file)
@@ -1,6 +1,6 @@
 /* SPU specific support for 32-bit ELF
 
-   Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+   Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -301,6 +301,7 @@ struct spu_link_hash_table
 
   /* Shortcuts to overlay sections.  */
   asection *ovtab;
+  asection *init;
   asection *toe;
   asection **ovl_sec;
 
@@ -320,6 +321,10 @@ struct spu_link_hash_table
   /* Total number of overlays.  */
   unsigned int num_overlays;
 
+  /* For soft icache.  */
+  unsigned int line_size_log2;
+  unsigned int num_lines_log2;
+
   /* How much memory we have.  */
   unsigned int local_store;
   /* Local store --auto-overlay should reserve for non-overlay
@@ -345,7 +350,10 @@ struct got_entry
 {
   struct got_entry *next;
   unsigned int ovl;
-  bfd_vma addend;
+  union {
+    bfd_vma addend;
+    bfd_vma br_addr;
+  };
   bfd_vma stub_addr;
 };
 
@@ -360,6 +368,7 @@ struct call_info
   unsigned int max_depth;
   unsigned int is_tail : 1;
   unsigned int is_pasted : 1;
+  unsigned int priority : 13;
 };
 
 struct function_info
@@ -382,6 +391,10 @@ struct function_info
   unsigned int call_count;
   /* Address range of (this part of) function.  */
   bfd_vma lo, hi;
+  /* Offset where we found a store of lr, or -1 if none found.  */
+  bfd_vma lr_store;
+  /* Offset where we found the stack adjustment insn.  */
+  bfd_vma sp_adjust;
   /* Stack usage.  */
   int stack;
   /* Distance from root of call tree.  Tail and hot/cold branches
@@ -415,6 +428,9 @@ struct spu_elf_stack_info
   struct function_info fun[1];
 };
 
+static struct function_info *find_function (asection *, bfd_vma,
+                                           struct bfd_link_info *);
+
 /* Create a spu ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
@@ -449,6 +465,8 @@ spu_elf_setup (struct bfd_link_info *info, struct spu_elf_params *params)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
   htab->params = params;
+  htab->line_size_log2 = bfd_log2 (htab->params->line_size);
+  htab->num_lines_log2 = bfd_log2 (htab->params->num_lines);
 }
 
 /* Find the symbol for the given R_SYMNDX in IBFD and set *HP and *SYMP
@@ -597,6 +615,7 @@ spu_elf_find_overlays (struct bfd_link_info *info)
   unsigned int i, n, ovl_index, num_buf;
   asection *s;
   bfd_vma ovl_end;
+  const char *ovly_mgr_entry;
 
   if (info->output_bfd->section_count < 2)
     return FALSE;
@@ -622,51 +641,149 @@ spu_elf_find_overlays (struct bfd_link_info *info)
   /* Sort them by vma.  */
   qsort (alloc_sec, n, sizeof (*alloc_sec), sort_sections);
 
-  /* Look for overlapping vmas.  Any with overlap must be overlays.
-     Count them.  Also count the number of overlay regions.  */
   ovl_end = alloc_sec[0]->vma + alloc_sec[0]->size;
-  for (ovl_index = 0, num_buf = 0, i = 1; i < n; i++)
+  if (htab->params->ovly_flavour == ovly_soft_icache)
     {
-      s = alloc_sec[i];
-      if (s->vma < ovl_end)
+      /* Look for an overlapping vma to find the first overlay section.  */
+      bfd_vma vma_start = 0;
+      bfd_vma lma_start = 0;
+
+      for (i = 1; i < n; i++)
        {
-         asection *s0 = alloc_sec[i - 1];
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
+           {
+             asection *s0 = alloc_sec[i - 1];
+             vma_start = s0->vma;
+             lma_start = s0->lma;
+             ovl_end = (s0->vma
+                        + ((bfd_vma) 1
+                           << (htab->num_lines_log2 + htab->line_size_log2)));
+             --i;
+             break;
+           }
+         else
+           ovl_end = s->vma + s->size;
+       }
 
-         if (spu_elf_section_data (s0)->u.o.ovl_index == 0)
+      /* Now find any sections within the cache area.  */
+      for (ovl_index = 0, num_buf = 0; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma >= ovl_end)
+           break;
+
+         /* A section in an overlay area called .ovl.init is not
+            an overlay, in the sense that it might be loaded in
+            by the overlay manager, but rather the initial
+            section contents for the overlay buffer.  */
+         if (strncmp (s->name, ".ovl.init", 9) != 0)
            {
-             alloc_sec[ovl_index] = s0;
-             spu_elf_section_data (s0)->u.o.ovl_index = ++ovl_index;
-             spu_elf_section_data (s0)->u.o.ovl_buf = ++num_buf;
+             num_buf = ((s->vma - vma_start) >> htab->line_size_log2) + 1;
+             if (((s->vma - vma_start) & (htab->params->line_size - 1))
+                 || ((s->lma - lma_start) & (htab->params->line_size - 1)))
+               {
+                 info->callbacks->einfo (_("%X%P: overlay section %A "
+                                           "does not start on a cache line.\n"),
+                                         s);
+                 return FALSE;
+               }
+             else if (s->size > htab->params->line_size)
+               {
+                 info->callbacks->einfo (_("%X%P: overlay section %A "
+                                           "is larger than a cache line.\n"),
+                                         s);
+                 return FALSE;
+               }
+
+             alloc_sec[ovl_index++] = s;
+             spu_elf_section_data (s)->u.o.ovl_index
+               = ((s->lma - lma_start) >>  htab->line_size_log2) + 1;
+             spu_elf_section_data (s)->u.o.ovl_buf = num_buf;
            }
-         alloc_sec[ovl_index] = s;
-         spu_elf_section_data (s)->u.o.ovl_index = ++ovl_index;
-         spu_elf_section_data (s)->u.o.ovl_buf = num_buf;
-         if (s0->vma != s->vma)
+       }
+
+      /* Ensure there are no more overlay sections.  */
+      for ( ; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
            {
-             info->callbacks->einfo (_("%X%P: overlay sections %A and %A "
-                                       "do not start at the same address.\n"),
-                                     s0, s);
+             info->callbacks->einfo (_("%X%P: overlay section %A "
+                                       "is not in cache area.\n"),
+                                     alloc_sec[i-1]);
              return FALSE;
            }
-         if (ovl_end < s->vma + s->size)
+         else
+           ovl_end = s->vma + s->size;
+       }
+    }
+  else
+    {
+      /* Look for overlapping vmas.  Any with overlap must be overlays.
+        Count them.  Also count the number of overlay regions.  */
+      for (ovl_index = 0, num_buf = 0, i = 1; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
+           {
+             asection *s0 = alloc_sec[i - 1];
+
+             if (spu_elf_section_data (s0)->u.o.ovl_index == 0)
+               {
+                 ++num_buf;
+                 if (strncmp (s0->name, ".ovl.init", 9) != 0)
+                   {
+                     alloc_sec[ovl_index] = s0;
+                     spu_elf_section_data (s0)->u.o.ovl_index = ++ovl_index;
+                     spu_elf_section_data (s0)->u.o.ovl_buf = num_buf;
+                   }
+                 else
+                   ovl_end = s->vma + s->size;
+               }
+             if (strncmp (s->name, ".ovl.init", 9) != 0)
+               {
+                 alloc_sec[ovl_index] = s;
+                 spu_elf_section_data (s)->u.o.ovl_index = ++ovl_index;
+                 spu_elf_section_data (s)->u.o.ovl_buf = num_buf;
+                 if (s0->vma != s->vma)
+                   {
+                     info->callbacks->einfo (_("%X%P: overlay sections %A "
+                                               "and %A do not start at the "
+                                               "same address.\n"),
+                                             s0, s);
+                     return FALSE;
+                   }
+                 if (ovl_end < s->vma + s->size)
+                   ovl_end = s->vma + s->size;
+               }
+           }
+         else
            ovl_end = s->vma + s->size;
        }
-      else
-       ovl_end = s->vma + s->size;
     }
 
   htab->num_overlays = ovl_index;
   htab->num_buf = num_buf;
   htab->ovl_sec = alloc_sec;
-  htab->ovly_load = elf_link_hash_lookup (&htab->elf, "__ovly_load",
+  ovly_mgr_entry = "__ovly_load";
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    ovly_mgr_entry = "__icache_br_handler";
+  htab->ovly_load = elf_link_hash_lookup (&htab->elf, ovly_mgr_entry,
                                          FALSE, FALSE, FALSE);
-  htab->ovly_return = elf_link_hash_lookup (&htab->elf, "__ovly_return",
-                                           FALSE, FALSE, FALSE);
+  if (htab->params->ovly_flavour != ovly_soft_icache)
+    htab->ovly_return = elf_link_hash_lookup (&htab->elf, "__ovly_return",
+                                             FALSE, FALSE, FALSE);
   return ovl_index != 0;
 }
 
-#define BRSL   0x33000000
+/* Non-zero to use bra in overlay stubs rather than br.  */
+#define BRA_STUBS 0
+
+#define BRA    0x30000000
+#define BRASL  0x31000000
 #define BR     0x32000000
+#define BRSL   0x33000000
 #define NOP    0x40200000
 #define LNOP   0x00200000
 #define ILA    0x42000000
@@ -736,7 +853,15 @@ maybe_needs_stubs (asection *input_section)
 enum _stub_type
 {
   no_stub,
-  ovl_stub,
+  call_ovl_stub,
+  br000_ovl_stub,
+  br001_ovl_stub,
+  br010_ovl_stub,
+  br011_ovl_stub,
+  br100_ovl_stub,
+  br101_ovl_stub,
+  br110_ovl_stub,
+  br111_ovl_stub,
   nonovl_stub,
   stub_error
 };
@@ -756,8 +881,9 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
   struct spu_link_hash_table *htab = spu_hash_table (info);
   enum elf_spu_reloc_type r_type;
   unsigned int sym_type;
-  bfd_boolean branch;
+  bfd_boolean branch, hint, call;
   enum _stub_type ret = no_stub;
+  bfd_byte insn[4];
 
   if (sym_sec == NULL
       || sym_sec->output_section == bfd_abs_section_ptr
@@ -775,14 +901,9 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
         makes setjmp/longjmp between overlays work.  */
       if (strncmp (h->root.root.string, "setjmp", 6) == 0
          && (h->root.root.string[6] == '\0' || h->root.root.string[6] == '@'))
-       ret = ovl_stub;
+       ret = call_ovl_stub;
     }
 
-  /* Usually, symbols in non-overlay sections don't need stubs.  */
-  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index == 0
-      && !htab->params->non_overlay_stubs)
-    return ret;
-
   if (h != NULL)
     sym_type = h->type;
   else
@@ -790,10 +911,10 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
 
   r_type = ELF32_R_TYPE (irela->r_info);
   branch = FALSE;
+  hint = FALSE;
+  call = FALSE;
   if (r_type == R_SPU_REL16 || r_type == R_SPU_ADDR16)
     {
-      bfd_byte insn[4];
-
       if (contents == NULL)
        {
          contents = insn;
@@ -806,10 +927,12 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
       else
        contents += irela->r_offset;
 
-      if (is_branch (contents) || is_hint (contents))
+      branch = is_branch (contents);
+      hint = is_hint (contents);
+      if (branch || hint)
        {
-         branch = TRUE;
-         if ((contents[0] & 0xfd) == 0x31
+         call = (contents[0] & 0xfd) == 0x31;
+         if (call
              && sym_type != STT_FUNC
              && contents != insn)
            {
@@ -840,20 +963,45 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
        }
     }
 
-  if (sym_type != STT_FUNC
-      && !branch
-      && (sym_sec->flags & SEC_CODE) == 0)
+  if ((!branch && htab->params->ovly_flavour == ovly_soft_icache)
+      || (sym_type != STT_FUNC
+         && !(branch || hint)
+         && (sym_sec->flags & SEC_CODE) == 0))
+    return no_stub;
+
+  /* Usually, symbols in non-overlay sections don't need stubs.  */
+  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index == 0
+      && !htab->params->non_overlay_stubs)
     return ret;
 
   /* A reference from some other section to a symbol in an overlay
      section needs a stub.  */
   if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index
        != spu_elf_section_data (input_section->output_section)->u.o.ovl_index)
-    ret = ovl_stub;
+    {
+      if (call || sym_type == STT_FUNC)
+       ret = call_ovl_stub;
+      else
+       {
+         ret = br000_ovl_stub;
+
+         if (branch)
+           {
+             unsigned int lrlive = (contents[1] & 0x70) >> 4;
+             ret += lrlive;
+           }
+       }
+    }
 
   /* If this insn isn't a branch then we are possibly taking the
-     address of a function and passing it out somehow.  */
-  return !branch && sym_type == STT_FUNC ? nonovl_stub : ret;
+     address of a function and passing it out somehow.  Soft-icache code
+     always generates inline code to do indirect branches.  */
+  if (!(branch || hint)
+      && sym_type == STT_FUNC
+      && htab->params->ovly_flavour != ovly_soft_icache)
+    ret = nonovl_stub;
+
+  return ret;
 }
 
 static bfd_boolean
@@ -891,6 +1039,12 @@ count_stub (struct spu_link_hash_table *htab,
       head = elf_local_got_ents (ibfd) + ELF32_R_SYM (irela->r_info);
     }
 
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      htab->stub_count[ovl] += 1;
+      return TRUE;
+    }
+
   addend = 0;
   if (irela != NULL)
     addend = irela->r_addend;
@@ -963,10 +1117,19 @@ ovl_stub_size (enum _ovly_flavour ovly_flavour)
    ila $78,ovl_number
    lnop
    ila $79,target_address
-   br __ovly_load  */
+   br __ovly_load
+
+   Software icache stubs are:
+
+   .word target_index
+   .word target_ia;
+   .word lrlive_branchlocalstoreaddr;
+   brasl $75,__icache_br_handler
+   .quad xor_pattern
+*/
 
 static bfd_boolean
-build_stub (struct spu_link_hash_table *htab,
+build_stub (struct bfd_link_info *info,
            bfd *ibfd,
            asection *isec,
            enum _stub_type stub_type,
@@ -975,10 +1138,12 @@ build_stub (struct spu_link_hash_table *htab,
            bfd_vma dest,
            asection *dest_sec)
 {
-  unsigned int ovl, dest_ovl;
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  unsigned int ovl, dest_ovl, set_id;
   struct got_entry *g, **head;
   asection *sec;
-  bfd_vma addend, from, to;
+  bfd_vma addend, from, to, br_dest, patt;
+  unsigned int lrlive;
 
   ovl = 0;
   if (stub_type != nonovl_stub)
@@ -993,17 +1158,34 @@ build_stub (struct spu_link_hash_table *htab,
   if (irela != NULL)
     addend = irela->r_addend;
 
-  for (g = *head; g != NULL; g = g->next)
-    if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
-      break;
-  if (g == NULL)
-    abort ();
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      g = bfd_malloc (sizeof *g);
+      if (g == NULL)
+       return FALSE;
+      g->ovl = ovl;
+      g->br_addr = 0;
+      if (irela != NULL)
+       g->br_addr = (irela->r_offset
+                     + isec->output_offset
+                     + isec->output_section->vma);
+      g->next = *head;
+      *head = g;
+    }
+  else
+    {
+      for (g = *head; g != NULL; g = g->next)
+       if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+         break;
+      if (g == NULL)
+       abort ();
 
-  if (g->ovl == 0 && ovl != 0)
-    return TRUE;
+      if (g->ovl == 0 && ovl != 0)
+       return TRUE;
 
-  if (g->stub_addr != (bfd_vma) -1)
-    return TRUE;
+      if (g->stub_addr != (bfd_vma) -1)
+       return TRUE;
+    }
 
   sec = htab->stub_sec[ovl];
   dest += dest_sec->output_offset + dest_sec->output_section->vma;
@@ -1029,17 +1211,138 @@ build_stub (struct spu_link_hash_table *htab,
                  sec->contents + sec->size + 4);
       bfd_put_32 (sec->owner, ILA + ((dest << 7) & 0x01ffff80) + 79,
                  sec->contents + sec->size + 8);
-      bfd_put_32 (sec->owner, BR + (((to - (from + 12)) << 5) & 0x007fff80),
-                 sec->contents + sec->size + 12);
+      if (!BRA_STUBS)
+       bfd_put_32 (sec->owner, BR + (((to - (from + 12)) << 5) & 0x007fff80),
+                   sec->contents + sec->size + 12);
+      else
+       bfd_put_32 (sec->owner, BRA + ((to << 5) & 0x007fff80),
+                   sec->contents + sec->size + 12);
       break;
 
     case ovly_compact:
-      bfd_put_32 (sec->owner, BRSL + (((to - from) << 5) & 0x007fff80) + 75,
-                 sec->contents + sec->size);
+      if (!BRA_STUBS)
+       bfd_put_32 (sec->owner, BRSL + (((to - from) << 5) & 0x007fff80) + 75,
+                   sec->contents + sec->size);
+      else
+       bfd_put_32 (sec->owner, BRASL + ((to << 5) & 0x007fff80) + 75,
+                   sec->contents + sec->size);
       bfd_put_32 (sec->owner, (dest & 0x3ffff) | (dest_ovl << 18),
                  sec->contents + sec->size + 4);
       break;
 
+    case ovly_soft_icache:
+      lrlive = 0;
+      if (stub_type == nonovl_stub)
+       ;
+      else if (stub_type == call_ovl_stub)
+       /* A brsl makes lr live and *(*sp+16) is live.
+          Tail calls have the same liveness.  */
+       lrlive = 5;
+      else if (!htab->params->lrlive_analysis)
+       /* Assume stack frame and lr save.  */
+       lrlive = 1;
+      else if (irela != NULL)
+       {
+         /* Analyse branch instructions.  */
+         struct function_info *caller;
+         bfd_vma off;
+
+         caller = find_function (isec, irela->r_offset, info);
+         if (caller->start == NULL)
+           off = irela->r_offset;
+         else
+           {
+             struct function_info *found = NULL;
+
+             /* Find the earliest piece of this function that
+                has frame adjusting instructions.  We might
+                see dynamic frame adjustment (eg. for alloca)
+                in some later piece, but functions using
+                alloca always set up a frame earlier.  Frame
+                setup instructions are always in one piece.  */
+             if (caller->lr_store != (bfd_vma) -1
+                 || caller->sp_adjust != (bfd_vma) -1)
+               found = caller;
+             while (caller->start != NULL)
+               {
+                 caller = caller->start;
+                 if (caller->lr_store != (bfd_vma) -1
+                     || caller->sp_adjust != (bfd_vma) -1)
+                   found = caller;
+               }
+             if (found != NULL)
+               caller = found;
+             off = (bfd_vma) -1;
+           }
+
+         if (off > caller->sp_adjust)
+           {
+             if (off > caller->lr_store)
+               /* Only *(*sp+16) is live.  */
+               lrlive = 1;
+             else
+               /* If no lr save, then we must be in a
+                  leaf function with a frame.
+                  lr is still live.  */
+               lrlive = 4;
+           }
+         else if (off > caller->lr_store)
+           {
+             /* Between lr save and stack adjust.  */
+             lrlive = 3;
+             /* This should never happen since prologues won't
+                be split here.  */
+             BFD_ASSERT (0);
+           }
+         else
+           /* On entry to function.  */
+           lrlive = 5;
+
+         if (stub_type != br000_ovl_stub
+             && lrlive != stub_type - br000_ovl_stub)
+           info->callbacks->einfo (_("%A:0x%v lrlive .brinfo (%u) differs "
+                                     "from analysis (%u)\n"),
+                                   isec, irela->r_offset, lrlive,
+                                   stub_type - br000_ovl_stub);
+       }
+
+      /* If given lrlive info via .brinfo, use it.  */
+      if (stub_type > br000_ovl_stub)
+       lrlive = stub_type - br000_ovl_stub;
+
+      /* The branch that uses this stub goes to stub_addr + 12.  We'll
+         set up an xor pattern that can be used by the icache manager
+        to modify this branch to go directly to its destination.  */
+      g->stub_addr += 12;
+      br_dest = g->stub_addr;
+      if (irela == NULL)
+       {
+         /* Except in the case of _SPUEAR_ stubs, the branch in
+            question is the one in the stub itself.  */
+         BFD_ASSERT (stub_type == nonovl_stub);
+         g->br_addr = g->stub_addr;
+         br_dest = to;
+       }
+
+      bfd_put_32 (sec->owner, dest_ovl - 1,
+                 sec->contents + sec->size + 0);
+      set_id = (dest_ovl - 1) >> htab->num_lines_log2;
+      bfd_put_32 (sec->owner, (set_id << 18) | (dest & 0x3ffff),
+                 sec->contents + sec->size + 4);
+      bfd_put_32 (sec->owner, (lrlive << 29) | (g->br_addr & 0x3ffff),
+                 sec->contents + sec->size + 8);
+      bfd_put_32 (sec->owner, BRASL + ((to << 5) & 0x007fff80) + 75,
+                 sec->contents + sec->size + 12);
+      patt = dest ^ br_dest;
+      if (irela != NULL && ELF32_R_TYPE (irela->r_info) == R_SPU_REL16)
+       patt = (dest - g->br_addr) ^ (br_dest - g->br_addr);
+      bfd_put_32 (sec->owner, (patt << 5) & 0x007fff80,
+                 sec->contents + sec->size + 16 + (g->br_addr & 0xf));
+      if (ovl == 0)
+       /* Extra space for linked list entries.  */
+       sec->size += 16;
+      break;
+
     default:
       abort ();
     }
@@ -1144,7 +1447,7 @@ build_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
       && (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index != 0
          || htab->params->non_overlay_stubs))
     {
-      return build_stub (htab, NULL, NULL, nonovl_stub, h, NULL,
+      return build_stub (info, NULL, NULL, nonovl_stub, h, NULL,
                         h->root.u.def.value, sym_sec);
     }
   
@@ -1256,7 +1559,7 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
                  else
                    dest = sym->st_value;
                  dest += irela->r_addend;
-                 if (!build_stub (htab, ibfd, isec, stub_type, h, irela,
+                 if (!build_stub (info, ibfd, isec, stub_type, h, irela,
                                   dest, sym_sec))
                    goto error_ret_free_internal;
                }
@@ -1291,6 +1594,7 @@ spu_elf_size_stubs (struct bfd_link_info *info)
   flagword flags;
   unsigned int i;
   asection *stub;
+  const char *ovout;
 
   if (!process_stubs (info, FALSE))
     return 0;
@@ -1318,6 +1622,9 @@ spu_elf_size_stubs (struct bfd_link_info *info)
                                     htab->params->ovly_flavour + 3))
     return 0;
   stub->size = htab->stub_count[0] * ovl_stub_size (htab->params->ovly_flavour);
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    /* Extra space for linked list entries.  */
+    stub->size += htab->stub_count[0] * 16;
   (*htab->params->place_spu_section) (stub, NULL, ".text");
 
   for (i = 0; i < htab->num_overlays; ++i)
@@ -1334,19 +1641,6 @@ spu_elf_size_stubs (struct bfd_link_info *info)
       (*htab->params->place_spu_section) (stub, osec, NULL);
     }
 
- /* htab->ovtab consists of two arrays.
-    .  struct {
-    .    u32 vma;
-    .    u32 size;
-    .    u32 file_off;
-    .    u32 buf;
-    .  } _ovly_table[];
-    .
-    .  struct {
-    .    u32 mapped;
-    .  } _ovly_buf_table[];
-    .  */
-
   flags = (SEC_ALLOC | SEC_LOAD
           | SEC_HAS_CONTENTS | SEC_IN_MEMORY);
   htab->ovtab = bfd_make_section_anyway_with_flags (ibfd, ".ovtab", flags);
@@ -1354,14 +1648,51 @@ spu_elf_size_stubs (struct bfd_link_info *info)
       || !bfd_set_section_alignment (ibfd, htab->ovtab, 4))
     return 0;
 
-  htab->ovtab->size = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
-  (*htab->params->place_spu_section) (htab->ovtab, NULL, ".data");
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      /* Space for icache manager tables.
+        a) Tag array, one quadword per cache line.
+        b) Linked list elements, max_branch per line quadwords.
+        c) Indirect branch descriptors, 8 quadwords.  */
+      htab->ovtab->size = 16 * (((1 + htab->params->max_branch)
+                                << htab->num_lines_log2)
+                               + 8);
+
+      htab->init = bfd_make_section_anyway_with_flags (ibfd, ".ovini", flags);
+      if (htab->init == NULL
+         || !bfd_set_section_alignment (ibfd, htab->init, 4))
+       return 0;
+
+      htab->init->size = 16;
+      (*htab->params->place_spu_section) (htab->init, NULL, ".ovl.init");
+    }
+  else
+    {
+      /* htab->ovtab consists of two arrays.
+        .      struct {
+        .        u32 vma;
+        .        u32 size;
+        .        u32 file_off;
+        .        u32 buf;
+        .      } _ovly_table[];
+        .
+        .      struct {
+        .        u32 mapped;
+        .      } _ovly_buf_table[];
+        .  */
+
+      htab->ovtab->size = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
+    }
+  ovout = ".data";
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    ovout = ".data.icache";
+  (*htab->params->place_spu_section) (htab->ovtab, NULL, ovout);
 
   htab->toe = bfd_make_section_anyway_with_flags (ibfd, ".toe", SEC_ALLOC);
   if (htab->toe == NULL
       || !bfd_set_section_alignment (ibfd, htab->toe, 4))
     return 0;
-  htab->toe->size = 16;
+  htab->toe->size = htab->params->ovly_flavour == ovly_soft_icache ? 256 : 16;
   (*htab->params->place_spu_section) (htab->toe, NULL, ".toe");
 
   return 2;
@@ -1413,6 +1744,15 @@ spu_elf_open_builtin_lib (bfd **ovl_bfd, const struct _ovl_stream *stream)
   return *ovl_bfd != NULL;
 }
 
+static unsigned int
+overlay_index (asection *sec)
+{
+  if (sec == NULL
+      || sec->output_section == bfd_abs_section_ptr)
+    return 0;
+  return spu_elf_section_data (sec->output_section)->u.o.ovl_index;
+}
+
 /* Define an STT_OBJECT symbol.  */
 
 static struct elf_link_hash_entry *
@@ -1456,7 +1796,7 @@ define_ovtab_symbol (struct spu_link_hash_table *htab, const char *name)
 
 /* Fill in all stubs and the overlay tables.  */
 
-bfd_boolean
+static bfd_boolean
 spu_elf_build_stubs (struct bfd_link_info *info)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
@@ -1480,8 +1820,17 @@ spu_elf_build_stubs (struct bfd_link_info *info)
        htab->stub_sec[i]->size = 0;
       }
 
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_load", FALSE, FALSE, FALSE);
-  htab->ovly_load = h;
+  h = htab->ovly_load;
+  if (h == NULL)
+    {
+      const char *ovly_mgr_entry = "__ovly_load";
+
+      if (htab->params->ovly_flavour == ovly_soft_icache)
+       ovly_mgr_entry = "__icache_br_handler";
+      h = elf_link_hash_lookup (&htab->elf, ovly_mgr_entry,
+                               FALSE, FALSE, FALSE);
+      htab->ovly_load = h;
+    }
   BFD_ASSERT (h != NULL
              && (h->root.type == bfd_link_hash_defined
                  || h->root.type == bfd_link_hash_defweak)
@@ -1496,8 +1845,13 @@ spu_elf_build_stubs (struct bfd_link_info *info)
       return FALSE;
     }
 
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_return", FALSE, FALSE, FALSE);
-  htab->ovly_return = h;
+  h = htab->ovly_return;
+  if (h == NULL && htab->params->ovly_flavour != ovly_soft_icache)
+    {
+      h = elf_link_hash_lookup (&htab->elf, "__ovly_return",
+                               FALSE, FALSE, FALSE);
+      htab->ovly_return = h;
+    }
 
   /* Fill in all the stubs.  */
   process_stubs (info, TRUE);
@@ -1522,61 +1876,154 @@ spu_elf_build_stubs (struct bfd_link_info *info)
       htab->stub_sec[i]->rawsize = 0;
     }
 
+  if (htab->ovtab == NULL || htab->ovtab->size == 0)
+    return TRUE;
+
   htab->ovtab->contents = bfd_zalloc (htab->ovtab->owner, htab->ovtab->size);
   if (htab->ovtab->contents == NULL)
     return FALSE;
 
-  /* Write out _ovly_table.  */
   p = htab->ovtab->contents;
-  /* set low bit of .size to mark non-overlay area as present.  */
-  p[7] = 1;
-  obfd = htab->ovtab->output_section->owner;
-  for (s = obfd->sections; s != NULL; s = s->next)
+  if (htab->params->ovly_flavour == ovly_soft_icache)
     {
-      unsigned int ovl_index = spu_elf_section_data (s)->u.o.ovl_index;
+#define BI_HANDLER "__icache_ptr___icache_bi_handler0"
+      char name[sizeof (BI_HANDLER)];
+      bfd_vma off, icache_base, linklist, bihand;
+
+      h = define_ovtab_symbol (htab, "__icache_tagbase");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 0;
+      h->size = 16 << htab->num_lines_log2;
+      off = h->size;
+      icache_base = htab->ovl_sec[0]->vma;
+      linklist = (htab->ovtab->output_section->vma
+                 + htab->ovtab->output_offset
+                 + off);
+      for (i = 0; i < htab->params->num_lines; i++)
+       {
+         bfd_vma line_end = icache_base + ((i + 1) << htab->line_size_log2);
+         bfd_vma stub_base = line_end - htab->params->max_branch * 32;
+         bfd_vma link_elem = linklist + i * htab->params->max_branch * 16;
+         bfd_vma locator = link_elem - stub_base / 2;
+
+         bfd_put_32 (htab->ovtab->owner, locator, p + 4);
+         bfd_put_16 (htab->ovtab->owner, link_elem, p + 8);
+         bfd_put_16 (htab->ovtab->owner, link_elem, p + 10);
+         bfd_put_16 (htab->ovtab->owner, link_elem, p + 12);
+         bfd_put_16 (htab->ovtab->owner, link_elem, p + 14);
+         p += 16;
+       }
+
+      h = define_ovtab_symbol (htab, "__icache_linked_list");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = off;
+      h->size = htab->params->max_branch << (htab->num_lines_log2 + 4);
+      off += h->size;
+      p += h->size;
+
+      h = elf_link_hash_lookup (&htab->elf, "__icache_bi_handler",
+                                FALSE, FALSE, FALSE);
+      bihand = 0;
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && h->def_regular)
+       bihand = (h->root.u.def.value
+                 + h->root.u.def.section->output_offset
+                 + h->root.u.def.section->output_section->vma);
+      memcpy (name, BI_HANDLER, sizeof (BI_HANDLER));
+      for (i = 0; i < 8; i++)
+       {
+         name[sizeof (BI_HANDLER) - 2] = '0' + i;
+         h = define_ovtab_symbol (htab, name);
+         if (h == NULL)
+           return FALSE;
+         h->root.u.def.value = off;
+         h->size = 16;
+         bfd_put_32 (htab->ovtab->owner, bihand, p);
+         bfd_put_32 (htab->ovtab->owner, i << 28, p + 8);
+         p += 16;
+         off += 16;
+       }
+
+      h = define_ovtab_symbol (htab, "__icache_base");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 0;
+      h->root.u.def.section = htab->ovl_sec[0];
+      h->size = htab->num_buf << htab->line_size_log2;
 
-      if (ovl_index != 0)
+      if (htab->init != NULL && htab->init->size != 0)
        {
-         unsigned long off = ovl_index * 16;
-         unsigned int ovl_buf = spu_elf_section_data (s)->u.o.ovl_buf;
+         htab->init->contents = bfd_zalloc (htab->init->owner,
+                                            htab->init->size);
+         if (htab->init->contents == NULL)
+           return FALSE;
 
-         bfd_put_32 (htab->ovtab->owner, s->vma, p + off);
-         bfd_put_32 (htab->ovtab->owner, (s->size + 15) & -16, p + off + 4);
-         /* file_off written later in spu_elf_modify_program_headers.  */
-         bfd_put_32 (htab->ovtab->owner, ovl_buf, p + off + 12);
+         h = define_ovtab_symbol (htab, "__icache_fileoff");
+         if (h == NULL)
+           return FALSE;
+         h->root.u.def.value = 0;
+         h->root.u.def.section = htab->init;
+         h->size = 8;
        }
     }
+  else
+    {
+      /* Write out _ovly_table.  */
+      /* set low bit of .size to mark non-overlay area as present.  */
+      p[7] = 1;
+      obfd = htab->ovtab->output_section->owner;
+      for (s = obfd->sections; s != NULL; s = s->next)
+       {
+         unsigned int ovl_index = spu_elf_section_data (s)->u.o.ovl_index;
 
-  h = define_ovtab_symbol (htab, "_ovly_table");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = 16;
-  h->size = htab->num_overlays * 16;
+         if (ovl_index != 0)
+           {
+             unsigned long off = ovl_index * 16;
+             unsigned int ovl_buf = spu_elf_section_data (s)->u.o.ovl_buf;
+
+             bfd_put_32 (htab->ovtab->owner, s->vma, p + off);
+             bfd_put_32 (htab->ovtab->owner, (s->size + 15) & -16,
+                         p + off + 4);
+             /* file_off written later in spu_elf_modify_program_headers.  */
+             bfd_put_32 (htab->ovtab->owner, ovl_buf, p + off + 12);
+           }
+       }
 
-  h = define_ovtab_symbol (htab, "_ovly_table_end");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16;
-  h->size = 0;
+      h = define_ovtab_symbol (htab, "_ovly_table");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 16;
+      h->size = htab->num_overlays * 16;
 
-  h = define_ovtab_symbol (htab, "_ovly_buf_table");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16;
-  h->size = htab->num_buf * 4;
+      h = define_ovtab_symbol (htab, "_ovly_table_end");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->num_overlays * 16 + 16;
+      h->size = 0;
 
-  h = define_ovtab_symbol (htab, "_ovly_buf_table_end");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
-  h->size = 0;
+      h = define_ovtab_symbol (htab, "_ovly_buf_table");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->num_overlays * 16 + 16;
+      h->size = htab->num_buf * 4;
+
+      h = define_ovtab_symbol (htab, "_ovly_buf_table_end");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
+      h->size = 0;
+    }
 
   h = define_ovtab_symbol (htab, "_EAR_");
   if (h == NULL)
     return FALSE;
   h->root.u.def.section = htab->toe;
   h->root.u.def.value = 0;
-  h->size = 16;
+  h->size = htab->params->ovly_flavour == ovly_soft_icache ? 16 * 16 : 16;
 
   return TRUE;
 }
@@ -1606,15 +2053,22 @@ spu_elf_check_vma (struct bfd_link_info *info)
          return m->sections[i];
 
   /* No need for overlays if it all fits.  */
-  htab->params->auto_overlay = 0;
+  if (htab->params->ovly_flavour != ovly_soft_icache)
+    htab->params->auto_overlay = 0;
   return NULL;
 }
 
 /* OFFSET in SEC (presumably) is the beginning of a function prologue.
-   Search for stack adjusting insns, and return the sp delta.  */
+   Search for stack adjusting insns, and return the sp delta.
+   If a store of lr is found save the instruction offset to *LR_STORE.
+   If a stack adjusting instruction is found, save that offset to
+   *SP_ADJUST.  */
 
 static int
-find_function_stack_adjust (asection *sec, bfd_vma offset)
+find_function_stack_adjust (asection *sec,
+                           bfd_vma offset,
+                           bfd_vma *lr_store,
+                           bfd_vma *sp_adjust)
 {
   int reg[128];
 
@@ -1629,11 +2083,16 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
       if (!bfd_get_section_contents (sec->owner, sec, buf, offset, 4))
        break;
 
-      if (buf[0] == 0x24 /* stqd */)
-       continue;
-
       rt = buf[3] & 0x7f;
       ra = ((buf[2] & 0x3f) << 1) | (buf[3] >> 7);
+
+      if (buf[0] == 0x24 /* stqd */)
+       {
+         if (rt == 0 /* lr */ && ra == 1 /* sp */)
+           *lr_store = offset;
+         continue;
+       }
+
       /* Partly decoded immediate field.  */
       imm = (buf[1] << 9) | (buf[2] << 1) | (buf[3] >> 7);
 
@@ -1647,6 +2106,7 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
            {
              if (reg[rt] > 0)
                break;
+             *sp_adjust = offset;
              return reg[rt];
            }
        }
@@ -1659,6 +2119,7 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
            {
              if (reg[rt] > 0)
                break;
+             *sp_adjust = offset;
              return reg[rt];
            }
        }
@@ -1859,7 +2320,11 @@ maybe_insert_function (asection *sec,
     sinfo->fun[i].u.sym = sym_h;
   sinfo->fun[i].lo = off;
   sinfo->fun[i].hi = off + size;
-  sinfo->fun[i].stack = -find_function_stack_adjust (sec, off);
+  sinfo->fun[i].lr_store = -1;
+  sinfo->fun[i].sp_adjust = -1;
+  sinfo->fun[i].stack = -find_function_stack_adjust (sec, off,
+                                                    &sinfo->fun[i].lr_store,
+                                                    &sinfo->fun[i].sp_adjust);
   sinfo->num_fun += 1;
   return &sinfo->fun[i];
 }
@@ -2079,6 +2544,7 @@ mark_functions_via_relocs (asection *sec,
   Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
   Elf_Internal_Shdr *symtab_hdr;
   void *psyms;
+  unsigned int priority = 0;
   static bfd_boolean warned;
 
   if (!interesting_section (sec)
@@ -2135,6 +2601,12 @@ mark_functions_via_relocs (asection *sec,
          if (is_branch (insn))
            {
              is_call = (insn[0] & 0xfd) == 0x31;
+             priority = insn[1] & 0x0f;
+             priority <<= 8;
+             priority |= insn[2];
+             priority <<= 8;
+             priority |= insn[3];
+             priority >>= 7;
              if ((sym_sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_CODE))
                  != (SEC_ALLOC | SEC_LOAD | SEC_CODE))
                {
@@ -2215,6 +2687,7 @@ mark_functions_via_relocs (asection *sec,
        return FALSE;
       callee->is_tail = !is_call;
       callee->is_pasted = FALSE;
+      callee->priority = priority;
       callee->count = 0;
       if (callee->fun->last_caller != sec)
        {
@@ -2677,7 +3150,10 @@ remove_cycles (struct function_info *fun,
        }
       else if (call->fun->marking)
        {
-         if (!spu_hash_table (info)->params->auto_overlay)
+         struct spu_link_hash_table *htab = spu_hash_table (info);
+
+         if (!htab->params->auto_overlay
+             && htab->params->stack_analysis)
            {
              const char *f1 = func_name (fun);
              const char *f2 = func_name (call->fun);
@@ -2754,7 +3230,7 @@ build_call_tree (struct bfd_link_info *info)
   return for_each_node (mark_detached_root, info, &depth, FALSE);
 }
 
-/* qsort predicate to sort calls by max_depth then count.  */
+/* qsort predicate to sort calls by priority, max_depth then count.  */
 
 static int
 sort_calls (const void *a, const void *b)
@@ -2763,6 +3239,10 @@ sort_calls (const void *a, const void *b)
   struct call_info *const *c2 = b;
   int delta;
 
+  delta = (*c2)->priority - (*c1)->priority;
+  if (delta != 0)
+    return delta;
+
   delta = (*c2)->max_depth - (*c1)->max_depth;
   if (delta != 0)
     return delta;
@@ -2918,9 +3398,10 @@ mark_overlay_section (struct function_info *fun,
     }
 
   /* Don't put entry code into an overlay.  The overlay manager needs
-     a stack!  */
+     a stack!  Also, don't mark .ovl.init as an overlay.  */
   if (fun->lo + fun->sec->output_offset + fun->sec->output_section->vma
-      == info->output_bfd->start_address)
+      == info->output_bfd->start_address
+      || strncmp (fun->sec->output_section->name, ".ovl.init", 9) == 0)
     {
       fun->sec->linker_mark = 0;
       if (fun->rodata != NULL)
@@ -3008,6 +3489,7 @@ collect_lib_sections (struct function_info *fun,
   size = fun->sec->size;
   if (fun->rodata)
     size += fun->rodata->size;
+
   if (size <= lib_param->lib_size)
     {
       *lib_param->lib_sections++ = fun->sec;
@@ -3345,23 +3827,26 @@ sum_stack (struct function_info *fun,
     return TRUE;
 
   f1 = func_name (fun);
-  if (!fun->non_root)
-    info->callbacks->info (_("  %s: 0x%v\n"), f1, (bfd_vma) cum_stack);
-  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
-                         f1, (bfd_vma) stack, (bfd_vma) cum_stack);
-
-  if (has_call)
+  if (htab->params->stack_analysis)
     {
-      info->callbacks->minfo (_("  calls:\n"));
-      for (call = fun->call_list; call; call = call->next)
-       if (!call->is_pasted)
-         {
-           const char *f2 = func_name (call->fun);
-           const char *ann1 = call->fun == max ? "*" : " ";
-           const char *ann2 = call->is_tail ? "t" : " ";
+      if (!fun->non_root)
+       info->callbacks->info (_("  %s: 0x%v\n"), f1, (bfd_vma) cum_stack);
+      info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
+                             f1, (bfd_vma) stack, (bfd_vma) cum_stack);
 
-           info->callbacks->minfo (_("   %s%s %s\n"), ann1, ann2, f2);
-         }
+      if (has_call)
+       {
+         info->callbacks->minfo (_("  calls:\n"));
+         for (call = fun->call_list; call; call = call->next)
+           if (!call->is_pasted)
+             {
+               const char *f2 = func_name (call->fun);
+               const char *ann1 = call->fun == max ? "*" : " ";
+               const char *ann2 = call->is_tail ? "t" : " ";
+
+               info->callbacks->minfo (_("   %s%s %s\n"), ann1, ann2, f2);
+             }
+       }
     }
 
   if (sum_stack_param->emit_stack_syms)
@@ -3430,6 +3915,87 @@ sort_bfds (const void *a, const void *b)
   return strcmp ((*abfd1)->filename, (*abfd2)->filename);
 }
 
+static unsigned int
+print_one_overlay_section (FILE *script,
+                          unsigned int base,
+                          unsigned int count,
+                          unsigned int ovlynum,
+                          unsigned int *ovly_map,
+                          asection **ovly_sections,
+                          struct bfd_link_info *info)
+{
+  unsigned int j;
+         
+  for (j = base; j < count && ovly_map[j] == ovlynum; j++)
+    {
+      asection *sec = ovly_sections[2 * j];
+
+      if (fprintf (script, "   %s%c%s (%s)\n",
+                  (sec->owner->my_archive != NULL
+                   ? sec->owner->my_archive->filename : ""),
+                  info->path_separator,
+                  sec->owner->filename,
+                  sec->name) <= 0)
+       return -1;
+      if (sec->segment_mark)
+       {
+         struct call_info *call = find_pasted_call (sec);
+         while (call != NULL)
+           {
+             struct function_info *call_fun = call->fun;
+             sec = call_fun->sec;
+             if (fprintf (script, "   %s%c%s (%s)\n",
+                          (sec->owner->my_archive != NULL
+                           ? sec->owner->my_archive->filename : ""),
+                          info->path_separator,
+                          sec->owner->filename,
+                          sec->name) <= 0)
+               return -1;
+             for (call = call_fun->call_list; call; call = call->next)
+               if (call->is_pasted)
+                 break;
+           }
+       }
+    }
+
+  for (j = base; j < count && ovly_map[j] == ovlynum; j++)
+    {
+      asection *sec = ovly_sections[2 * j + 1];
+      if (sec != NULL
+         && fprintf (script, "   %s%c%s (%s)\n",
+                     (sec->owner->my_archive != NULL
+                      ? sec->owner->my_archive->filename : ""),
+                     info->path_separator,
+                     sec->owner->filename,
+                     sec->name) <= 0)
+       return -1;
+
+      sec = ovly_sections[2 * j];
+      if (sec->segment_mark)
+       {
+         struct call_info *call = find_pasted_call (sec);
+         while (call != NULL)
+           {
+             struct function_info *call_fun = call->fun;
+             sec = call_fun->rodata;
+             if (sec != NULL
+                 && fprintf (script, "   %s%c%s (%s)\n",
+                             (sec->owner->my_archive != NULL
+                              ? sec->owner->my_archive->filename : ""),
+                             info->path_separator,
+                             sec->owner->filename,
+                             sec->name) <= 0)
+               return -1;
+             for (call = call_fun->call_list; call; call = call->next)
+               if (call->is_pasted)
+                 break;
+           }
+       }
+    }
+
+  return j;
+}
+
 /* Handle --auto-overlay.  */
 
 static void spu_elf_auto_overlay (struct bfd_link_info *)
@@ -3449,6 +4015,7 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
   unsigned int *ovly_map;
   FILE *script;
   unsigned int total_overlay_size, overlay_size;
+  const char *ovly_mgr_entry;
   struct elf_link_hash_entry *h;
   struct _mos_param mos_param;
   struct _uos_param uos_param;
@@ -3480,7 +4047,10 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
     = bfd_get_section_by_name (info->output_bfd, ".interrupt");
 
   htab = spu_hash_table (info);
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_load",
+  ovly_mgr_entry = "__ovly_load";
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    ovly_mgr_entry = "__icache_br_handler";
+  h = elf_link_hash_lookup (&htab->elf, ovly_mgr_entry,
                            FALSE, FALSE, FALSE);
   if (h != NULL
       && (h->root.type == bfd_link_hash_defined
@@ -3539,6 +4109,10 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
            fixed_size -= sec->size;
            total_overlay_size += sec->size;
          }
+       else if ((sec->flags & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)
+                && sec->output_section->owner == info->output_bfd
+                && strncmp (sec->output_section->name, ".ovl.init", 9) == 0)
+         fixed_size -= sec->size;
       if (count != old_count)
        bfd_arr[bfd_count++] = ibfd;
     }
@@ -3589,11 +4163,32 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
   fixed_size += htab->non_ovly_stub * ovl_stub_size (htab->params->ovly_flavour);
   if (fixed_size + mos_param.max_overlay_size <= htab->local_store)
     {
-      /* Guess number of overlays.  Assuming overlay buffer is on
-        average only half full should be conservative.  */
-      ovlynum = total_overlay_size * 2 / (htab->local_store - fixed_size);
-      /* Space for _ovly_table[], _ovly_buf_table[] and toe.  */
-      fixed_size += ovlynum * 16 + 16 + 4 + 16;
+      if (htab->params->ovly_flavour == ovly_soft_icache)
+       {
+         /* Stubs in the non-icache area are bigger.  */
+         fixed_size += htab->non_ovly_stub * 16;
+         /* Space for icache manager tables.
+            a) Tag array, one quadword per cache line.
+            - word 0: ia address of present line, init to zero.
+            - word 1: link locator.  link_elem=stub_addr/2+locator
+            - halfwords 4-7: head/tail pointers for linked lists.  */
+         fixed_size += 16 << htab->num_lines_log2;
+         /* b) Linked list elements, max_branch per line.  */
+         fixed_size += htab->params->max_branch << (htab->num_lines_log2 + 4);
+         /* c) Indirect branch descriptors, 8 quadwords.  */
+         fixed_size += 8 * 16;
+         /* d) Pointers to __ea backing store, 16 quadwords.  */
+         fixed_size += 16 * 16;
+       }
+      else
+       {
+         /* Guess number of overlays.  Assuming overlay buffer is on
+            average only half full should be conservative.  */
+         ovlynum = (total_overlay_size * 2 * htab->params->num_lines
+                    / (htab->local_store - fixed_size));
+         /* Space for _ovly_table[], _ovly_buf_table[] and toe.  */
+         fixed_size += ovlynum * 16 + 16 + 4 + 16;
+       }
     }
 
   if (fixed_size + mos_param.max_overlay_size > htab->local_store)
@@ -3631,7 +4226,9 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
     goto err_exit;
 
   memset (&dummy_caller, 0, sizeof (dummy_caller));
-  overlay_size = (htab->local_store - fixed_size) / htab->params->num_regions;
+  overlay_size = (htab->local_store - fixed_size) / htab->params->num_lines;
+  if (htab->params->line_size != 0)
+    overlay_size = htab->params->line_size;
   base = 0;
   ovlynum = 0;
   while (base < count)
@@ -3722,10 +4319,12 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
                    break;
                  }
            }
+         if (htab->params->ovly_flavour == ovly_soft_icache
+             && num_stubs > htab->params->max_branch)
+           break;
          if (tmp + num_stubs * ovl_stub_size (htab->params->ovly_flavour)
              > overlay_size)
            break;
-         
          size = tmp;
        }
 
@@ -3756,104 +4355,99 @@ spu_elf_auto_overlay (struct bfd_link_info *info)
   if (fprintf (script, "SECTIONS\n{\n") <= 0)
     goto file_err;
 
-  for (region = 1; region <= htab->params->num_regions; region++)
+  if (htab->params->ovly_flavour == ovly_soft_icache)
     {
-      ovlynum = region;
+      if (fprintf (script,
+                  " .data.icache ALIGN (16) : { *(.ovtab) *(.data.icache) }\n"
+                  " . = ALIGN (%u);\n"
+                  " .ovl.init : { *(.ovl.init) }\n"
+                  " . = ABSOLUTE (ADDR (.ovl.init));\n",
+                  htab->params->line_size) <= 0)
+       goto file_err;
+
       base = 0;
-      while (base < count && ovly_map[base] < ovlynum)
-       base++;
+      ovlynum = 1;
+      while (base < count)
+       {
+         unsigned int indx = ovlynum - 1;
+         unsigned int vma, lma;
 
-      if (base == count)
-       break;
+         vma = (indx & (htab->num_lines_log2 - 1)) << htab->line_size_log2;
+         lma = indx << htab->line_size_log2;
+
+         if (fprintf (script, " .ovly%u ABSOLUTE (ADDR (.ovl.init)) + %u "
+                      ": AT (ALIGN (LOADADDR (.ovl.init) + SIZEOF (.ovl.init), 16) + %u) {\n",
+                      ovlynum, vma, lma) <= 0)
+           goto file_err;
+
+         base = print_one_overlay_section (script, base, count, ovlynum,
+                                           ovly_map, ovly_sections, info);
+         if (base == (unsigned) -1)
+           goto file_err;
+
+         if (fprintf (script, "  }\n") <= 0)
+           goto file_err;
+
+         ovlynum++;
+       }
 
-      if (fprintf (script, " OVERLAY :\n {\n") <= 0)
+      if (fprintf (script, " . = ABSOLUTE (ADDR (.ovl.init)) + %u;\n",
+                  1 << (htab->num_lines_log2 + htab->line_size_log2)) <= 0)
+       goto file_err;
+    }
+  else
+    {
+      if (fprintf (script,
+                  " . = ALIGN (16);\n"
+                  " .ovl.init : { *(.ovl.init) }\n"
+                  " . = ABSOLUTE (ADDR (.ovl.init));\n") <= 0)
        goto file_err;
 
-      while (base < count)
+      for (region = 1; region <= htab->params->num_lines; region++)
        {
-         unsigned int j;
-         
-         if (fprintf (script, "  .ovly%u {\n", ovlynum) <= 0)
-           goto file_err;
+         ovlynum = region;
+         base = 0;
+         while (base < count && ovly_map[base] < ovlynum)
+           base++;
 
-         for (j = base; j < count && ovly_map[j] == ovlynum; j++)
-           {
-             asection *sec = ovly_sections[2 * j];
+         if (base == count)
+           break;
 
-             if (fprintf (script, "   %s%c%s (%s)\n",
-                          (sec->owner->my_archive != NULL
-                           ? sec->owner->my_archive->filename : ""),
-                          info->path_separator,
-                          sec->owner->filename,
-                          sec->name) <= 0)
+         if (region == 1)
+           {
+             /* We need to set lma since we are overlaying .ovl.init.  */
+             if (fprintf (script,
+                          " OVERLAY : AT (ALIGN (LOADADDR (.ovl.init) + SIZEOF (.ovl.init), 16))\n {\n") <= 0)
+               goto file_err;
+           }
+         else
+           {
+             if (fprintf (script, " OVERLAY :\n {\n") <= 0)
                goto file_err;
-             if (sec->segment_mark)
-               {
-                 struct call_info *call = find_pasted_call (sec);
-                 while (call != NULL)
-                   {
-                     struct function_info *call_fun = call->fun;
-                     sec = call_fun->sec;
-                     if (fprintf (script, "   %s%c%s (%s)\n",
-                                  (sec->owner->my_archive != NULL
-                                   ? sec->owner->my_archive->filename : ""),
-                                  info->path_separator,
-                                  sec->owner->filename,
-                                  sec->name) <= 0)
-                       goto file_err;
-                     for (call = call_fun->call_list; call; call = call->next)
-                       if (call->is_pasted)
-                         break;
-                   }
-               }
            }
 
-         for (j = base; j < count && ovly_map[j] == ovlynum; j++)
+         while (base < count)
            {
-             asection *sec = ovly_sections[2 * j + 1];
-             if (sec != NULL
-                 && fprintf (script, "   %s%c%s (%s)\n",
-                             (sec->owner->my_archive != NULL
-                              ? sec->owner->my_archive->filename : ""),
-                             info->path_separator,
-                             sec->owner->filename,
-                             sec->name) <= 0)
+             if (fprintf (script, "  .ovly%u {\n", ovlynum) <= 0)
                goto file_err;
 
-             sec = ovly_sections[2 * j];
-             if (sec->segment_mark)
-               {
-                 struct call_info *call = find_pasted_call (sec);
-                 while (call != NULL)
-                   {
-                     struct function_info *call_fun = call->fun;
-                     sec = call_fun->rodata;
-                     if (sec != NULL
-                         && fprintf (script, "   %s%c%s (%s)\n",
-                                     (sec->owner->my_archive != NULL
-                                      ? sec->owner->my_archive->filename : ""),
-                                     info->path_separator,
-                                     sec->owner->filename,
-                                     sec->name) <= 0)
-                       goto file_err;
-                     for (call = call_fun->call_list; call; call = call->next)
-                       if (call->is_pasted)
-                         break;
-                   }
-               }
+             base = print_one_overlay_section (script, base, count, ovlynum,
+                                               ovly_map, ovly_sections, info);
+             if (base == (unsigned) -1)
+               goto file_err;
+
+             if (fprintf (script, "  }\n") <= 0)
+               goto file_err;
+
+             ovlynum += htab->params->num_lines;
+             while (base < count && ovly_map[base] < ovlynum)
+               base++;
            }
 
-         if (fprintf (script, "  }\n") <= 0)
+         if (fprintf (script, " }\n") <= 0)
            goto file_err;
-
-         base = j;
-         ovlynum += htab->params->num_regions;
-         while (base < count && ovly_map[base] < ovlynum)
-           base++;
        }
 
-      if (fprintf (script, " }\n") <= 0)
-       goto file_err;
     }
 
   free (ovly_map);
@@ -3891,17 +4485,21 @@ spu_elf_stack_analysis (struct bfd_link_info *info)
     return FALSE;
 
   htab = spu_hash_table (info);
-  info->callbacks->info (_("Stack size for call graph root nodes.\n"));
-  info->callbacks->minfo (_("\nStack size for functions.  "
-                           "Annotations: '*' max stack, 't' tail call\n"));
+  if (htab->params->stack_analysis)
+    {
+      info->callbacks->info (_("Stack size for call graph root nodes.\n"));
+      info->callbacks->minfo (_("\nStack size for functions.  "
+                               "Annotations: '*' max stack, 't' tail call\n"));
+    }
 
   sum_stack_param.emit_stack_syms = htab->params->emit_stack_syms;
   sum_stack_param.overall_stack = 0;
   if (!for_each_node (sum_stack, info, &sum_stack_param, TRUE))
     return FALSE;
 
-  info->callbacks->info (_("Maximum stack required is 0x%v\n"),
-                        (bfd_vma) sum_stack_param.overall_stack);
+  if (htab->params->stack_analysis)
+    info->callbacks->info (_("Maximum stack required is 0x%v\n"),
+                          (bfd_vma) sum_stack_param.overall_stack);
   return TRUE;
 }
 
@@ -3915,9 +4513,14 @@ spu_elf_final_link (bfd *output_bfd, struct bfd_link_info *info)
   if (htab->params->auto_overlay)
     spu_elf_auto_overlay (info);
 
-  if (htab->params->stack_analysis
+  if ((htab->params->stack_analysis
+       || (htab->params->ovly_flavour == ovly_soft_icache
+          && htab->params->lrlive_analysis))
       && !spu_elf_stack_analysis (info))
-    info->callbacks->einfo ("%X%P: stack analysis error: %E\n");
+    info->callbacks->einfo ("%X%P: stack/lrlive analysis error: %E\n");
+
+  if (!spu_elf_build_stubs (info))
+    info->callbacks->einfo ("%F%P: can not build overlay stubs: %E\n");
 
   return bfd_elf_final_link (output_bfd, info);
 }
@@ -3974,10 +4577,12 @@ spu_elf_relocate_section (bfd *output_bfd,
   bfd_boolean emit_these_relocs = FALSE;
   bfd_boolean is_ea_sym;
   bfd_boolean stubs;
+  unsigned int iovl = 0;
 
   htab = spu_hash_table (info);
   stubs = (htab->stub_sec != NULL
           && maybe_needs_stubs (input_section));
+  iovl = overlay_index (input_section);
   ea = bfd_get_section_by_name (output_bfd, "._ea");
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = (struct elf_link_hash_entry **) (elf_sym_hashes (input_bfd));
@@ -3998,6 +4603,7 @@ spu_elf_relocate_section (bfd *output_bfd,
       bfd_reloc_status_type r;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
+      bfd_boolean overlay_encoded;
       enum _stub_type stub_type;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
@@ -4082,8 +4688,59 @@ spu_elf_relocate_section (bfd *output_bfd,
       is_ea_sym = (ea != NULL
                   && sec != NULL
                   && sec->output_section == ea);
+      overlay_encoded = FALSE;
+
+      /* If this symbol is in an overlay area, we may need to relocate
+        to the overlay stub.  */
+      addend = rel->r_addend;
+      if (stubs
+         && !is_ea_sym
+         && (stub_type = needs_ovl_stub (h, sym, sec, input_section, rel,
+                                         contents, info)) != no_stub)
+       {
+         unsigned int ovl = 0;
+         struct got_entry *g, **head;
+
+         if (stub_type != nonovl_stub)
+           ovl = iovl;
+
+         if (h != NULL)
+           head = &h->got.glist;
+         else
+           head = elf_local_got_ents (input_bfd) + r_symndx;
 
-      if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
+         for (g = *head; g != NULL; g = g->next)
+           if (htab->params->ovly_flavour == ovly_soft_icache
+               ? g->br_addr == (rel->r_offset
+                                + input_section->output_offset
+                                + input_section->output_section->vma)
+               : g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+             break;
+         if (g == NULL)
+           abort ();
+
+         relocation = g->stub_addr;
+         addend = 0;
+       }
+      else
+       {
+         /* For soft icache, encode the overlay index into addresses.  */
+         if (htab->params->ovly_flavour == ovly_soft_icache
+             && !is_ea_sym)
+           {
+             unsigned int ovl = overlay_index (sec);
+             if (ovl != 0)
+               {
+                 unsigned int set_id = (ovl - 1) >> htab->num_lines_log2;
+                 relocation += set_id << 18;
+                 overlay_encoded = set_id != 0;
+               }
+           }
+       }
+
+      if (unresolved_reloc)
+       ;
+      else if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
        {
          if (is_ea_sym)
            {
@@ -4101,8 +4758,7 @@ spu_elf_relocate_section (bfd *output_bfd,
          emit_these_relocs = TRUE;
          continue;
        }
-
-      if (is_ea_sym)
+      else if (is_ea_sym)
        unresolved_reloc = TRUE;
 
       if (unresolved_reloc)
@@ -4117,35 +4773,6 @@ spu_elf_relocate_section (bfd *output_bfd,
          ret = FALSE;
        }
 
-      /* If this symbol is in an overlay area, we may need to relocate
-        to the overlay stub.  */
-      addend = rel->r_addend;
-      if (stubs
-         && (stub_type = needs_ovl_stub (h, sym, sec, input_section, rel,
-                                         contents, info)) != no_stub)
-       {
-         unsigned int ovl = 0;
-         struct got_entry *g, **head;
-
-         if (stub_type != nonovl_stub)
-           ovl = (spu_elf_section_data (input_section->output_section)
-                  ->u.o.ovl_index);
-
-         if (h != NULL)
-           head = &h->got.glist;
-         else
-           head = elf_local_got_ents (input_bfd) + r_symndx;
-
-         for (g = *head; g != NULL; g = g->next)
-           if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
-             break;
-         if (g == NULL)
-           abort ();
-
-         relocation = g->stub_addr;
-         addend = 0;
-       }
-
       r = _bfd_final_link_relocate (howto,
                                    input_bfd,
                                    input_section,
@@ -4159,6 +4786,11 @@ spu_elf_relocate_section (bfd *output_bfd,
          switch (r)
            {
            case bfd_reloc_overflow:
+             /* FIXME: We don't want to warn on most references
+                within an overlay to itself, but this may silence a
+                warning that should be reported.  */
+             if (overlay_encoded && sec == input_section)
+               break;
              if (!((*info->callbacks->reloc_overflow)
                    (info, (h ? &h->root : NULL), sym_name, howto->name,
                     (bfd_vma) 0, input_bfd, input_section, rel->r_offset)))
@@ -4248,7 +4880,9 @@ spu_elf_output_symbol_hook (struct bfd_link_info *info,
       struct got_entry *g;
 
       for (g = h->got.glist; g != NULL; g = g->next)
-       if (g->addend == 0 && g->ovl == 0)
+       if (htab->params->ovly_flavour == ovly_soft_icache
+           ? g->br_addr == g->stub_addr
+           : g->addend == 0 && g->ovl == 0)
          {
            sym->st_shndx = (_bfd_elf_section_from_bfd_section
                             (htab->stub_sec[0]->output_section->owner,
@@ -4409,7 +5043,8 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
            /* Mark this as an overlay header.  */
            phdr[i].p_flags |= PF_OVERLAY;
 
-           if (htab->ovtab != NULL && htab->ovtab->size != 0)
+           if (htab->ovtab != NULL && htab->ovtab->size != 0
+               && htab->params->ovly_flavour != ovly_soft_icache)
              {
                bfd_byte *p = htab->ovtab->contents;
                unsigned int off = o * 16 + 8;
@@ -4418,6 +5053,13 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
                bfd_put_32 (htab->ovtab->owner, phdr[i].p_offset, p + off);
              }
          }
+      /* Soft-icache has its file offset put in .ovl.init.  */
+      if (htab->init != NULL && htab->init->size != 0)
+       {
+         bfd_vma val = elf_section_data (htab->ovl_sec[0])->this_hdr.sh_offset;
+
+         bfd_put_32 (htab->init->owner, val, htab->init->contents + 4);
+       }
     }
 
   /* Round up p_filesz and p_memsz of PT_LOAD segments to multiples
index 442dd5d3b2e830e142427f2cbca90f32b6d88858..0487d5917dfdf0681953e0df7d87f0b803e75080 100644 (file)
@@ -44,6 +44,9 @@ struct spu_elf_params
      non-overlay regions.  */
   unsigned int non_overlay_stubs : 1;
 
+  /* Set if lr liveness analysis should be done.  */
+  unsigned int lrlive_analysis : 1;
+
   /* Set if stack size analysis should be done.  */
   unsigned int stack_analysis : 1;
 
@@ -55,7 +58,9 @@ struct spu_elf_params
   bfd_vma local_store_hi;
 
   /* Control --auto-overlay feature.  */
-  unsigned int num_regions;
+  unsigned int num_lines;
+  unsigned int line_size;
+  unsigned int max_branch;
   unsigned int auto_overlay_fixed;
   unsigned int auto_overlay_reserved;
   int extra_stack_space;
@@ -92,6 +97,7 @@ enum _ovly_flavour
 {
   ovly_compact,
   ovly_normal,
+  ovly_soft_icache,
   ovly_none
 };
 
@@ -108,5 +114,4 @@ extern bfd_boolean spu_elf_open_builtin_lib (bfd **,
 extern bfd_boolean spu_elf_create_sections (struct bfd_link_info *);
 extern bfd_boolean spu_elf_find_overlays (struct bfd_link_info *);
 extern int spu_elf_size_stubs (struct bfd_link_info *);
-extern bfd_boolean spu_elf_build_stubs (struct bfd_link_info *);
 extern asection *spu_elf_check_vma (struct bfd_link_info *);
index e544b76b8ee04cbebb1190232e57c77ba9d93d5d..819600f9c00fe4a5bc2cc1b7b0b4914fe433dfb5 100644 (file)
@@ -1,3 +1,12 @@
+2009-01-12  Alan Modra  <amodra@bigpond.net.au>
+
+       * config/tc-spu.c (md_pseudo_table): Add "brinfo".
+       (brinfo): New var.
+       (md_assemble): Poke brinfo into branch instructions.
+       (spu_brinfo): New function.
+       (md_apply_fix): Don't assume insn fields start off at zero, mask
+       them to remove possible brinfo.
+
 2009-01-10  H.J. Lu  <hongjiu.lu@intel.com>
 
        * doc/c-i386.texi: Reformat.
index e159e34f0bc7818c825bef6f4a1e422239207cb6..0b40a562f1f366dab54df4d1bc304213052dbe44 100644 (file)
@@ -1,6 +1,6 @@
 /* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU)
 
-   Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+   Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -53,6 +53,7 @@ static const char *get_reg (const char *param, struct spu_insn *insn, int arg,
                            int accept_expr);
 static int calcop (struct spu_opcode *format, const char *param,
                   struct spu_insn *insn);
+static void spu_brinfo (int);
 static void spu_cons (int);
 
 extern char *myname;
@@ -82,6 +83,7 @@ const char FLT_CHARS[] = "dDfF";
 const pseudo_typeS md_pseudo_table[] =
 {
   {"align", s_align_ptwo, 4},
+  {"brinfo", spu_brinfo, 0},
   {"bss", s_lcomm_bytes, 1},
   {"def", s_set, 0},
   {"dfloat", float_cons, 'd'},
@@ -104,6 +106,9 @@ const pseudo_typeS md_pseudo_table[] =
   {0,0,0}
 };
 
+/* Bits plugged into branch instruction offset field.  */
+unsigned int brinfo;
+
 void
 md_begin (void)
 {
@@ -342,6 +347,16 @@ md_assemble (char *op)
       as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d);
     }
 
+  if (brinfo != 0
+      && (insn.tag <= M_BRASL
+         || (insn.tag >= M_BRZ && insn.tag <= M_BRHNZ))
+      && (insn.opcode & 0x7ff80) == 0
+      && (insn.reloc_arg[0] == A_R18
+         || insn.reloc_arg[0] == A_S18
+         || insn.reloc_arg[1] == A_R18
+         || insn.reloc_arg[1] == A_S18))
+    insn.opcode |= brinfo << 7;
+
   /* grow the current frag and plop in the opcode */
 
   thisfrag = frag_more (4);
@@ -370,6 +385,9 @@ md_assemble (char *op)
        fixP->tc_fix_data.insn_tag = insn.tag;
       }
   dwarf2_emit_insn (4);
+
+  /* .brinfo lasts exactly one instruction.  */
+  brinfo = 0;
 }
 
 static int
@@ -752,6 +770,39 @@ md_create_long_jump (char *ptr,
 }
 #endif
 
+/* Handle .brinfo <priority>,<lrlive>.  */
+static void
+spu_brinfo (int ignore ATTRIBUTE_UNUSED)
+{
+  addressT priority;
+  addressT lrlive;
+
+  priority = get_absolute_expression ();
+  SKIP_WHITESPACE ();
+
+  lrlive = 0;
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      lrlive = get_absolute_expression ();
+    }
+
+  if (priority > 0x1fff)
+    {
+      as_bad (_("invalid priority '%lu'"), (unsigned long) priority);
+      priority = 0;
+    }
+
+  if (lrlive > 7)
+    {
+      as_bad (_("invalid lrlive '%lu'"), (unsigned long) lrlive);
+      lrlive = 0;
+    }
+
+  brinfo = (lrlive << 13) | priority;
+  demand_empty_rest_of_line ();
+}
+
 /* Support @ppu on symbols referenced in .int/.long/.word/.quad.  */
 static void
 spu_cons (int nbytes)
@@ -898,6 +949,7 @@ void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   unsigned int res;
+  unsigned int mask;
   valueT val = *valP;
   char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
 
@@ -944,6 +996,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     {
       fixP->fx_done = 1;
       res = 0;
+      mask = 0;
       if (fixP->tc_fix_data.arg_format > A_P)
        {
          int hi = arg_encode[fixP->tc_fix_data.arg_format].hi;
@@ -955,84 +1008,94 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        }
 
       switch (fixP->fx_r_type)
-        {
-        case BFD_RELOC_8:
+       {
+       case BFD_RELOC_8:
          md_number_to_chars (place, val, 1);
          return;
 
-        case BFD_RELOC_16:
+       case BFD_RELOC_16:
          md_number_to_chars (place, val, 2);
          return;
 
-        case BFD_RELOC_32:
+       case BFD_RELOC_32:
        case BFD_RELOC_32_PCREL:
          md_number_to_chars (place, val, 4);
          return;
 
-        case BFD_RELOC_64:
+       case BFD_RELOC_64:
          md_number_to_chars (place, val, 8);
          return;
 
-        case BFD_RELOC_SPU_IMM7:
-          res = (val & 0x7f) << 14;
-          break;
+       case BFD_RELOC_SPU_IMM7:
+         res = val << 14;
+         mask = 0x7f << 14;
+         break;
 
-        case BFD_RELOC_SPU_IMM8:
-          res = (val & 0xff) << 14;
-          break;
+       case BFD_RELOC_SPU_IMM8:
+         res = val << 14;
+         mask = 0xff << 14;
+         break;
 
-        case BFD_RELOC_SPU_IMM10:
-          res = (val & 0x3ff) << 14;
-          break;
+       case BFD_RELOC_SPU_IMM10:
+         res = val << 14;
+         mask = 0x3ff << 14;
+         break;
 
-        case BFD_RELOC_SPU_IMM10W:
-          res = (val & 0x3ff0) << 10;
-          break;
+       case BFD_RELOC_SPU_IMM10W:
+         res = val << 10;
+         mask = 0x3ff0 << 10;
+         break;
 
-        case BFD_RELOC_SPU_IMM16:
-          res = (val & 0xffff) << 7;
-          break;
+       case BFD_RELOC_SPU_IMM16:
+         res = val << 7;
+         mask = 0xffff << 7;
+         break;
 
-        case BFD_RELOC_SPU_IMM16W:
-          res = (val & 0x3fffc) << 5;
-          break;
+       case BFD_RELOC_SPU_IMM16W:
+         res = val << 5;
+         mask = 0x3fffc << 5;
+         break;
 
-        case BFD_RELOC_SPU_IMM18:
-          res = (val & 0x3ffff) << 7;
-          break;
+       case BFD_RELOC_SPU_IMM18:
+         res = val << 7;
+         mask = 0x3ffff << 7;
+         break;
 
-        case BFD_RELOC_SPU_PCREL9a:
-          res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
-          break;
+       case BFD_RELOC_SPU_PCREL9a:
+         res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
+         mask = (0x1fc >> 2) | (0x600 << 14);
+         break;
 
-        case BFD_RELOC_SPU_PCREL9b:
-          res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
-          break;
+       case BFD_RELOC_SPU_PCREL9b:
+         res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
+         mask = (0x1fc >> 2) | (0x600 << 5);
+         break;
 
-        case BFD_RELOC_SPU_PCREL16:
-          res = (val & 0x3fffc) << 5;
-          break;
+       case BFD_RELOC_SPU_PCREL16:
+         res = val << 5;
+         mask = 0x3fffc << 5;
+         break;
 
        case BFD_RELOC_SPU_HI16:
-         res = (val >> 9) & 0x7fff80;
+         res = val >> 9;
+         mask = 0xffff << 7;
          break;
 
        case BFD_RELOC_SPU_LO16:
-         res = (val << 7) & 0x7fff80;
+         res = val << 7;
+         mask = 0xffff << 7;
          break;
 
-        default:
-          as_bad_where (fixP->fx_file, fixP->fx_line,
-                        _("reloc %d not supported by object file format"),
-                        (int) fixP->fx_r_type);
-        }
-
-      if (res != 0)
-        {
-          place[0] |= (res >> 24) & 0xff;
-          place[1] |= (res >> 16) & 0xff;
-          place[2] |= (res >> 8) & 0xff;
-          place[3] |= (res) & 0xff;
-        }
+       default:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("reloc %d not supported by object file format"),
+                       (int) fixP->fx_r_type);
+       }
+
+      res &= mask;
+      place[0] = (place[0] & (~mask >> 24)) | ((res >> 24) & 0xff);
+      place[1] = (place[1] & (~mask >> 16)) | ((res >> 16) & 0xff);
+      place[2] = (place[2] & (~mask >> 8)) | ((res >> 8) & 0xff);
+      place[3] = (place[3] & ~mask) | (res & 0xff);
     }
 }
index 747633c846c4980e9bb6acb5ff4975a322bb678d..30ed0399e0ebdd5e241969242a52f284bf59c341 100644 (file)
@@ -1,3 +1,26 @@
+2009-01-12  Alan Modra  <amodra@bigpond.net.au>
+
+       * emultempl/spuelf.em (params): Init new fields.
+       (num_lines_set, line_size_set, icache_mgr, icache_mgr_stream): New vars.
+       (spu_place_special_section): Adjust placement for soft-icache.  Pad
+       soft-icache section to a fixed size.  Clear addr_tree.
+       (spu_elf_load_ovl_mgr): Support soft-icache.  Map overlay manager
+       sections a little more intelligently.
+       (gld${EMULATION_NAME}_finish): Don't call spu_elf_build_stubs.
+       (OPTION_SPU_NUM_LINES): Rename from OPTION_SPU_NUM_REGIONS.
+       (OPTION_SPU_SOFT_ICACHE, OPTION_SPU_LINE_SIZE): Define.
+       (OPTION_SPU_LRLIVE): Define.
+       (PARSE_AND_LIST_LONGOPTS): Add new soft-icache options.
+       (PARSE_AND_LIST_OPTIONS): Likewise.
+       (PARSE_AND_LIST_ARGS_CASES): Handle them.
+       * emultempl/spu_icache.S: Dummy file.
+       * emultempl/spu_icache.o_c: Regenerate.
+       * Makefile.am (eelf32_spu.c): Depend on spu_icache.o_c.
+       (spu_icache.o_c): Add rule to build.
+       (CLEANFILES): Zap temp files.
+       (EXTRA_DIST): Add spu_icache.o_c.
+       * Makefile.in: Regenerate.
+
 2009-01-08  Kai Tietz  <kai.tietz@onevision.com>
 
        * pe.em (OPTION_USE_NUL_PREFIXED_IMPORT_TABLES): New.
index 3423dfc9bdb91f9a163937cb0bc2e67a5c5d241a..83d92ec20d86f1852d3bacf0c4921eb1072ca4f2 100644 (file)
@@ -753,7 +753,7 @@ eelf32_sparc_vxworks.c: $(srcdir)/emulparams/elf32_sparc_vxworks.sh \
   $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_sparc_vxworks "$(tdir_elf32_sparc_vxworks)"
 eelf32_spu.c: $(srcdir)/emulparams/elf32_spu.sh $(srcdir)/emultempl/spuelf.em \
-  $(srcdir)/emultempl/spu_ovl.o_c \
+  $(srcdir)/emultempl/spu_ovl.o_c $(srcdir)/emultempl/spu_icache.o_c \
   ldemul-list.h \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_spu "$(tdir_elf32_spu)"
@@ -764,6 +764,13 @@ $(srcdir)/emultempl/spu_ovl.o_c: @MAINT@ $(srcdir)/emultempl/spu_ovl.S
          ../gas/as-new -o spu_ovl.o spu_ovl.s; \
          ../binutils/bin2c <spu_ovl.o >$@; \
        fi
+$(srcdir)/emultempl/spu_icache.o_c: @MAINT@ $(srcdir)/emultempl/spu_icache.S
+       if ../gas/as-new --version \
+               | grep 'target.*spu' >/dev/null 2>/dev/null; then \
+         cpp -DOVLY_IRQ_SAVE $(srcdir)/emultempl/spu_icache.S spu_icache.s; \
+         ../gas/as-new -o spu_icache.o spu_icache.s; \
+         ../binutils/bin2c <spu_icache.o >$@; \
+       fi
 eelf32_i860.c: $(srcdir)/emulparams/elf32_i860.sh \
   $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_i860 "$(tdir_elf32_i860)"
@@ -1844,7 +1851,7 @@ MOSTLYCLEANFILES = $(STAGESTUFF) ld1$(EXEEXT) ld2$(EXEEXT) ld3$(EXEEXT) \
        ldemul-list.h crtbegin.o crtend.o ld.log ld.sum
 mostlyclean-local:
        -rm -rf tmpdir
-CLEANFILES = dep.sed DEP DEPA DEP1 DEP2 spu_ovl.s spu_ovl.o
+CLEANFILES = dep.sed DEP DEPA DEP1 DEP2 spu_ovl.s spu_ovl.o spu_icache.s spu_icache.o
 
 .PHONY: install-html install-html-am install-html-recursive
 
@@ -1924,7 +1931,7 @@ install-data-local: install-info
 # Stuff that should be included in a distribution.  The diststuff
 # target is run by the taz target in ../Makefile.in.
 EXTRA_DIST = ldgram.c ldgram.h ldlex.c emultempl/spu_ovl.o_c \
-           deffilep.c deffilep.h $(man_MANS)
+            emultempl/spu_icache.o_c deffilep.c deffilep.h $(man_MANS)
 diststuff: info $(EXTRA_DIST)
 all: info ld.1
 
index 6f3ae8b8837c60d1d54428d11901db4eafb59c00..3da3450c6523358474c61c62f33bee209ad836eb 100644 (file)
@@ -739,13 +739,13 @@ CONFIG_STATUS_DEPENDENCIES = $(srcdir)/configure.host $(srcdir)/configure.tgt \
 MOSTLYCLEANFILES = $(STAGESTUFF) ld1$(EXEEXT) ld2$(EXEEXT) ld3$(EXEEXT) \
        ldemul-list.h crtbegin.o crtend.o ld.log ld.sum
 
-CLEANFILES = dep.sed DEP DEPA DEP1 DEP2 spu_ovl.s spu_ovl.o
+CLEANFILES = dep.sed DEP DEPA DEP1 DEP2 spu_ovl.s spu_ovl.o spu_icache.s spu_icache.o
 html__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
 
 # Stuff that should be included in a distribution.  The diststuff
 # target is run by the taz target in ../Makefile.in.
 EXTRA_DIST = ldgram.c ldgram.h ldlex.c emultempl/spu_ovl.o_c \
-           deffilep.c deffilep.h $(man_MANS)
+            emultempl/spu_icache.o_c deffilep.c deffilep.h $(man_MANS)
 
 DISTCLEANFILES = tdirs site.exp site.bak stringify.sed $(am__append_1)
 all: config.h
@@ -1597,7 +1597,7 @@ eelf32_sparc_vxworks.c: $(srcdir)/emulparams/elf32_sparc_vxworks.sh \
   $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_sparc_vxworks "$(tdir_elf32_sparc_vxworks)"
 eelf32_spu.c: $(srcdir)/emulparams/elf32_spu.sh $(srcdir)/emultempl/spuelf.em \
-  $(srcdir)/emultempl/spu_ovl.o_c \
+  $(srcdir)/emultempl/spu_ovl.o_c $(srcdir)/emultempl/spu_icache.o_c \
   ldemul-list.h \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_spu "$(tdir_elf32_spu)"
@@ -1608,6 +1608,13 @@ $(srcdir)/emultempl/spu_ovl.o_c: @MAINT@ $(srcdir)/emultempl/spu_ovl.S
          ../gas/as-new -o spu_ovl.o spu_ovl.s; \
          ../binutils/bin2c <spu_ovl.o >$@; \
        fi
+$(srcdir)/emultempl/spu_icache.o_c: @MAINT@ $(srcdir)/emultempl/spu_icache.S
+       if ../gas/as-new --version \
+               | grep 'target.*spu' >/dev/null 2>/dev/null; then \
+         cpp -DOVLY_IRQ_SAVE $(srcdir)/emultempl/spu_icache.S spu_icache.s; \
+         ../gas/as-new -o spu_icache.o spu_icache.s; \
+         ../binutils/bin2c <spu_icache.o >$@; \
+       fi
 eelf32_i860.c: $(srcdir)/emulparams/elf32_i860.sh \
   $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32_i860 "$(tdir_elf32_i860)"
diff --git a/ld/emultempl/spu_icache.S b/ld/emultempl/spu_icache.S
new file mode 100644 (file)
index 0000000..be7d523
--- /dev/null
@@ -0,0 +1,4 @@
+       .text
+       .global __icache_br_handler
+__icache_br_handler:
+       stop
diff --git a/ld/emultempl/spu_icache.o_c b/ld/emultempl/spu_icache.o_c
new file mode 100644 (file)
index 0000000..e69de29
index bfabb8b72ee68346f5a2148df38d5e2d52a43d4c..f72690d1783a501775ea0ad1c191f645df02ae88 100644 (file)
@@ -1,5 +1,5 @@
 # This shell script emits a C file. -*- C -*-
-#   Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+#   Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 #
 # This file is part of the GNU Binutils.
 #
@@ -37,11 +37,13 @@ static struct spu_elf_params params =
   &spu_elf_load_ovl_mgr,
   &spu_elf_open_overlay_script,
   &spu_elf_relink,
-  0, ovly_normal, 0, 0, 0, 0,
+  0, ovly_normal, 0, 0, 0, 0, 0,
   0, 0x3ffff,
-  1, 0, 0, 2000
+  1, 0, 16, 0, 0, 2000
 };
   
+static unsigned int num_lines_set = 0;
+static unsigned int line_size_set = 0;
 static char *auto_overlay_file = 0;
 int my_argc;
 char **my_argv;
@@ -52,7 +54,20 @@ EOF
 if ! cat ${srcdir}/emultempl/spu_ovl.o_c >> e${EMULATION_NAME}.c
 then
   echo >&2 "Missing ${srcdir}/emultempl/spu_ovl.o_c"
-  echo >&2 "You must build gas/as-new with --target=spu to build spu_ovl.o"
+  echo >&2 "You must build gas/as-new with --target=spu"
+  exit 1
+fi
+
+fragment <<EOF
+};
+
+static const char icache_mgr[] = {
+EOF
+
+if ! cat ${srcdir}/emultempl/spu_icache.o_c >> e${EMULATION_NAME}.c
+then
+  echo >&2 "Missing ${srcdir}/emultempl/spu_icache.o_c"
+  echo >&2 "You must build gas/as-new with --target=spu"
   exit 1
 fi
 
@@ -64,6 +79,11 @@ static const struct _ovl_stream ovl_mgr_stream = {
   ovl_mgr + sizeof (ovl_mgr)
 };
 
+static const struct _ovl_stream icache_mgr_stream = {
+  icache_mgr,
+  icache_mgr + sizeof (icache_mgr)
+};
+
 
 static int
 is_spu_target (void)
@@ -96,7 +116,8 @@ spu_after_open (void)
 }
 
 /* If O is NULL, add section S at the end of output section OUTPUT_NAME.
-   If O is not NULL, add section S at the beginning of output section O.
+   If O is not NULL, add section S at the beginning of output section O,
+   except for soft-icache which adds to the end.
 
    Really, we should be duplicating ldlang.c map_input_to_output_sections
    logic here, ie. using the linker script to find where the section
@@ -115,8 +136,12 @@ spu_place_special_section (asection *s, asection *o, const char *output_name)
     output_name = o->name;
   os = lang_output_section_find (output_name);
   if (os == NULL)
-    gld${EMULATION_NAME}_place_orphan (s, output_name, 0);
-  else if (o != NULL && os->children.head != NULL)
+    {
+      os = gld${EMULATION_NAME}_place_orphan (s, output_name, 0);
+      os->addr_tree = NULL;
+    }
+  else if (params.ovly_flavour != ovly_soft_icache
+          && o != NULL && os->children.head != NULL)
     {
       lang_statement_list_type add;
 
@@ -126,7 +151,21 @@ spu_place_special_section (asection *s, asection *o, const char *output_name)
       os->children.head = add.head;
     }
   else
-    lang_add_section (&os->children, s, os);
+    {
+      if (params.ovly_flavour == ovly_soft_icache && o != NULL)
+       {
+         /* Pad this stub section so that it finishes at the
+            end of the icache line.  */
+         etree_type *e_size;
+         lang_statement_list_type *save = stat_ptr;
+
+         stat_ptr = &os->children;
+         e_size = exp_intop (params.line_size - s->size);
+         lang_add_assignment (exp_assop ('=', ".", e_size));
+         stat_ptr = save;
+       }
+      lang_add_section (&os->children, s, os);
+    }
 
   s->output_section->size += s->size;
 }
@@ -137,10 +176,19 @@ static bfd_size_type
 spu_elf_load_ovl_mgr (void)
 {
   struct elf_link_hash_entry *h;
+  const char *ovly_mgr_entry;
+  const struct _ovl_stream *mgr_stream;
   bfd_size_type total = 0;
 
+  ovly_mgr_entry = "__ovly_load";
+  mgr_stream = &ovl_mgr_stream;
+  if (params.ovly_flavour == ovly_soft_icache)
+    {
+      ovly_mgr_entry = "__icache_br_handler";
+      mgr_stream = &icache_mgr_stream;
+    }
   h = elf_link_hash_lookup (elf_hash_table (&link_info),
-                           "__ovly_load", FALSE, FALSE, FALSE);
+                           ovly_mgr_entry, FALSE, FALSE, FALSE);
 
   if (h != NULL
       && (h->root.type == bfd_link_hash_defined
@@ -149,7 +197,7 @@ spu_elf_load_ovl_mgr (void)
     {
       /* User supplied __ovly_load.  */
     }
-  else if (ovl_mgr_stream.start == ovl_mgr_stream.end)
+  else if (mgr_stream->start == mgr_stream->end)
     einfo ("%F%P: no built-in overlay manager\n");
   else
     {
@@ -159,7 +207,7 @@ spu_elf_load_ovl_mgr (void)
                                    lang_input_file_is_file_enum,
                                    NULL);
 
-      if (!spu_elf_open_builtin_lib (&ovl_is->the_bfd, &ovl_mgr_stream))
+      if (!spu_elf_open_builtin_lib (&ovl_is->the_bfd, mgr_stream))
        einfo ("%X%P: can not open built-in overlay manager: %E\n");
       else
        {
@@ -168,13 +216,38 @@ spu_elf_load_ovl_mgr (void)
          if (!load_symbols (ovl_is, NULL))
            einfo ("%X%P: can not load built-in overlay manager: %E\n");
 
-         /* Map overlay manager sections to output sections.  */
+         /* Map overlay manager sections to output sections.
+            First try for a matching output section name, if that
+            fails then try mapping .abc.xyz to .abc, otherwise map
+            to .text.  */
          for (in = ovl_is->the_bfd->sections; in != NULL; in = in->next)
            if ((in->flags & (SEC_ALLOC | SEC_LOAD))
                == (SEC_ALLOC | SEC_LOAD))
              {
-               total += in->size;
-               spu_place_special_section (in, NULL, ".text");
+               const char *oname = in->name;
+               if (strncmp (in->name, ".ovl.init", 9) != 0)
+                 {
+                   total += in->size;
+                   if (!lang_output_section_find (oname))
+                     {
+                       lang_output_section_statement_type *os = NULL;
+                       char *p = strchr (oname + 1, '.');
+                       if (p != NULL)
+                         {
+                           size_t len = p - oname;
+                           p = memcpy (xmalloc (len + 1), oname, len);
+                           p[len] = '\0';
+                           os = lang_output_section_find (p);
+                           free (p);
+                         }
+                       if (os != NULL)
+                         oname = os->name;
+                       else
+                         oname = ".text";
+                     }
+                 }
+
+               spu_place_special_section (in, NULL, oname);
              }
        }
     }
@@ -338,9 +411,6 @@ gld${EMULATION_NAME}_finish (void)
        }
       else if (params.auto_overlay)
        einfo ("%P: --auto-overlay ignored with zero local store range\n");
-
-      if (!spu_elf_build_stubs (&link_info))
-       einfo ("%F%P: can not build overlay stubs: %E\n");
     }
 
   finish_default ();
@@ -520,8 +590,11 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_SPU_AUTO_OVERLAY                (OPTION_SPU_STACK_SYMS + 1)
 #define OPTION_SPU_AUTO_RELINK         (OPTION_SPU_AUTO_OVERLAY + 1)
 #define OPTION_SPU_OVERLAY_RODATA      (OPTION_SPU_AUTO_RELINK + 1)
-#define OPTION_SPU_NUM_REGIONS         (OPTION_SPU_OVERLAY_RODATA + 1)
-#define OPTION_SPU_FIXED_SPACE         (OPTION_SPU_NUM_REGIONS + 1)
+#define OPTION_SPU_SOFT_ICACHE         (OPTION_SPU_OVERLAY_RODATA + 1)
+#define OPTION_SPU_LINE_SIZE           (OPTION_SPU_SOFT_ICACHE + 1)
+#define OPTION_SPU_NUM_LINES           (OPTION_SPU_LINE_SIZE + 1)
+#define OPTION_SPU_LRLIVE              (OPTION_SPU_NUM_LINES + 1)
+#define OPTION_SPU_FIXED_SPACE         (OPTION_SPU_LRLIVE + 1)
 #define OPTION_SPU_RESERVED_SPACE      (OPTION_SPU_FIXED_SPACE + 1)
 #define OPTION_SPU_EXTRA_STACK         (OPTION_SPU_RESERVED_SPACE + 1)
 #define OPTION_SPU_NO_AUTO_OVERLAY     (OPTION_SPU_EXTRA_STACK + 1)
@@ -529,6 +602,10 @@ PARSE_AND_LIST_PROLOGUE='
 
 PARSE_AND_LIST_LONGOPTS='
   { "plugin", no_argument, NULL, OPTION_SPU_PLUGIN },
+  { "soft-icache", no_argument, NULL, OPTION_SPU_SOFT_ICACHE },
+  { "lrlive-analysis", no_argument, NULL, OPTION_SPU_LRLIVE },
+  { "num-lines", required_argument, NULL, OPTION_SPU_NUM_LINES },
+  { "line-size", required_argument, NULL, OPTION_SPU_LINE_SIZE },
   { "no-overlays", no_argument, NULL, OPTION_SPU_NO_OVERLAYS },
   { "emit-stub-syms", no_argument, NULL, OPTION_SPU_STUB_SYMS },
   { "extra-overlay-stubs", no_argument, NULL, OPTION_SPU_NON_OVERLAY_STUBS },
@@ -538,7 +615,8 @@ PARSE_AND_LIST_LONGOPTS='
   { "auto-overlay", optional_argument, NULL, OPTION_SPU_AUTO_OVERLAY },
   { "auto-relink", no_argument, NULL, OPTION_SPU_AUTO_RELINK },
   { "overlay-rodata", no_argument, NULL, OPTION_SPU_OVERLAY_RODATA },
-  { "num-regions", required_argument, NULL, OPTION_SPU_NUM_REGIONS },
+  { "num-regions", required_argument, NULL, OPTION_SPU_NUM_LINES },
+  { "region-size", required_argument, NULL, OPTION_SPU_LINE_SIZE },
   { "fixed-space", required_argument, NULL, OPTION_SPU_FIXED_SPACE },
   { "reserved-space", required_argument, NULL, OPTION_SPU_RESERVED_SPACE },
   { "extra-stack-space", required_argument, NULL, OPTION_SPU_EXTRA_STACK },
@@ -560,11 +638,16 @@ PARSE_AND_LIST_OPTIONS='
   --overlay-rodata            Place read-only data with associated function\n\
                               code in overlays.\n\
   --num-regions               Number of overlay buffers (default 1).\n\
+  --region-size               Size of overlay buffers (default 0, auto).\n\
   --fixed-space=bytes         Local store for non-overlay code and data.\n\
   --reserved-space=bytes      Local store for stack and heap.  If not specified\n\
                               ld will estimate stack size and assume no heap.\n\
   --extra-stack-space=bytes   Space for negative sp access (default 2000) if\n\
-                              --reserved-space not given.\n"
+                              --reserved-space not given.\n\
+  --soft-icache               Generate software icache overlays.\n\
+  --num-lines                 Number of soft-icache lines (default 32).\n\
+  --line-size                 Size of soft-icache lines (default 1k).\n\
+  --lrlive-analysis           Scan function prologue for lr liveness.\n"
                   ));
 '
 
@@ -624,13 +707,47 @@ PARSE_AND_LIST_ARGS_CASES='
       params.auto_overlay |= 4;
       break;
 
-    case OPTION_SPU_NUM_REGIONS:
+    case OPTION_SPU_SOFT_ICACHE:
+      params.ovly_flavour = ovly_soft_icache;
+      if (!num_lines_set)
+       params.num_lines = 32;
+      else if ((params.num_lines & -params.num_lines) != params.num_lines)
+       einfo (_("%P%F: invalid --num-lines/--num-regions `%u'\''\n"),
+              params.num_lines);
+      if (!line_size_set)
+       params.line_size = 1024;
+      else if ((params.line_size & -params.line_size) != params.line_size)
+       einfo (_("%P%F: invalid --line-size/--region-size `%u'\''\n"),
+              params.line_size);
+      break;
+
+    case OPTION_SPU_LRLIVE:
+      params.lrlive_analysis = 1;
+      break;
+
+    case OPTION_SPU_NUM_LINES:
+      {
+       char *end;
+       params.num_lines = strtoul (optarg, &end, 0);
+       num_lines_set = 1;
+       if (*end == 0
+           && (params.ovly_flavour != ovly_soft_icache
+               || (params.num_lines & -params.num_lines) == params.num_lines))
+         break;
+       einfo (_("%P%F: invalid --num-lines/--num-regions `%s'\''\n"), optarg);
+      }
+      break;
+
+    case OPTION_SPU_LINE_SIZE:
       {
        char *end;
-       params.num_regions = strtoul (optarg, &end, 0);
-       if (*end == 0)
+       params.line_size = strtoul (optarg, &end, 0);
+       line_size_set = 1;
+       if (*end == 0
+           && (params.ovly_flavour != ovly_soft_icache
+               || (params.line_size & -params.line_size) == params.line_size))
          break;
-       einfo (_("%P%F: invalid --num-regions `%s'\''\n"), optarg);
+       einfo (_("%P%F: invalid --line-size/--region-size `%s'\''\n"), optarg);
       }
       break;
 
index 1c8b545660c2699a7358be62d9ea87050736916b..1ba7fcd727b0921057ea51ab401d80bd9e01537b 100644 (file)
@@ -1,3 +1,8 @@
+2009-01-12  Alan Modra  <amodra@bigpond.net.au>
+
+       * ld-spu/ovl.d: Allow for absolute branches in stubs.
+       * ld-spu/ovl2.d: Likewise.
+
 2009-01-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * ld-elf/linkoncerdiff.d, ld-elf/linkoncerdiff1.s,
index 9d34a117087b5d976f167799feee32805d1e9ada..42d00b7f6c0eb1ab0265f39591671575ca88969b 100644 (file)
@@ -29,64 +29,64 @@ Disassembly of section \.text:
 .*     bi      \$0
 
 #00000130 <00000000\.ovl_call\.f1_a1>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 04 04 00.*
 #
 #00000138 <00000000\.ovl_call\.f2_a1>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 04 04 04.*
 #
 #00000140 <00000000\.ovl_call\.f1_a2>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 08 04 00.*
 #
 #00000148 <00000000\.ovl_call\.f2_a2>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 08 04 24.*
 #
 #00000150 <00000000\.ovl_call\.f4_a1>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 04 04 10.*
 #
 #00000158 <00000000.ovl_call.14:8>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 08 04 34.*
 
 00000130 <00000000\.ovl_call\.f1_a1>:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1024       # 400
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000140 <00000000\.ovl_call\.f2_a1>:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1028       # 404
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000150 <00000000.ovl_call.f1_a2>:
 .*     ila     \$78,2
 .*     lnop
 .*     ila     \$79,1024       # 400
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000160 <00000000\.ovl_call\.f2_a2>:
 .*     ila     \$78,2
 .*     lnop
 .*     ila     \$79,1060       # 424
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000170 <00000000\.ovl_call\.f4_a1>:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1040       # 410
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000180 <00000000.ovl_call.14:8>:
 .*     ila     \$78,2
 .*     lnop
 .*     ila     \$79,1076       # 434
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 #...
 [0-9a-f]+ <__ovly_return>:
index f9dca195485d9c20f2fd708197e06899faec4aab..b50914614b4724b4db55e7918eda6cd2bc9b9e0a 100644 (file)
@@ -24,40 +24,40 @@ Disassembly of section \.text:
        \.\.\.
 
 #00000118 <00000000\.ovl_call.f1_a1>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 04 04 00.*
 #
 #00000120 <00000000\.ovl_call.setjmp>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 00 01 0c.*
 #
 #00000128 <_SPUEAR_f1_a2>:
-#.*    brsl    \$75,.* <__ovly_load>.*
+#.*    bra?sl  \$75,.* <__ovly_load>.*
 #.*00 08 04 00.*
 
 00000120 <00000000\.ovl_call.f1_a1>:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1040       # 410
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000130 <00000000\.ovl_call.setjmp>:
 .*     ila     \$78,0
 .*     lnop
 .*     ila     \$79,268        # 10c
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000140 <00000000\.ovl_call\.13:5>:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1044       # 414
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000150 <_SPUEAR_f1_a2>:
 .*     ila     \$78,2
 .*     lnop
 .*     ila     \$79,1040       # 410
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 #...
 Disassembly of section \.ov_a1:
@@ -66,7 +66,7 @@ Disassembly of section \.ov_a1:
 .*     ila     \$78,2
 .*     lnop
 .*     ila     \$79,1044       # 414
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000410 <f1_a1>:
 .*     bi      \$0
@@ -83,7 +83,7 @@ Disassembly of section \.ov_a2:
 .*     ila     \$78,1
 .*     lnop
 .*     ila     \$79,1056       # 420
-.*     br      .* <__ovly_load>.*
+.*     bra?    .* <__ovly_load>.*
 
 00000410 <f1_a2>:
 .*     br      .* <longjmp>.*