This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[Patch, avr] Relax LDS/STS to IN/OUT if symbol is in I/O address range
- From: Senthil Kumar Selvaraj <senthil_kumar dot selvaraj at atmel dot com>
- To: <binutils at sourceware dot org>
- Cc: <chertykov at gmail dot com>, <gjl at gcc dot gnu dot org>
- Date: Mon, 29 Sep 2014 16:15:46 +0530
- Subject: [Patch, avr] Relax LDS/STS to IN/OUT if symbol is in I/O address range
- Authentication-results: sourceware.org; auth=none
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 .*