Fix PR 24571 - Relaxation does not shorten jmp or call to target at pc-relative...
authorSenthil Kumar Selvaraj <senthilkumar.selvaraj@microchip.com>
Thu, 16 May 2019 06:42:33 +0000 (12:12 +0530)
committerSenthil Kumar Selvaraj <senthilkumar.selvaraj@microchip.com>
Tue, 21 May 2019 07:18:06 +0000 (12:48 +0530)
The range check done to transform an absolute call/jump to a pc-relative one is
off-by-one, and that causes this shortening optimization to be missed if the
branch target is right at the range boundary.

In the non-shrinkable case, the range is what is mentioned in the ISA - -4094
bytes in the backward direction, and 4096 bytes in the positive direction.

In the shrinkable case, the forward jump range increases by two bytes (deleted
because of the shortening from call/jmp to rcall/rjmp), and therefore, the
range is -4094 in the reverse, and 4098 in the positive direction.

Fix the ranges for !shrinkable and shrinkable cases, and add a test caes to
ensure jumps to max forward and backward ranges get relaxed to rjmp.

bfd/ChangeLog
bfd/elf32-avr.c
ld/ChangeLog
ld/testsuite/ld-avr/relax-insn-at-range-boundary.d [new file with mode: 0644]
ld/testsuite/ld-avr/relax-insn-at-range-boundary.s [new file with mode: 0644]

index 0d7eda4f7089aefdb7d85046c46fd05337599518..7d22cc03d3edd662c99413e1b5e30820d47beca1 100644 (file)
@@ -1,3 +1,9 @@
+2019-05-21  Senthil Kumar Selvaraj  <senthilkumar.selvaraj@microchip.com>
+
+       PR ld/24571
+       * bfd/elf32-avr.c (elf32_avr_relax_section): Adjust range check
+       when computing distance_short_enough.
+
 2019-05-21  Senthil Kumar Selvaraj  <senthilkumar.selvaraj@microchip.com>
 
        PR ld/24564
index f8a843e16c8c5a55a49233db88cf2cb4ff038c96..34ad42300dd9383ac681009dd9d4a6de3f9a2876 100644 (file)
@@ -2643,16 +2643,28 @@ elf32_avr_relax_section (bfd *abfd,
            /* Compute the distance from this insn to the branch target.  */
            gap = value - dot;
 
+           /* The ISA manual states that addressable range is PC - 2k + 1 to
+              PC + 2k. In bytes, that would be -4094 <= PC <= 4096. The range
+              is shifted one word to the right, because pc-relative instructions
+              implicitly add one word i.e. rjmp 0 jumps to next insn, not the
+              current one.
+              Therefore, for the !shrinkable case, the range is as above.
+              If shrinkable, then the current code only deletes bytes 3 and
+              4 of the absolute call/jmp, so the forward jump range increases
+              by 2 bytes, but the backward (negative) jump range remains
+              the same. */
+
+
            /* Check if the gap falls in the range that can be accommodated
               in 13bits signed (It is 12bits when encoded, as we deal with
               word addressing). */
-           if (!shrinkable && ((int) gap >= -4096 && (int) gap <= 4095))
+           if (!shrinkable && ((int) gap >= -4094 && (int) gap <= 4096))
              distance_short_enough = 1;
            /* If shrinkable, then we can check for a range of distance which
-              is two bytes farther on both the directions because the call
+              is two bytes farther on the positive direction because the call
               or jump target will be closer by two bytes after the
               relaxation. */
-           else if (shrinkable && ((int) gap >= -4094 && (int) gap <= 4097))
+           else if (shrinkable && ((int) gap >= -4094 && (int) gap <= 4098))
              distance_short_enough = 1;
 
            /* Here we handle the wrap-around case.  E.g. for a 16k device
index 20be135c6e03e4406739c73e33172392729730a9..e038e400333b9c2128da4aaa9578d67e908a7ce1 100644 (file)
@@ -1,3 +1,9 @@
+2019-05-21  Senthil Kumar Selvaraj  <senthilkumar.selvaraj@microchip.com>
+
+       PR ld/24571
+       * ld/testsuite/ld-avr/relax-insn-at-range-boundary.d: New test.
+       * ld/testsuite/ld-avr/relax-insn-at-range-boundary.s: New test.
+
 2019-05-21  Senthil Kumar Selvaraj  <senthilkumar.selvaraj@microchip.com>
 
        PR ld/24564
diff --git a/ld/testsuite/ld-avr/relax-insn-at-range-boundary.d b/ld/testsuite/ld-avr/relax-insn-at-range-boundary.d
new file mode 100644 (file)
index 0000000..6183896
--- /dev/null
@@ -0,0 +1,17 @@
+#name: AVR relaxation, jump to symbol at ends of pc-relative range boundary
+#as: -mlink-relax -mavr51
+#ld: --relax -mavr51
+#source: relax-insn-at-range-boundary.s
+#objdump: -d
+#target: avr-*-*
+
+#...
+00000000.*
+       ...
+     ffc:      00 00           nop
+     ffe:      00 c8           rjmp    .-4096          ; 0x0 .*
+    1000:      ff c7           rjmp    .+4094          ; 0x2000 <forward_target>
+       ...
+
+00002000 <forward_target>:
+#...
diff --git a/ld/testsuite/ld-avr/relax-insn-at-range-boundary.s b/ld/testsuite/ld-avr/relax-insn-at-range-boundary.s
new file mode 100644 (file)
index 0000000..4d710df
--- /dev/null
@@ -0,0 +1,11 @@
+.section ".text", "ax",@progbits
+.global main
+main:
+backward_target:                ; Exactly -4094 bytes from jmp
+  .ds.b 4094, 0
+  jmp backward_target
+  jmp forward_target
+  .ds.b 4094, 0
+forward_target:                 ; Exactly 4098 bytes before relax, 4096 bytes after relax
+  nop
+