+
+ /* See if we have a delay slot. */
+ if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+ {
+#define G0 0
+#define O7 15
+#define XCC (2 << 20)
+#define COND(x) (((x)&0xf)<<25)
+#define CONDA COND(0x8)
+#define INSN_BPA (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA (F2(0,2) | CONDA)
+#define INSN_OR F3(2, 0x2, 0)
+#define INSN_NOP F2(0,4)
+
+ long delay;
+
+ /* If the instruction is a call with either:
+ restore
+ arithmetic instruction with rd == %o7
+ where rs1 != %o7 and rs2 if it is register != %o7
+ then we can optimize if the call destination is near
+ by changing the call into a branch always. */
+ if (INSN_BIG_ENDIAN)
+ delay = bfd_getb32 ((unsigned char *) buf + 4);
+ else
+ delay = bfd_getl32 ((unsigned char *) buf + 4);
+ if ((insn & OP (~0)) != OP (1) || (delay & OP (~0)) != OP (2))
+ break;
+ if ((delay & OP3 (~0)) != OP3 (0x3d) /* Restore. */
+ && ((delay & OP3 (0x28)) != 0 /* Arithmetic. */
+ || ((delay & RD (~0)) != RD (O7))))
+ break;
+ if ((delay & RS1 (~0)) == RS1 (O7)
+ || ((delay & F3I (~0)) == 0
+ && (delay & RS2 (~0)) == RS2 (O7)))
+ break;
+ /* Ensure the branch will fit into simm22. */
+ if ((val & 0x3fe00000)
+ && (val & 0x3fe00000) != 0x3fe00000)
+ break;
+ /* Check if the arch is v9 and branch will fit
+ into simm19. */
+ if (((val & 0x3c0000) == 0
+ || (val & 0x3c0000) == 0x3c0000)
+ && (sparc_arch_size == 64
+ || current_architecture >= SPARC_OPCODE_ARCH_V9))
+ /* ba,pt %xcc */
+ insn = INSN_BPA | (val & 0x7ffff);
+ else
+ /* ba */
+ insn = INSN_BA | (val & 0x3fffff);
+ if (fixP->fx_where >= 4
+ && ((delay & (0xffffffff ^ RS1 (~0)))
+ == (INSN_OR | RD (O7) | RS2 (G0))))
+ {
+ long setter;
+ int reg;
+
+ if (INSN_BIG_ENDIAN)
+ setter = bfd_getb32 ((unsigned char *) buf - 4);
+ else
+ setter = bfd_getl32 ((unsigned char *) buf - 4);
+ if ((setter & (0xffffffff ^ RD (~0)))
+ != (INSN_OR | RS1 (O7) | RS2 (G0)))
+ break;
+ /* The sequence was
+ or %o7, %g0, %rN
+ call foo
+ or %rN, %g0, %o7
+
+ If call foo was replaced with ba, replace
+ or %rN, %g0, %o7 with nop. */
+ reg = (delay & RS1 (~0)) >> 14;
+ if (reg != ((setter & RD (~0)) >> 25)
+ || reg == G0 || reg == O7)
+ break;
+
+ if (INSN_BIG_ENDIAN)
+ bfd_putb32 (INSN_NOP, (unsigned char *) buf + 4);
+ else
+ bfd_putl32 (INSN_NOP, (unsigned char *) buf + 4);
+ }
+ }