This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Patch, avr] Relax LDS/STS to IN/OUT if symbol is in I/O address range


The patch below adds linker relaxation support for rewriting LDS/STS
instructions to IN/OUT where appropriate. The IN/OUT instruction is
shorter by a couple of bytes, and executes a cycle quicker. The 
compiler already does this optimization for addresses known at
compile time - this linker patch does it for the rest.

The patch looks for R_AVR_16 relocations, and if it finds an
LDS/STS instruction with the symbol value (i.e. address) in I/O range,
rewrites it to use IN/OUT instead, adjusting the address for SFR offsets.

The patch also includes a couple of test cases to verify that it works
for tiny, mega and xmega archs, and to ensure I/O range check is
implemented correctly.

If ok, could someone commit please? I don't have commit access.

Regards
Senthil

bfd/ChangeLog

2014-09-29  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>

	* elf32-avr.c (elf32_avr_relax_section): Look for R_AVR_16
	  relocs to rewrite LDS/STS to IN/OUT.
	  (try_relax_lds_sts_to_in_out): New function.

diff --git bfd/elf32-avr.c bfd/elf32-avr.c
index 54d67bf..ec5d042 100644
--- bfd/elf32-avr.c
+++ bfd/elf32-avr.c
@@ -1909,6 +1909,61 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
   return TRUE;
 }
 
+static
+bfd_boolean try_relax_lds_sts_to_in_out (bfd *abfd,
+                                        bfd_byte *contents,
+                                        bfd_vma symval,
+                                        Elf_Internal_Rela *irel)
+{
+  /* If there is less than 2 bytes before the reloc, then it cannot
+     be an LDS/STS */
+  if (irel->r_offset < 2)
+    return FALSE;
+
+  /* Read the opcode from the previous two bytes  and bail out if it
+     is not LDS/STS */
+  bfd_vma insn_msb = bfd_get_8 (abfd, contents + irel->r_offset - 1);
+  bfd_vma insn_lsb = bfd_get_8 (abfd, contents + irel->r_offset - 2);
+  bfd_boolean is_lds = ((0x90 == (insn_msb & 0xFE)) &&
+                        (0x0 == (insn_lsb & 0x0F)));
+  bfd_boolean is_sts = ((0x92 == (insn_msb & 0xFE)) &&
+                        (0x0 == (insn_lsb & 0x0F)));
+
+  if (!(is_lds || is_sts))
+    return FALSE;
+
+  /* megas have the IO address space at a 32 byte offset, and IN/OUT
+     instructions work in the IO address space - adjust for that. */
+  bfd_vma mem_address = symval + irel->r_addend;
+  bfd_boolean is_xmega = bfd_get_mach (abfd) >= bfd_mach_avrxmega1 &&
+                         bfd_get_mach(abfd) <= bfd_mach_avrxmega7;
+  bfd_vma sfr_offset = is_xmega ? 0 : 32;
+
+  bfd_vma reg = ((insn_msb & 0x01) << 4) | ((insn_lsb & 0xF0) >> 4);
+  bfd_vma address = mem_address - sfr_offset;
+
+  /* Rewrite the instruction if the address falls in the IO range
+     (0x0 - 0x3F, both inclusive) */
+  if (address > (bfd_vma)0x3F)
+    return FALSE;
+
+  /* Write out LSB */
+  bfd_put_8 (abfd,
+             ((reg & 0xF) << 4) | (address & 0xF),
+             contents + irel->r_offset - 2);
+
+  /* Compute and write out MSB */
+  bfd_vma msb = (0xB << 4);
+
+  if (is_sts)
+    msb |= (0x01 << 3);
+
+  msb |= ((reg >> 4) & 0x01) | (((address >> 4) & 0x03) << 1);
+  bfd_put_8 (abfd, msb, contents + irel->r_offset - 1);
+
+  return TRUE;
+}
+
 /* This function handles relaxing for the avr.
    Many important relaxing opportunities within functions are already
    realized by the compiler itself.
@@ -2028,7 +2083,8 @@ elf32_avr_relax_section (bfd *abfd,
 
       if (   ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL
 	  && ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL
-	  && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL)
+	  && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL
+      && ELF32_R_TYPE (irel->r_info) != R_AVR_16)
         continue;
 
       /* Get the section contents if we haven't done so already.  */
