From e0001a05d2e4967ee86f4468cdc4fafea66b92d1 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 1 Apr 2003 15:50:31 +0000 Subject: Add Xtensa port --- ld/ChangeLog | 13 + ld/Makefile.am | 6 + ld/Makefile.in | 6 + ld/configure.tgt | 1 + ld/emulparams/elf32xtensa.sh | 32 + ld/emulparams/xtensa-config.sh | 8 + ld/emultempl/xtensaelf.em | 1586 ++++++++++++++++++++++++++++++++++ ld/gen-doc.texi | 1 + ld/ld.texinfo | 77 +- ld/scripttempl/elfxtensa.sc | 397 +++++++++ ld/testsuite/ChangeLog | 15 + ld/testsuite/ld-elf/merge.d | 2 +- ld/testsuite/ld-scripts/crossref.exp | 7 + ld/testsuite/ld-srec/srec.exp | 5 + ld/testsuite/ld-xtensa/coalesce.exp | 93 ++ ld/testsuite/ld-xtensa/coalesce.t | 6 + ld/testsuite/ld-xtensa/coalesce1.s | 15 + ld/testsuite/ld-xtensa/coalesce2.s | 9 + ld/testsuite/ld-xtensa/lcall.exp | 107 +++ ld/testsuite/ld-xtensa/lcall.t | 6 + ld/testsuite/ld-xtensa/lcall1.s | 12 + ld/testsuite/ld-xtensa/lcall2.s | 5 + 22 files changed, 2407 insertions(+), 2 deletions(-) create mode 100644 ld/emulparams/elf32xtensa.sh create mode 100644 ld/emulparams/xtensa-config.sh create mode 100644 ld/emultempl/xtensaelf.em create mode 100644 ld/scripttempl/elfxtensa.sc create mode 100644 ld/testsuite/ld-xtensa/coalesce.exp create mode 100644 ld/testsuite/ld-xtensa/coalesce.t create mode 100644 ld/testsuite/ld-xtensa/coalesce1.s create mode 100644 ld/testsuite/ld-xtensa/coalesce2.s create mode 100644 ld/testsuite/ld-xtensa/lcall.exp create mode 100644 ld/testsuite/ld-xtensa/lcall.t create mode 100644 ld/testsuite/ld-xtensa/lcall1.s create mode 100644 ld/testsuite/ld-xtensa/lcall2.s (limited to 'ld') diff --git a/ld/ChangeLog b/ld/ChangeLog index 9836da4..2ea63d6 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,16 @@ +2003-04-01 Bob Wilson + + * Makefile.am (ALL_EMULATIONS): Add eelf32xtensa.o. + (eelf32xtensa.c): New target. + * Makefile.in: Regenerate. + * configure.tgt: Handle xtensa-*-*. + * gen-doc.texi: Set XTENSA variable. + * ld.texinfo: Set XTENSA variable. Add new Xtensa node. + * emulparams/elf32xtensa.sh: New file. + * emulparams/xtensa-config.sh: Likewise. + * emultempl/xtensaelf.em: Likewise. + * scripttempl/elfxtensa.sc: Likewise. + 2003-04-01 Jakub Jelinek * configure.tgt (powerpc*-*-linux*): Add elf32ppc to ppc64 diff --git a/ld/Makefile.am b/ld/Makefile.am index 41af307..6e35d38 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -183,6 +183,7 @@ ALL_EMULATIONS = \ eelf32ppcwindiss.o \ eelf32vax.o \ eelf32xstormy16.o \ + eelf32xtensa.o \ eelf_i386.o \ eelf_i386_be.o \ eelf_i386_chaos.o \ @@ -589,6 +590,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \ eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32vax "$(tdir_elf32vax)" +eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \ + $(srcdir)/emulparams/xtensa-config.sh \ + $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \ + $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS} + ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)" eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32fr30 "$(tdir_fr30)" diff --git a/ld/Makefile.in b/ld/Makefile.in index 23b7d5b..dc226af 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -297,6 +297,7 @@ ALL_EMULATIONS = \ eelf32ppcwindiss.o \ eelf32vax.o \ eelf32xstormy16.o \ + eelf32xtensa.o \ eelf_i386.o \ eelf_i386_be.o \ eelf_i386_chaos.o \ @@ -1315,6 +1316,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \ eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32vax "$(tdir_elf32vax)" +eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \ + $(srcdir)/emulparams/xtensa-config.sh \ + $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \ + $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS} + ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)" eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32fr30 "$(tdir_fr30)" diff --git a/ld/configure.tgt b/ld/configure.tgt index 3e4fa25..8c10d42 100644 --- a/ld/configure.tgt +++ b/ld/configure.tgt @@ -525,6 +525,7 @@ iq2000-*-elf) targ_emul=elf32iq2000 ; targ_extra_emuls="elf32iq10" ;; frv-*-*) targ_emul=elf32frv ;; w65-*-*) targ_emul=w65 ;; xstormy16-*-*) targ_emul=elf32xstormy16 ;; +xtensa-*-*) targ_emul=elf32xtensa;; fr30-*-*) targ_emul=elf32fr30 ;; mcore-*-pe) targ_emul=mcorepe ; targ_extra_ofiles="deffilep.o pe-dll.o" ;; diff --git a/ld/emulparams/elf32xtensa.sh b/ld/emulparams/elf32xtensa.sh new file mode 100644 index 0000000..12e2882 --- /dev/null +++ b/ld/emulparams/elf32xtensa.sh @@ -0,0 +1,32 @@ +# First set some configuration-specific variables +. ${srcdir}/emulparams/xtensa-config.sh + +# See genscripts.sh and ../scripttempl/elfxtensa.sc for the meaning of these. +SCRIPT_NAME=elfxtensa +TEMPLATE_NAME=elf32 +EXTRA_EM_FILE=xtensaelf +OUTPUT_FORMAT=undefined +BIG_OUTPUT_FORMAT="elf32-xtensa-be" +LITTLE_OUTPUT_FORMAT="elf32-xtensa-le" +TEXT_START_ADDR=0x400000 +NONPAGED_TEXT_START_ADDR=0x400000 +ARCH=xtensa +MACHINE= +GENERATE_SHLIB_SCRIPT=yes +GENERATE_COMBRELOC_SCRIPT=yes +NO_SMALL_DATA=yes +OTHER_READONLY_SECTIONS=' + .xt_except_table : { KEEP (*(.xt_except_table)) } + .xt.lit : { *(.xt.lit*) *(.gnu.linkonce.p*) } +' +OTHER_READWRITE_SECTIONS=' + .xt_except_desc : + { + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + *(.xt_except_desc_end) + } +' +OTHER_SECTIONS=' + .xt.insn : { *(.xt.insn) *(.gnu.linkonce.x*) } +' diff --git a/ld/emulparams/xtensa-config.sh b/ld/emulparams/xtensa-config.sh new file mode 100644 index 0000000..b273df5 --- /dev/null +++ b/ld/emulparams/xtensa-config.sh @@ -0,0 +1,8 @@ +# Xtensa configuration settings. + +## NOTE: This file was automatically generated by the Xtensa Processor +## Generator. Changes made here will be lost when this file is +## updated or replaced with the settings for a different Xtensa +## processor configuration. DO NOT EDIT! + +MAXPAGESIZE=0x1000 diff --git a/ld/emultempl/xtensaelf.em b/ld/emultempl/xtensaelf.em new file mode 100644 index 0000000..b070075 --- /dev/null +++ b/ld/emultempl/xtensaelf.em @@ -0,0 +1,1586 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2003 +# Free Software Foundation, Inc. +# +# This file is part of GLD, the Gnu Linker. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# This file is sourced from elf32.em, and defines extra xtensa-elf +# specific routines. +# +cat >>e${EMULATION_NAME}.c < + +static char *elf_xtensa_choose_target + PARAMS ((int, char **)); +static bfd_boolean elf_xtensa_place_orphan + PARAMS ((lang_input_statement_type *, asection *)); +static void elf_xtensa_before_parse + PARAMS ((void)); +static void elf_xtensa_before_allocation + PARAMS ((void)); +static void xtensa_wild_group_interleave + PARAMS ((lang_statement_union_type *)); +static void xtensa_wild_group_interleave_callback + PARAMS ((lang_statement_union_type *)); +static void xtensa_colocate_output_literals + PARAMS ((lang_statement_union_type *)); +static void xtensa_colocate_output_literals_callback + PARAMS ((lang_statement_union_type *)); + + +/* Flag for the emulation-specific "--no-relax" option. */ +static bfd_boolean disable_relaxation = FALSE; + +/* This number is irrelevant until we turn on use_literal_pages */ +static bfd_vma xtensa_page_power = 12; /* 4K pages. */ + +/* To force a page break between literals and text, change + xtensa_use_literal_pages to "true". */ +static bfd_boolean xtensa_use_literal_pages = FALSE; + +#define EXTRA_VALIDATION 0 + + +static char * +elf_xtensa_choose_target (argc, argv) + int argc ATTRIBUTE_UNUSED; + char **argv ATTRIBUTE_UNUSED; +{ + if (XCHAL_HAVE_BE) + return "${BIG_OUTPUT_FORMAT}"; + else + return "${LITTLE_OUTPUT_FORMAT}"; +} + + +static bfd_boolean +elf_xtensa_place_orphan (file, s) + lang_input_statement_type *file; + asection *s; +{ + /* Early exit for relocatable links. */ + if (link_info.relocateable) + return FALSE; + + return gld${EMULATION_NAME}_place_orphan (file, s); +} + + +static void +elf_xtensa_before_parse () +{ + /* Just call the default hook.... Tensilica's version of this function + does some other work that isn't relevant here. */ + gld${EMULATION_NAME}_before_parse (); +} + + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +void +elf_xtensa_before_allocation () +{ + bfd *in_bfd; + bfd_boolean is_big_endian = XCHAL_HAVE_BE; + + /* Check that the output endianness matches the Xtensa + configuration. The BFD library always includes both big and + little endian target vectors for Xtensa, but it only supports the + detailed instruction encode/decode operations (such as are + required to process relocations) for the selected Xtensa + configuration. */ + + if (is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + { + einfo (_("%F%P: little endian output does not match " + "Xtensa configuration\n")); + } + if (!is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_BIG) + { + einfo (_("%F%P: big endian output does not match " + "Xtensa configuration\n")); + } + + /* Check that the endianness for each input file matches the output. + The merge_private_bfd_data hook has already reported any mismatches + as errors, but those errors are not fatal. At this point, we + cannot go any further if there are any mismatches. */ + + for (in_bfd = link_info.input_bfds; + in_bfd != NULL; + in_bfd = in_bfd->link_next) + { + if ((is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + || (!is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_BIG)) + einfo (_("%F%P: cross-endian linking not supported\n")); + } + + /* Enable relaxation by default if the "--no-relax" option was not + specified. This is done here instead of in the before_parse hook + because there is a check in main() to prohibit use of --relax and + -r together and that combination should be allowed for Xtensa. */ + + if (!disable_relaxation) + command_line.relax = TRUE; + + gld${EMULATION_NAME}_before_allocation (); + + xtensa_wild_group_interleave (stat_ptr->head); + if (command_line.relax) + xtensa_colocate_output_literals (stat_ptr->head); + + /* TBD: We need to force the page alignments to here and only do + them as needed for the entire output section. Finally, if this + is a relocateable link then we need to add alignment notes so + that the literals can be separated later. */ +} + + +typedef struct wildcard_list section_name_list; + +typedef struct reloc_deps_e_t reloc_deps_e; +typedef struct reloc_deps_section_t reloc_deps_section; +typedef struct reloc_deps_graph_t reloc_deps_graph; + + +struct reloc_deps_e_t +{ + asection *src; /* Contains l32rs. */ + asection *tgt; /* Contains literals. */ + reloc_deps_e *next; +}; + +/* Place these in the userdata field. */ +struct reloc_deps_section_t +{ + reloc_deps_e *preds; + reloc_deps_e *succs; + bfd_boolean is_only_literal; +}; + + +struct reloc_deps_graph_t +{ + size_t count; + size_t size; + asection **sections; +}; + +static void xtensa_layout_wild + PARAMS ((const reloc_deps_graph *, lang_wild_statement_type *)); + +typedef void (*deps_callback_t) + PARAMS ((asection *, /* src_sec */ + bfd_vma, /* src_offset */ + asection *, /* target_sec */ + bfd_vma, /* target_offset */ + PTR)); /* closure */ + +static void build_deps_graph_callback + PARAMS ((asection *, bfd_vma, asection *, bfd_vma, PTR)); +extern bfd_boolean xtensa_callback_required_dependence + PARAMS ((bfd *, asection *, struct bfd_link_info *, + deps_callback_t, PTR)); +static void xtensa_ldlang_clear_addresses + PARAMS ((lang_statement_union_type *)); +static bfd_boolean ld_local_file_relocations_fit + PARAMS ((lang_statement_union_type *, const reloc_deps_graph *)); +static bfd_vma ld_assign_relative_paged_dot + PARAMS ((bfd_vma, lang_statement_union_type *, + const reloc_deps_graph *, bfd_boolean)); +static bfd_vma ld_xtensa_insert_page_offsets + PARAMS ((bfd_vma, lang_statement_union_type *, reloc_deps_graph *, + bfd_boolean)); +static void lang_for_each_statement_worker + PARAMS ((void (*) (lang_statement_union_type *), + lang_statement_union_type *)); +static void xtensa_move_dependencies_to_front + PARAMS ((reloc_deps_graph *, lang_wild_statement_type *)); +static reloc_deps_graph *ld_build_required_section_dependence + PARAMS ((lang_statement_union_type *)); +static bfd_boolean section_is_source + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean section_is_target + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean section_is_source_or_target + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean deps_has_sec_edge + PARAMS ((const reloc_deps_graph *, asection *, asection *)); +static bfd_boolean deps_has_edge + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *, + lang_statement_union_type *)); +static void add_deps_edge + PARAMS ((reloc_deps_graph *, asection *, asection *)); +#if EXTRA_VALIDATION +static size_t ld_count_children + PARAMS ((lang_statement_union_type *)); +#endif +static void free_reloc_deps_graph + PARAMS ((reloc_deps_graph *)); +static void xtensa_colocate_literals + PARAMS ((reloc_deps_graph *, lang_statement_union_type *)); +static reloc_deps_section *xtensa_get_section_deps + PARAMS ((const reloc_deps_graph *, asection *)); +static void xtensa_set_section_deps + PARAMS ((const reloc_deps_graph *, asection *, reloc_deps_section *)); +static void xtensa_append_section_deps + PARAMS ((reloc_deps_graph *, asection *)); + +extern lang_statement_list_type constructor_list; + +/* Begin verbatim code from ldlang.c: + the following are copied from ldlang.c because they are defined + there statically. */ + +static void +lang_for_each_statement_worker (func, s) + void (*func) PARAMS ((lang_statement_union_type *)); + lang_statement_union_type *s; +{ + for (; s != (lang_statement_union_type *) NULL; s = s->header.next) + { + func (s); + + switch (s->header.type) + { + case lang_constructors_statement_enum: + lang_for_each_statement_worker (func, constructor_list.head); + break; + case lang_output_section_statement_enum: + lang_for_each_statement_worker + (func, + s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + lang_for_each_statement_worker + (func, + s->wild_statement.children.head); + break; + case lang_group_statement_enum: + lang_for_each_statement_worker (func, + s->group_statement.children.head); + break; + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_section_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + default: + FAIL (); + break; + } + } +} + +/* End of verbatim code from ldlang.c. */ + + +reloc_deps_section * +xtensa_get_section_deps (deps, sec) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + asection *sec; +{ + /* We have a separate function for this so that + we could in the future keep a completely independent + structure that maps a section to its dependence edges. + For now, we place these in the sec->userdata field. */ + reloc_deps_section *sec_deps = (reloc_deps_section *) sec->userdata; + return sec_deps; +} + +void +xtensa_set_section_deps (deps, sec, deps_section) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + asection *sec; + reloc_deps_section *deps_section; +{ + sec->userdata = (void *) deps_section; +} + + +/* This is used to keep a list of all of the sections participating in + the graph so we can clean them up quickly. */ + +static void +xtensa_append_section_deps (deps, sec) + reloc_deps_graph *deps; + asection *sec; +{ + if (deps->size <= deps->count) + { + asection **new_sections; + size_t i; + size_t new_size; + + new_size = deps->size * 2; + if (new_size == 0) + new_size = 20; + + new_sections = (asection**) xmalloc (sizeof (asection*) * new_size); + memset (new_sections, 0, sizeof (asection*) * new_size); + for (i = 0; i < deps->count; i++) + { + new_sections[i] = deps->sections[i]; + } + if (deps->sections != NULL) + free (deps->sections); + deps->sections = new_sections; + deps->size = new_size; + } + deps->sections[deps->count] = sec; + deps->count++; +} + + +static void +free_reloc_deps_graph (deps) + reloc_deps_graph *deps; +{ + size_t i; + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + reloc_deps_section *sec_deps; + sec_deps = xtensa_get_section_deps (deps, sec); + if (sec_deps) + { + reloc_deps_e *next; + while (sec_deps->succs != NULL) + { + next = sec_deps->succs->next; + free (sec_deps->succs); + sec_deps->succs = next; + } + + while (sec_deps->preds != NULL) + { + next = sec_deps->preds->next; + free (sec_deps->preds); + sec_deps->preds = next; + } + free (sec_deps); + } + xtensa_set_section_deps (deps, sec, NULL); + } + if (deps->sections) + free (deps->sections); + + free (deps); +} + + +bfd_boolean +section_is_source (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return (sec_deps && sec_deps->succs != NULL); +} + + +bfd_boolean +section_is_target (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return (sec_deps && sec_deps->preds != NULL); +} + +bfd_boolean +section_is_source_or_target (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + return (section_is_source (deps, s) + || section_is_target (deps, s)); +} + + +typedef struct xtensa_ld_iter_stack_t xtensa_ld_iter_stack; +typedef struct xtensa_ld_iter_t xtensa_ld_iter; + +struct xtensa_ld_iter_t +{ + lang_statement_union_type *parent; /* Parent of the list. */ + lang_statement_list_type *l; /* List that holds it. */ + lang_statement_union_type **loc; /* Place in the list. */ +}; + +struct xtensa_ld_iter_stack_t +{ + xtensa_ld_iter iterloc; /* List that hold it. */ + + xtensa_ld_iter_stack *next; /* Next in the stack. */ + xtensa_ld_iter_stack *prev; /* Back pointer for stack. */ +}; + +static void ld_xtensa_move_section_after + PARAMS ((xtensa_ld_iter *, xtensa_ld_iter *)); + + +void +ld_xtensa_move_section_after (to, current) + xtensa_ld_iter *to; + xtensa_ld_iter *current; +{ + lang_statement_union_type *to_next; + lang_statement_union_type *current_next; + lang_statement_union_type **e; + +#if EXTRA_VALIDATION + size_t old_to_count, new_to_count; + size_t old_current_count, new_current_count; +#endif + + if (to == current) + return; + +#if EXTRA_VALIDATION + old_to_count = ld_count_children (to->parent); + old_current_count = ld_count_children (current->parent); +#endif + + to_next = *(to->loc); + current_next = (*current->loc)->header.next; + + *(to->loc) = *(current->loc); + + *(current->loc) = current_next; + (*(to->loc))->header.next = to_next; + + /* reset "to" list tail */ + for (e = &to->l->head; *e != NULL; e = &(*e)->header.next) + ; + to->l->tail = e; + + /* reset "current" list tail */ + for (e = ¤t->l->head; *e != NULL; e = &(*e)->header.next) + ; + current->l->tail = e; + +#if EXTRA_VALIDATION + new_to_count = ld_count_children (to->parent); + new_current_count = ld_count_children (current->parent); + + ASSERT ((old_to_count + old_current_count) + == (new_to_count + new_current_count)); +#endif +} + + +/* Can only be called with lang_statements that have lists. Returns + false if the list is empty. */ + +static bfd_boolean iter_stack_empty + PARAMS ((xtensa_ld_iter_stack **)); +static bfd_boolean iter_stack_push + PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *)); +static void iter_stack_pop + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_update + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_next + PARAMS ((xtensa_ld_iter_stack **)); +static lang_statement_union_type *iter_stack_current + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_create + PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *)); +static void iter_stack_copy_current + PARAMS ((xtensa_ld_iter_stack **, xtensa_ld_iter *)); + + +static bfd_boolean +iter_stack_empty (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + return (*stack_p == NULL); +} + + +static bfd_boolean +iter_stack_push (stack_p, parent) + xtensa_ld_iter_stack **stack_p; + lang_statement_union_type *parent; +{ + xtensa_ld_iter_stack *stack; + lang_statement_list_type *l = NULL; + + switch (parent->header.type) + { + case lang_output_section_statement_enum: + l = &parent->output_section_statement.children; + break; + case lang_wild_statement_enum: + l = &parent->wild_statement.children; + break; + case lang_group_statement_enum: + l = &parent->group_statement.children; + break; + default: + ASSERT (0); + return FALSE; + } + + /* Empty. do not push. */ + if (l->tail == &l->head) + return FALSE; + + stack = (xtensa_ld_iter_stack *) xmalloc (sizeof (xtensa_ld_iter_stack)); + memset (stack, 0, sizeof (xtensa_ld_iter_stack)); + stack->iterloc.parent = parent; + stack->iterloc.l = l; + stack->iterloc.loc = &l->head; + + stack->next = *stack_p; + stack->prev = NULL; + if (*stack_p != NULL) + (*stack_p)->prev = stack; + *stack_p = stack; + return TRUE; +} + + +static void +iter_stack_pop (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + xtensa_ld_iter_stack *stack; + + stack = *stack_p; + + if (stack == NULL) + { + ASSERT (stack != NULL); + return; + } + + if (stack->next != NULL) + stack->next->prev = NULL; + + *stack_p = stack->next; + free (stack); +} + + +/* This MUST be called if, during iteration, the user changes the + underlying structure. It will check for a NULL current and advance + accordingly. */ + +static void +iter_stack_update (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + if (!iter_stack_empty (stack_p) + && (*(*stack_p)->iterloc.loc) == NULL) + { + iter_stack_pop (stack_p); + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; + } +} + + +static void +iter_stack_next (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + xtensa_ld_iter_stack *stack; + lang_statement_union_type *current; + stack = *stack_p; + + current = *stack->iterloc.loc; + /* If we are on the first element. */ + if (current != NULL) + { + switch (current->header.type) + { + case lang_output_section_statement_enum: + case lang_wild_statement_enum: + case lang_group_statement_enum: + /* If the list if not empty, we are done. */ + if (iter_stack_push (stack_p, *stack->iterloc.loc)) + return; + /* Otherwise increment the pointer as normal. */ + break; + default: + break; + } + } + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; +} + + +static lang_statement_union_type * +iter_stack_current (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + return *((*stack_p)->iterloc.loc); +} + + +/* The iter stack is a preorder. */ + +static void +iter_stack_create (stack_p, parent) + xtensa_ld_iter_stack **stack_p; + lang_statement_union_type *parent; +{ + iter_stack_push (stack_p, parent); +} + + +static void +iter_stack_copy_current (stack_p, front) + xtensa_ld_iter_stack **stack_p; + xtensa_ld_iter *front; +{ + *front = (*stack_p)->iterloc; +} + + +void +xtensa_colocate_literals (deps, statement) + reloc_deps_graph *deps; + lang_statement_union_type *statement; +{ + /* Keep a stack of pointers to control iteration through the contours. */ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + xtensa_ld_iter front; /* Location where new insertion should occur. */ + xtensa_ld_iter *front_p = NULL; + + xtensa_ld_iter current; /* Location we are checking. */ + xtensa_ld_iter *current_p = NULL; + bfd_boolean in_literals = FALSE; + + if (deps->count == 0) + return; + +#if 0 + ld_assign_relative_paged_dot (0x100000, statement, deps, + xtensa_use_literal_pages); + + if (!ld_local_file_relocations_fit (statement, deps)) + fprintf (stderr, "initial relocation placement does not fit\n"); + + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); +#endif + + iter_stack_create (stack_p, statement); + + while (!iter_stack_empty (stack_p)) + { + bfd_boolean skip_increment = FALSE; + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_assignment_statement_enum: + /* Any assignment statement should block reordering across it. */ + front_p = NULL; + in_literals = FALSE; + break; + + case lang_input_section_enum: + if (front_p == NULL) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + if (in_literals) + { + front_p = &front; + iter_stack_copy_current (stack_p, front_p); + } + } + else + { + bfd_boolean is_target; + current_p = ¤t; + iter_stack_copy_current (stack_p, current_p); + is_target = (section_is_target (deps, l) + && !section_is_source (deps, l)); + + if (in_literals) + { + iter_stack_copy_current (stack_p, front_p); + if (!is_target) + in_literals = FALSE; + } + else + { + if (is_target) + { + /* Try to insert in place. */ + ld_xtensa_move_section_after (front_p, current_p); + ld_assign_relative_paged_dot (0x100000, + statement, + deps, + xtensa_use_literal_pages); + + /* We use this code because it's already written. */ + if (!ld_local_file_relocations_fit (statement, deps)) + { + /* Move it back. */ + ld_xtensa_move_section_after (current_p, front_p); + /* Reset the literal placement. */ + iter_stack_copy_current (stack_p, front_p); + } + else + { + /* Move front pointer up by one. */ + front_p->loc = &(*front_p->loc)->header.next; + + /* Do not increment the current pointer. */ + skip_increment = TRUE; + } + } + } + } + break; + default: + break; + } + + if (!skip_increment) + iter_stack_next (stack_p); + else + /* Be careful to update the stack_p if it now is a null. */ + iter_stack_update (stack_p); + } + + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); +} + + +void +xtensa_move_dependencies_to_front (deps, w) + reloc_deps_graph *deps; + lang_wild_statement_type *w; +{ + /* Keep a front pointer and a current pointer. */ + lang_statement_union_type **front; + lang_statement_union_type **current; + + /* Walk to the end of the targets. */ + for (front = &w->children.head; + (*front != NULL) && section_is_source_or_target (deps, *front); + front = &(*front)->header.next) + ; + + if (*front == NULL) + return; + + current = &(*front)->header.next; + while (*current != NULL) + { + if (section_is_source_or_target (deps, *current)) + { + /* Insert in place. */ + xtensa_ld_iter front_iter; + xtensa_ld_iter current_iter; + + front_iter.parent = (lang_statement_union_type *) w; + front_iter.l = &w->children; + front_iter.loc = front; + + current_iter.parent = (lang_statement_union_type *) w; + current_iter.l = &w->children; + current_iter.loc = current; + + ld_xtensa_move_section_after (&front_iter, ¤t_iter); + front = &(*front)->header.next; + } + else + { + current = &(*current)->header.next; + } + } +} + + +static bfd_boolean +deps_has_sec_edge (deps, src, tgt) + const reloc_deps_graph *deps; + asection *src; + asection *tgt; +{ + const reloc_deps_section *sec_deps; + const reloc_deps_e *sec_deps_e; + + sec_deps = xtensa_get_section_deps (deps, src); + if (sec_deps == NULL) + return FALSE; + + for (sec_deps_e = sec_deps->succs; + sec_deps_e != NULL; + sec_deps_e = sec_deps_e->next) + { + ASSERT (sec_deps_e->src == src); + if (sec_deps_e->tgt == tgt) + return TRUE; + } + return FALSE; +} + + +static bfd_boolean +deps_has_edge (deps, src, tgt) + const reloc_deps_graph *deps; + lang_statement_union_type *src; + lang_statement_union_type *tgt; +{ + if (!section_is_source (deps, src)) + return FALSE; + if (!section_is_target (deps, tgt)) + return FALSE; + + if (src->header.type != lang_input_section_enum) + return FALSE; + if (tgt->header.type != lang_input_section_enum) + return FALSE; + + return deps_has_sec_edge (deps, src->input_section.section, + tgt->input_section.section); +} + + +static void +add_deps_edge (deps, src_sec, tgt_sec) + reloc_deps_graph *deps; + asection *src_sec; + asection *tgt_sec; +{ + reloc_deps_section *src_sec_deps; + reloc_deps_section *tgt_sec_deps; + + reloc_deps_e *src_edge; + reloc_deps_e *tgt_edge; + + if (deps_has_sec_edge (deps, src_sec, tgt_sec)) + return; + + src_sec_deps = xtensa_get_section_deps (deps, src_sec); + if (src_sec_deps == NULL) + { + /* Add a section. */ + src_sec_deps = (reloc_deps_section *) + xmalloc (sizeof (reloc_deps_section)); + memset (src_sec_deps, 0, sizeof (reloc_deps_section)); + src_sec_deps->is_only_literal = 0; + src_sec_deps->preds = NULL; + src_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, src_sec, src_sec_deps); + xtensa_append_section_deps (deps, src_sec); + } + + tgt_sec_deps = xtensa_get_section_deps (deps, tgt_sec); + if (tgt_sec_deps == NULL) + { + /* Add a section. */ + tgt_sec_deps = (reloc_deps_section *) + xmalloc (sizeof (reloc_deps_section)); + memset (tgt_sec_deps, 0, sizeof (reloc_deps_section)); + tgt_sec_deps->is_only_literal = 0; + tgt_sec_deps->preds = NULL; + tgt_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, tgt_sec, tgt_sec_deps); + xtensa_append_section_deps (deps, tgt_sec); + } + + /* Add the edges. */ + src_edge = (reloc_deps_e *) xmalloc (sizeof (reloc_deps_e)); + memset (src_edge, 0, sizeof (reloc_deps_e)); + src_edge->src = src_sec; + src_edge->tgt = tgt_sec; + src_edge->next = src_sec_deps->succs; + src_sec_deps->succs = src_edge; + + tgt_edge = (reloc_deps_e *) xmalloc (sizeof (reloc_deps_e)); + memset (tgt_edge, 0, sizeof (reloc_deps_e)); + tgt_edge->src = src_sec; + tgt_edge->tgt = tgt_sec; + tgt_edge->next = tgt_sec_deps->preds; + tgt_sec_deps->preds = tgt_edge; +} + + +void +build_deps_graph_callback (src_sec, src_offset, + target_sec, target_offset, closure) + asection *src_sec; + bfd_vma src_offset ATTRIBUTE_UNUSED; + asection *target_sec; + bfd_vma target_offset ATTRIBUTE_UNUSED; + PTR closure; +{ + reloc_deps_graph *deps; + deps = (reloc_deps_graph*) closure; + + /* If the target is defined. */ + if (target_sec != NULL) + add_deps_edge (deps, src_sec, target_sec); +} + + +reloc_deps_graph * +ld_build_required_section_dependence (s) + lang_statement_union_type *s; +{ + reloc_deps_graph *deps; + xtensa_ld_iter_stack *stack = NULL; + + deps = (reloc_deps_graph*) xmalloc (sizeof (reloc_deps_graph)); + deps->sections = NULL; + deps->count = 0; + deps->size = 0; + + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input; + input = &l->input_section; + xtensa_callback_required_dependence (input->ifile->the_bfd, + input->section, + &link_info, + /* Use the same closure. */ + build_deps_graph_callback, + (PTR) deps); + } + } + return deps; +} + + +#if EXTRA_VALIDATION +size_t +ld_count_children (s) + lang_statement_union_type *s; +{ + size_t count = 0; + xtensa_ld_iter_stack *stack = NULL; + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + ASSERT (l != NULL); + count++; + } + return count; +} +#endif /* EXTRA_VALIDATION */ + + +void +xtensa_wild_group_interleave_callback (statement) + lang_statement_union_type * statement; +{ + lang_wild_statement_type *w; + reloc_deps_graph *deps; + if (statement->header.type == lang_wild_statement_enum) + { +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder; + + w = &statement->wild_statement; + + no_reorder = FALSE; + + /* If it has 0 or 1 section bound, then do not reorder. */ + if (w->children.head == NULL + || (w->children.head->header.type == lang_input_section_enum + && w->children.head->header.next == NULL)) + no_reorder = TRUE; + + if (w->filenames_sorted) + no_reorder = TRUE; + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.sorted == TRUE) + { + no_reorder = TRUE; + break; + } + } + } + + /* Special case until the NOREORDER linker directive is supported: + *(.init) output sections and *(.fini) specs may NOT be reordered. */ + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.name + && ((strcmp (".init", l->spec.name) == 0) + || (strcmp (".fini", l->spec.name) == 0))) + { + no_reorder = TRUE; + break; + } + } + } + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder && deps->count != 0) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_layout_wild (deps, w); + + xtensa_move_dependencies_to_front (deps, w); +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +void +xtensa_wild_group_interleave (s) + lang_statement_union_type *s; +{ + lang_for_each_statement_worker (xtensa_wild_group_interleave_callback, s); +} + + +void +xtensa_layout_wild (deps, w) + const reloc_deps_graph *deps; + lang_wild_statement_type *w; +{ + /* If it does not fit initially, we need to do this step. Move all + of the wild literal sections to a new list, then move each of + them back in just before the first section they depend on. */ + lang_statement_union_type **s_p; +#if EXTRA_VALIDATION + size_t old_count, new_count; + size_t ct1, ct2; +#endif + + lang_wild_statement_type literal_wild; + literal_wild.header.next = NULL; + literal_wild.header.type = lang_wild_statement_enum; + literal_wild.filename = NULL; + literal_wild.filenames_sorted = FALSE; + literal_wild.section_list = NULL; + literal_wild.keep_sections = FALSE; + literal_wild.children.head = NULL; + literal_wild.children.tail = &literal_wild.children.head; + +#if EXTRA_VALIDATION + old_count = ld_count_children ((lang_statement_union_type*) w); +#endif + + s_p = &w->children.head; + while (*s_p != NULL) + { + lang_statement_union_type *l = *s_p; + if (l->header.type == lang_input_section_enum) + { + if (section_is_target (deps, l) + && ! section_is_source (deps, l)) + { + /* Detach. */ + *s_p = l->header.next; + if (*s_p == NULL) + w->children.tail = s_p; + l->header.next = NULL; + + /* Append. */ + *literal_wild.children.tail = l; + literal_wild.children.tail = &l->header.next; + continue; + } + } + s_p = &(*s_p)->header.next; + } + +#if EXTRA_VALIDATION + ct1 = ld_count_children ((lang_statement_union_type*) w); + ct2 = ld_count_children ((lang_statement_union_type*) &literal_wild); + + ASSERT (old_count == (ct1 + ct2)); +#endif + + /* Now place them back in front of their dependent sections. */ + + while (literal_wild.children.head != NULL) + { + lang_statement_union_type *lit = literal_wild.children.head; + bfd_boolean placed = FALSE; + +#if EXTRA_VALIDATION + ASSERT (ct2 > 0); + ct2--; +#endif + + /* Detach. */ + literal_wild.children.head = lit->header.next; + if (literal_wild.children.head == NULL) + literal_wild.children.tail = &literal_wild.children.head; + lit->header.next = NULL; + + /* Find a spot to place it. */ + for (s_p = &w->children.head; *s_p != NULL; s_p = &(*s_p)->header.next) + { + lang_statement_union_type *src = *s_p; + if (deps_has_edge (deps, src, lit)) + { + /* Place it here. */ + lit->header.next = *s_p; + *s_p = lit; + placed = TRUE; + break; + } + } + + if (!placed) + { + /* Put it at the end. */ + *w->children.tail = lit; + w->children.tail = &lit->header.next; + } + } + +#if EXTRA_VALIDATION + new_count = ld_count_children ((lang_statement_union_type*) w); + ASSERT (new_count == old_count); +#endif +} + + +void +xtensa_colocate_output_literals_callback (statement) + lang_statement_union_type * statement; +{ + lang_output_section_statement_type *os; + reloc_deps_graph *deps; + if (statement->header.type == lang_output_section_statement_enum) + { + /* Now, we walk over the contours of the output section statement. + + First we build the literal section dependences as before. + + At the first uniquely_literal section, we mark it as a good + spot to place other literals. Continue walking (and counting + sizes) until we find the next literal section. If this + section can be moved to the first one, then we move it. If + we every find a modification of ".", start over. If we find + a labeling of the current location, start over. Finally, at + the end, if we require page alignment, add page alignments. */ + +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder = FALSE; + + os = &statement->output_section_statement; + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Insert align/offset assignment statement. */ + if (xtensa_use_literal_pages) + { + ld_xtensa_insert_page_offsets ((bfd_vma) 0, statement, deps, + xtensa_use_literal_pages); + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, + statement); + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +void +xtensa_colocate_output_literals (s) + lang_statement_union_type *s; +{ + lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s); +} + + +void +xtensa_ldlang_clear_addresses (statement) + lang_statement_union_type * statement; +{ + switch (statement->header.type) + { + case lang_input_section_enum: + { + asection *bfd_section = statement->input_section.section; + bfd_section->output_offset = 0; + } + break; + default: + break; + } +} + + +bfd_vma +ld_assign_relative_paged_dot (dot, s, deps, lit_align) + bfd_vma dot; + lang_statement_union_type *s; + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + bfd_boolean lit_align; +{ + /* Walk through all of the input statements in this wild statement + assign dot to all of them. */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + size_t align_pow = section->alignment_power; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + bfd_boolean sec_is_target = section_is_target (deps, l); + bfd_boolean sec_is_source = section_is_source (deps, l); + + if (section->_raw_size != 0 + && (first_section + || (in_literals && !sec_is_target) + || (!in_literals && sec_is_target))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->_raw_size != 0) + in_literals = (sec_is_target && !sec_is_source); + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + dot += (1 << xtensa_page_power); + + dot = align_power (dot, align_pow); + section->output_offset = dot; + dot += section->_raw_size; + } + break; + case lang_fill_statement_enum: + dot += l->fill_statement.size; + break; + case lang_padding_statement_enum: + dot += l->padding_statement.size; + break; + default: + break; + } + } + return dot; +} + + +bfd_boolean +ld_local_file_relocations_fit (statement, deps) + lang_statement_union_type *statement; + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; +{ + /* Walk over all of the dependencies that we identified and make + sure that IF the source and target are here (addr != 0): + 1) target addr < source addr + 2) (roundup(source + source_size, 4) - rounddown(target, 4)) + < (256K - (1 << bad align)) + Need a worst-case proof.... */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + size_t max_align_power = 0; + size_t align_penalty = 256; + reloc_deps_e *e; + size_t i; + + /* Find the worst-case alignment requirement for this set of statements. */ + for (iter_stack_create (stack_p, statement); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input = &l->input_section; + asection *section = input->section; + if (section->alignment_power > max_align_power) + max_align_power = section->alignment_power; + } + } + + /* Now check that everything fits. */ + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + const reloc_deps_section *deps_section = + xtensa_get_section_deps (deps, sec); + if (deps_section) + { + /* We choose to walk through the successors. */ + for (e = deps_section->succs; e != NULL; e = e->next) + { + if ((e->src != e->tgt) + && e->src->output_section == e->tgt->output_section + && e->src->output_offset != 0 + && e->tgt->output_offset != 0) + { + bfd_vma l32r_addr = + align_power (e->src->output_offset + e->src->_raw_size, 2); + bfd_vma target_addr = e->tgt->output_offset & (~3); + if (l32r_addr < target_addr) + { + fprintf (stderr, "Warning: " + "l32r target section before l32r\n"); + return FALSE; + } + + if ((l32r_addr - target_addr) > (256*1024 - align_penalty)) + return FALSE; + } + } + } + } + + return TRUE; +} + + +bfd_vma +ld_xtensa_insert_page_offsets (dot, s, deps, lit_align) + bfd_vma dot; + lang_statement_union_type *s; + reloc_deps_graph *deps; + bfd_boolean lit_align; +{ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + if (!lit_align) + return FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + if (section->_raw_size != 0 + && (first_section + || (in_literals && !section_is_target (deps, l)) + || (!in_literals && section_is_target (deps, l)))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->_raw_size != 0) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + } + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + { + /* Create an expression that increments the current address, + i.e., "dot", by (1 << xtensa_align_power). */ + etree_type *name_op = exp_nameop (NAME, "."); + etree_type *addend_op = exp_intop (1 << xtensa_page_power); + etree_type *add_op = exp_binop ('+', name_op, addend_op); + etree_type *assign_op = exp_assop ('=', ".", add_op); + + lang_assignment_statement_type *assign_stmt; + lang_statement_union_type *assign_union; + lang_statement_list_type tmplist; + lang_statement_list_type *old_stat_ptr = stat_ptr; + + /* There is hidden state in "lang_add_assignment". It + appends the new assignment statement to the stat_ptr + list. Thus, we swap it before and after the call. */ + + tmplist.head = NULL; + tmplist.tail = &tmplist.head; + + stat_ptr = &tmplist; + /* Warning: side effect; statement appended to stat_ptr. */ + assign_stmt = lang_add_assignment (assign_op); + assign_union = (lang_statement_union_type *) assign_stmt; + stat_ptr = old_stat_ptr; + + assign_union->header.next = l; + *(*stack_p)->iterloc.loc = assign_union; + iter_stack_next (stack_p); + } + } + break; + default: + break; + } + } + return dot; +} + +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_NO_RELAX 301 +' + +PARSE_AND_LIST_LONGOPTS=' + { "no-relax", no_argument, NULL, OPTION_NO_RELAX}, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _(" --no-relax\t\tDo not relax branches or coalesce literals\n")); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_NO_RELAX: + disable_relaxation = TRUE; + break; +' + +# Replace some of the standard ELF functions with our own versions. +# +LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse +LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target +LDEMUL_PLACE_ORPHAN=elf_xtensa_place_orphan +LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation + diff --git a/ld/gen-doc.texi b/ld/gen-doc.texi index 0a5acb2..5add195 100644 --- a/ld/gen-doc.texi +++ b/ld/gen-doc.texi @@ -11,6 +11,7 @@ @set MSP430 @set TICOFF @set WIN32 +@set XTENSA @c 3. Properties of this configuration @clear SingleFormat diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 96a87f4..ce9bf90 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -45,6 +45,7 @@ @set V850 @set VAX @set WIN32 +@set XTENSA @end ifset @c man end @@ -157,6 +158,9 @@ section entitled ``GNU Free Documentation License''. @ifset WIN32 * Win32:: ld and WIN32 (cygwin/mingw) @end ifset +@ifset XTENSA +* Xtensa:: ld and Xtensa Processors +@end ifset @end ifclear @ifclear SingleFormat * BFD:: BFD @@ -1227,7 +1231,9 @@ This option is only supported on a few targets. @ifset I960 @xref{i960,, @command{ld} and the Intel 960 family}. @end ifset - +@ifset XTENSA +@xref{Xtensa,, @command{ld} and Xtensa Processors}. +@end ifset On some platforms, the @samp{--relax} option performs global optimizations that become possible when the linker resolves addressing @@ -4446,6 +4452,9 @@ functionality are not listed. @ifset WIN32 * WIN32:: @command{ld} and WIN32 (cygwin/mingw) @end ifset +@ifset XTENSA +* Xtensa:: @command{ld} and Xtensa Processors +@end ifset @end menu @end ifset @@ -5077,6 +5086,72 @@ which is probably not what you wanted. @end ifclear @end ifset +@ifset XTENSA +@ifclear GENERIC +@raisesections +@end ifclear + +@node Xtensa +@section @code{ld} and Xtensa Processors + +@cindex Xtensa processors +The default @command{ld} behavior for Xtensa processors is to interpret +@code{SECTIONS} commands so that lists of explicitly named sections in a +specification with a wildcard file will be interleaved when necessary to +keep literal pools within the range of PC-relative load offsets. For +example, with the command: + +@smallexample +SECTIONS +@{ + .text : @{ + *(.literal .text) + @} +@} +@end smallexample + +@noindent +@command{ld} may interleave some of the @code{.literal} +and @code{.text} sections from different object files to ensure that the +literal pools are within the range of PC-relative load offsets. A valid +interleaving might place the @code{.literal} sections from an initial +group of files followed by the @code{.text} sections of that group of +files. Then, the @code{.literal} sections from the rest of the files +and the @code{.text} sections from the rest of the files would follow. +The non-interleaved order can still be specified as: + +@smallexample +SECTIONS +@{ + .text : @{ + *(.literal) *(.text) + @} +@} +@end smallexample + +@cindex @code{--relax} on Xtensa +@cindex relaxing on Xtensa +@kindex --no-relax +The Xtensa version of @command{ld} enables the @option{--relax} option by +default to attempt to reduce space in the output image by combining +literals with identical values. It also provides the +@option{--no-relax} option to disable this optimization. When enabled, +the relaxation algorithm ensures that a literal will only be merged with +another literal when the new merged literal location is within the +offset range of all of its uses. + +The relaxation mechanism will also attempt to optimize +assembler-generated ``longcall'' sequences of +@code{L32R}/@code{CALLX@var{n}} when the target is known to fit into a +@code{CALL@var{n}} instruction encoding. The current optimization +converts the sequence into @code{NOP}/@code{CALL@var{n}} and removes the +literal referenced by the @code{L32R} instruction. + +@ifclear GENERIC +@lowersections +@end ifclear +@end ifset + @ifclear SingleFormat @node BFD @chapter BFD diff --git a/ld/scripttempl/elfxtensa.sc b/ld/scripttempl/elfxtensa.sc new file mode 100644 index 0000000..a4ba5c8 --- /dev/null +++ b/ld/scripttempl/elfxtensa.sc @@ -0,0 +1,397 @@ +# +# Unusual variables checked by this code: +# NOP - four byte opcode for no-op (defaults to 0) +# NO_SMALL_DATA - no .sbss/.sbss2/.sdata/.sdata2 sections if not +# empty. +# DATA_ADDR - if end-of-text-plus-one-page isn't right for data start +# INITIAL_READONLY_SECTIONS - at start of text segment +# OTHER_READONLY_SECTIONS - other than .text .init .rodata ... +# (e.g., .PARISC.milli) +# OTHER_TEXT_SECTIONS - these get put in .text when relocating +# OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ... +# (e.g., .PARISC.global) +# OTHER_BSS_SECTIONS - other than .bss .sbss ... +# OTHER_SECTIONS - at the end +# EXECUTABLE_SYMBOLS - symbols that must be defined for an +# executable (e.g., _DYNAMIC_LINK) +# TEXT_START_SYMBOLS - symbols that appear at the start of the +# .text section. +# DATA_START_SYMBOLS - symbols that appear at the start of the +# .data section. +# OTHER_GOT_SYMBOLS - symbols defined just before .got. +# OTHER_GOT_SECTIONS - sections just after .got. +# OTHER_SDATA_SECTIONS - sections just after .sdata. +# OTHER_BSS_SYMBOLS - symbols that appear at the start of the +# .bss section besides __bss_start. +# TEXT_DYNAMIC - .dynamic in text segment, not data segment. +# EMBEDDED - whether this is for an embedded system. +# SHLIB_TEXT_START_ADDR - if set, add to SIZEOF_HEADERS to set +# start address of shared library. +# INPUT_FILES - INPUT command of files to always include +# WRITABLE_RODATA - if set, the .rodata section should be writable +# INIT_START, INIT_END - statements just before and just after +# combination of .init sections. +# FINI_START, FINI_END - statements just before and just after +# combination of .fini sections. +# STACK_ADDR - start of a .stack section. +# OTHER_END_SYMBOLS - symbols to place right at the end of the script. +# +# When adding sections, do note that the names of some sections are used +# when specifying the start address of the next. +# + +# Many sections come in three flavours. There is the 'real' section, +# like ".data". Then there are the per-procedure or per-variable +# sections, generated by -ffunction-sections and -fdata-sections in GCC, +# and useful for --gc-sections, which for a variable "foo" might be +# ".data.foo". Then there are the linkonce sections, for which the linker +# eliminates duplicates, which are named like ".gnu.linkonce.d.foo". +# The exact correspondences are: +# +# Section Linkonce section +# .text .gnu.linkonce.t.foo +# .rodata .gnu.linkonce.r.foo +# .data .gnu.linkonce.d.foo +# .bss .gnu.linkonce.b.foo +# .sdata .gnu.linkonce.s.foo +# .sbss .gnu.linkonce.sb.foo +# .sdata2 .gnu.linkonce.s2.foo +# .sbss2 .gnu.linkonce.sb2.foo +# .debug_info .gnu.linkonce.wi.foo +# .tdata .gnu.linkonce.td.foo +# .tbss .gnu.linkonce.tb.foo +# +# Each of these can also have corresponding .rel.* and .rela.* sections. + +test -z "$ENTRY" && ENTRY=_start +test -z "${ELFSIZE}" && ELFSIZE=32 +test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8" +test "$LD_FLAG" = "N" && DATA_ADDR=. +test -n "$CREATE_SHLIB" && test -n "$SHLIB_DATA_ADDR" && COMMONPAGESIZE="" +test -z "$CREATE_SHLIB" && test -n "$DATA_ADDR" && COMMONPAGESIZE="" +DATA_SEGMENT_ALIGN="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))" +DATA_SEGMENT_END="" +if test -n "${COMMONPAGESIZE}"; then + DATA_SEGMENT_ALIGN="ALIGN (${SEGMENT_SIZE}) - ((${MAXPAGESIZE} - .) & (${MAXPAGESIZE} - 1)); . = DATA_SEGMENT_ALIGN (${MAXPAGESIZE}, ${COMMONPAGESIZE})" + DATA_SEGMENT_END=". = DATA_SEGMENT_END (.);" +fi +INTERP=".interp ${RELOCATING-0} : { *(.interp) }" +DYNAMIC=".dynamic ${RELOCATING-0} : { *(.dynamic) }" +RODATA=".rodata ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }" +INIT_LIT=".init.literal 0 : { *(.init.literal) }" +INIT=".init 0 : { *(.init) }" +FINI_LIT=".fini.literal 0 : { *(.fini.literal) }" +FINI=".fini 0 : { *(.fini) }" +if test -z "${NO_SMALL_DATA}"; then + SBSS=".sbss ${RELOCATING-0} : + { + ${RELOCATING+PROVIDE (__sbss_start = .);} + ${RELOCATING+PROVIDE (___sbss_start = .);} + *(.dynsbss) + *(.sbss${RELOCATING+ .sbss.* .gnu.linkonce.sb.*}) + *(.scommon) + ${RELOCATING+PROVIDE (__sbss_end = .);} + ${RELOCATING+PROVIDE (___sbss_end = .);} + }" + SBSS2=".sbss2 ${RELOCATING-0} : { *(.sbss2${RELOCATING+ .sbss2.* .gnu.linkonce.sb2.*}) }" + SDATA="/* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata ${RELOCATING-0} : + { + ${RELOCATING+${SDATA_START_SYMBOLS}} + *(.sdata${RELOCATING+ .sdata.* .gnu.linkonce.s.*}) + }" + SDATA2=".sdata2 ${RELOCATING-0} : { *(.sdata2${RELOCATING+ .sdata2.* .gnu.linkonce.s2.*}) }" + REL_SDATA=".rel.sdata ${RELOCATING-0} : { *(.rel.sdata${RELOCATING+ .rel.sdata.* .rel.gnu.linkonce.s.*}) } + .rela.sdata ${RELOCATING-0} : { *(.rela.sdata${RELOCATING+ .rela.sdata.* .rela.gnu.linkonce.s.*}) }" + REL_SBSS=".rel.sbss ${RELOCATING-0} : { *(.rel.sbss${RELOCATING+ .rel.sbss.* .rel.gnu.linkonce.sb.*}) } + .rela.sbss ${RELOCATING-0} : { *(.rela.sbss${RELOCATING+ .rela.sbss.* .rela.gnu.linkonce.sb.*}) }" + REL_SDATA2=".rel.sdata2 ${RELOCATING-0} : { *(.rel.sdata2${RELOCATING+ .rel.sdata2.* .rel.gnu.linkonce.s2.*}) } + .rela.sdata2 ${RELOCATING-0} : { *(.rela.sdata2${RELOCATING+ .rela.sdata2.* .rela.gnu.linkonce.s2.*}) }" + REL_SBSS2=".rel.sbss2 ${RELOCATING-0} : { *(.rel.sbss2${RELOCATING+ .rel.sbss2.* .rel.gnu.linkonce.sb2.*}) } + .rela.sbss2 ${RELOCATING-0} : { *(.rela.sbss2${RELOCATING+ .rela.sbss2.* .rela.gnu.linkonce.sb2.*}) }" +fi +CTOR=".ctors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${CTOR_START}} + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + + KEEP (*crtbegin.o(.ctors)) + + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + + KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + ${CONSTRUCTING+${CTOR_END}} + }" +DTOR=".dtors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${DTOR_START}} + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ${CONSTRUCTING+${DTOR_END}} + }" +STACK=" .stack ${RELOCATING-0}${RELOCATING+${STACK_ADDR}} : + { + ${RELOCATING+_stack = .;} + *(.stack) + }" + +# if this is for an embedded system, don't add SIZEOF_HEADERS. +if [ -z "$EMBEDDED" ]; then + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR} + SIZEOF_HEADERS" +else + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR}" +fi + +cat < + + * ld-elf/merge.d: xfail xtensa-*-*. + * ld-scripts/crossref.exp: Add -mtext-section-literals to CFLAGS + for Xtensa targets. + * ld-srec/srec.exp: Add -no-relax flag for Xtensa targets. + * ld-xtensa/coalesce1.s: New file. + * ld-xtensa/coalesce2.s: Likewise. + * ld-xtensa/coalesce.exp: Likewise. + * ld-xtensa/coalesce.t: Likewise. + * ld-xtensa/lcall1.s: Likewise. + * ld-xtensa/lcall2.s: Likewise. + * ld-xtensa/lcall.exp: Likewise. + * ld-xtensa/lcall.t: Likewise. + 2003-03-25 Alexandre Oliva * ld-mips-elf/mips-elf.exp: Added... diff --git a/ld/testsuite/ld-elf/merge.d b/ld/testsuite/ld-elf/merge.d index cbefcaf..6331927 100644 --- a/ld/testsuite/ld-elf/merge.d +++ b/ld/testsuite/ld-elf/merge.d @@ -3,7 +3,7 @@ #objdump: -s #xfail: "arc-*-*" "avr-*-*" "cris-*-*" "dlx-*-*" "fr30-*-*" "frv-*-*" #xfail: "hppa*-*-*" "h8300-*-*" "i960-*-*" "ip2k-*-*" "m32r-*-*" "mcore-*-*" -#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*" +#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*" "xtensa-*-*" .*: file format .*elf.* diff --git a/ld/testsuite/ld-scripts/crossref.exp b/ld/testsuite/ld-scripts/crossref.exp index fbc3151..4fd81b3 100644 --- a/ld/testsuite/ld-scripts/crossref.exp +++ b/ld/testsuite/ld-scripts/crossref.exp @@ -26,6 +26,13 @@ if { [which $CC] == 0 } { return } +# Xtensa targets currently default to putting literal values in a separate +# section and that requires linker script support, so put literals in text. +global CFLAGS +if [istarget xtensa*-*-*] { + set CFLAGS "$CFLAGS -mtext-section-literals" +} + if { ![ld_compile $CC "$srcdir/$subdir/cross1.c" tmpdir/cross1.o] \ || ![ld_compile $CC "$srcdir/$subdir/cross2.c" tmpdir/cross2.o] } { unresolved $test1 diff --git a/ld/testsuite/ld-srec/srec.exp b/ld/testsuite/ld-srec/srec.exp index a965c22..979cfda 100644 --- a/ld/testsuite/ld-srec/srec.exp +++ b/ld/testsuite/ld-srec/srec.exp @@ -288,6 +288,11 @@ proc run_srec_test { test objs } { if [istarget v850*-*-elf] { set objs "$objs -L ../gcc -lgcc" } + + # Xtensa ELF targets relax by default; S-Record linker does not + if [istarget xtensa*-*-*] { + set flags "$flags -no-relax" + } if { ![ld_simple_link $ld tmpdir/sr1 "$flags $objs"] \ || ![ld_simple_link $ld tmpdir/sr2.sr "$flags --oformat srec $objs"] } { diff --git a/ld/testsuite/ld-xtensa/coalesce.exp b/ld/testsuite/ld-xtensa/coalesce.exp new file mode 100644 index 0000000..ef51d6f --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce.exp @@ -0,0 +1,93 @@ +# Test literal coaslescing for Xtensa targets. +# By David Heine, Tensilica, Inc. +# Copyright 2002, 2003 +# Free Software Foundation, Inc. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +set testname "COALESCE" + +set OBJDUMPFLAGS "-dr" + +# +# default_ld_objdump +# run objdump on a file +# +proc default_ld_objdump { objdump object outputfile } { + global OBJDUMPFLAGS + global objdump_output + global host_triplet + + if {[which $objdump] == 0} then { + perror "$objdump does not exist" + return 0 + } + + if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" } + + verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile" + + catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output + set exec_output [prune_warnings $exec_output] + if [string match "" $exec_output] then { + return 1 + } else { + verbose -log "$exec_output" + perror "$object: objdump failed" + return 0 + } +} + + +if ![ld_assemble $as $srcdir/$subdir/coalesce1.s tmpdir/coalesce1.o] { + unresolved $testname + return +} +if ![ld_assemble $as $srcdir/$subdir/coalesce2.s tmpdir/coalesce2.o] { + unresolved $testname + return +} + +set object "tmpdir/coalesce" +set outputfile "$object.txt" + +if ![ld_simple_link $ld $object "-T $srcdir/$subdir/coalesce.t tmpdir/coalesce1.o tmpdir/coalesce2.o"] { + verbose -log "failure in ld" + fail $testname + return +} + +if ![default_ld_objdump $objdump $object $outputfile ] { + verbose -log "failure in objdump" + fail $testname + return +} + +set file [open $outputfile r] +set found 0 + +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "^0000000c
:" $line] { + set found 1 + } +} +close $file +if $found { + pass $testname +} else { + fail $testname +} + diff --git a/ld/testsuite/ld-xtensa/coalesce.t b/ld/testsuite/ld-xtensa/coalesce.t new file mode 100644 index 0000000..7bff69f --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce.t @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x00000000 : { + *(.literal .text) + } +} diff --git a/ld/testsuite/ld-xtensa/coalesce1.s b/ld/testsuite/ld-xtensa/coalesce1.s new file mode 100644 index 0000000..4374463 --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce1.s @@ -0,0 +1,15 @@ + .global foo + .data + .global g_name + .align 4 +g_name: + .word 0xffffffff + .text + .global main + .align 4 +main: + entry a5,16 + movi a5,20000 + movi a6,g_name + call8 foo + ret diff --git a/ld/testsuite/ld-xtensa/coalesce2.s b/ld/testsuite/ld-xtensa/coalesce2.s new file mode 100644 index 0000000..962915c --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce2.s @@ -0,0 +1,9 @@ + .text + .global foo + .global g_name +foo: + entry a5,16 + movi a5,20000 + movi a6,g_name + movi a7,50000 + ret diff --git a/ld/testsuite/ld-xtensa/lcall.exp b/ld/testsuite/ld-xtensa/lcall.exp new file mode 100644 index 0000000..9879a55 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall.exp @@ -0,0 +1,107 @@ +# Test Xtensa longcall optimization. +# By David Heine, Tensilica, Inc. +# Copyright 2002, 2003 +# Free Software Foundation, Inc. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +set testname "LCALL" + +set OBJDUMPFLAGS "-dr" + +# +# default_ld_objdump +# run objdump on a file +# +proc default_ld_objdump { objdump object outputfile } { + global OBJDUMPFLAGS + global objdump_output + global host_triplet + + if {[which $objdump] == 0} then { + perror "$objdump does not exist" + return 0 + } + + if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" } + + verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile" + + catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output + set exec_output [prune_warnings $exec_output] + if [string match "" $exec_output] then { + return 1 + } else { + verbose -log "$exec_output" + perror "$object: objdump failed" + return 0 + } +} + + +if ![ld_assemble $as $srcdir/$subdir/lcall1.s tmpdir/lcall1.o] { + unresolved $testname + return +} +if ![ld_assemble $as $srcdir/$subdir/lcall2.s tmpdir/lcall2.o] { + unresolved $testname + return +} + +set object "tmpdir/lcall" +set outputfile "$object.txt" + +if ![ld_simple_link $ld $object "-T $srcdir/$subdir/lcall.t tmpdir/lcall1.o tmpdir/lcall2.o"] { + verbose -log "failure in ld" + fail $testname + return +} + +if ![default_ld_objdump $objdump $object $outputfile ] { + verbose -log "failure in objdump" + fail $testname + return +} + +set file [open $outputfile r] +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "l32r" $line] { + verbose -log "Found an l32r in the linked object" + verbose -log "$line" + fail $testname + } +} +close $file +pass $testname + + +set testname "LCALL2" +set file [open $outputfile r] +set found 0 + +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "^00000004 :" $line] { + set found 1 + } +} +close $file +if $found { + pass $testname +} else { + fail $testname +} + diff --git a/ld/testsuite/ld-xtensa/lcall.t b/ld/testsuite/ld-xtensa/lcall.t new file mode 100644 index 0000000..7bff69f --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall.t @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x00000000 : { + *(.literal .text) + } +} diff --git a/ld/testsuite/ld-xtensa/lcall1.s b/ld/testsuite/ld-xtensa/lcall1.s new file mode 100644 index 0000000..1056c6a --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall1.s @@ -0,0 +1,12 @@ +.global foo +.text + .align 4 +label1: + .begin literal + .word 0xffffffff + .end literal + entry a5,16 +.begin longcalls + call4 foo +.end longcalls + nop diff --git a/ld/testsuite/ld-xtensa/lcall2.s b/ld/testsuite/ld-xtensa/lcall2.s new file mode 100644 index 0000000..f4784f0 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall2.s @@ -0,0 +1,5 @@ +.global foo +foo: + entry a5,16 + nop + ret -- cgit v1.1