@@ -2104,6 +2160,29 @@ elf32_avr_relax_section (bfd *abfd,
          the linker is run.  */
       switch (ELF32_R_TYPE (irel->r_info))
         {
+          /* Try rewriting LDS/STS instructions to IN/OUT, after checking
+             whether the address falls with IN/OUT range. */
+          case R_AVR_16:
+            elf_section_data (sec)->relocs = internal_relocs;
+            elf_section_data (sec)->this_hdr.contents = contents;
+            symtab_hdr->contents = (unsigned char *) isymbuf;
+            if (try_relax_lds_sts_to_in_out (abfd,
+                                             contents,
+                                             symval,
+                                             irel))
+              {
+                /* The rewritten IN/OUT instruction takes the two bytes
+                   before the relocation, so delete the other two. */
+                if (!elf32_avr_relax_delete_bytes (abfd, sec,
+                                                   irel->r_offset, 2))
+                  goto error_return;
+
+                irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+                                               R_AVR_NONE);
+                *again = TRUE;
+                break;
+              }
+          break;
 	  /* Try to turn a 22-bit absolute call/jump into an 13-bit
 	     pc-relative rcall/rjmp.  */
 	case R_AVR_CALL:

ld/testsuite/ChangeLog

2014-09-29  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>

	* ld-avr/relax_lds_sts_mega.d: Match and verify 
	  LDS/STS to IN/OUT relaxation.
	* ld-avr/relax_lds_sts_tiny.d: Likewise.
	* ld-avr/relax_lds_sts_xmega.d: Likewise.
	* ld-avr/relax_lds_sts.s: New test.

diff --git ld/testsuite/ld-avr/relax_lds_sts.s ld/testsuite/ld-avr/relax_lds_sts.s
new file mode 100644
index 0000000..b802254
--- /dev/null
+++ ld/testsuite/ld-avr/relax_lds_sts.s
@@ -0,0 +1,9 @@
+.file "lds_sts_relax.s"
+.section    .text,"ax",@progbits
+main:
+    sts x, r30
+    lds r30, x
+    sts y, r30
+    lds r30, y
+    sts z, r30
+    lds r30, z
diff --git ld/testsuite/ld-avr/relax_lds_sts_mega.d ld/testsuite/ld-avr/relax_lds_sts_mega.d
new file mode 100644
index 0000000..1ecc2c4
--- /dev/null
+++ ld/testsuite/ld-avr/relax_lds_sts_mega.d
@@ -0,0 +1,11 @@
+#name: AVR Relax sts/lds into out/in for MEGA
+#as: -mmcu=avr5 -mlink-relax
+#ld:  -mavr5 --relax --defsym x=33 --defsym y=95 --defsym z=96
+#source: relax_lds_sts.s
+#objdump: -s
+#target: avr-*-*
+
+.*:     file format elf32-avr
+
+Contents of section .text:
+ 0000 e1b9e1b1 efbfefb7 e0936000 e0916000  .*
diff --git ld/testsuite/ld-avr/relax_lds_sts_tiny.d ld/testsuite/ld-avr/relax_lds_sts_tiny.d
new file mode 100644
index 0000000..fb82f76
--- /dev/null
+++ ld/testsuite/ld-avr/relax_lds_sts_tiny.d
@@ -0,0 +1,11 @@
+#name: AVR No relaxation of lds/sts for TINY
+#as: -mmcu=avrtiny -mlink-relax
+#ld:  -mavrtiny --relax --defsym x=0x40 --defsym y=0xB0 --defsym z=0xBF
+#source: relax_lds_sts.s
+#objdump: -s
+#target: avr-*-*
+
+.*:     file format elf32-avr
+
+Contents of section .text:
+ 0000 e0a9e0a1 e0aee0a6 efaeefa6           .*
diff --git ld/testsuite/ld-avr/relax_lds_sts_xmega.d ld/testsuite/ld-avr/relax_lds_sts_xmega.d
new file mode 100644
index 0000000..f9cfdc2
--- /dev/null
+++ ld/testsuite/ld-avr/relax_lds_sts_xmega.d
@@ -0,0 +1,11 @@
+#name: AVR Relax sts/lds into out/in for XMEGA
+#as: -mmcu=avrxmega2 -mlink-relax
+#ld:  -mavrxmega2 --relax --defsym x=1 --defsym y=63 --defsym z=64
+#source: relax_lds_sts.s
+#objdump: -s
+#target: avr-*-*
+
+.*:     file format elf32-avr
+
+Contents of section .text:
+ 0000 e1b9e1b1 efbfefb7 e0934000 e0914000  .*


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]