From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23553 invoked by alias); 23 Feb 2011 13:39:07 -0000 Received: (qmail 23538 invoked by uid 22791); 23 Feb 2011 13:39:01 -0000 X-SWARE-Spam-Status: No, hits=-1.7 required=5.0 tests=AWL,BAYES_00,TW_XF,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 23 Feb 2011 13:38:52 +0000 Received: (qmail 3909 invoked from network); 23 Feb 2011 13:38:49 -0000 Received: from unknown (HELO tp.orcam.me.uk) (macro@127.0.0.2) by mail.codesourcery.com with ESMTPA; 23 Feb 2011 13:38:49 -0000 Date: Wed, 23 Feb 2011 13:39:00 -0000 From: "Maciej W. Rozycki" To: binutils@sourceware.org cc: Richard Sandiford Subject: [PATCH] MIPS/GAS: Fix DWARF-2 with branch swapping for MIPS16 code Message-ID: User-Agent: Alpine 1.10 (DEB 962 2008-03-14) MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Mailing-List: contact binutils-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: binutils-owner@sourceware.org X-SW-Source: 2011-02/txt/msg00280.txt.bz2 Hi, We have a long-standing problem with branch swapping in MIPS16 code. Consider the following real piece, extracted from step-test.c in the GDB test suite: sw $2,36($17) .loc 1 45 0 jal callee As you can see there's DWARF-2 line information associated with the procedure call instruction. As the SW instruction is 16-bit, GAS decides to reorder it into the JAL's delay slot. The resulting binary code looks like this: 60: 1800 0000 jal 0 60: R_MIPS16_26 callee 64: d940 sw v0,0(s1) but line #45 is recorded at its instruction's original location before the swapping took place i.e. 0x62. When such a program is being debugged and a breakpoint is requested at line #45, then the debugger faithfully places it at 0x62, targetting the second half of the JAL instruction and when control reaches this address, then the breakpoint is either missed (if hardware breakpoints are used, either explicitly or by the debug stub, because no address match happens) or the program goes astray (if software breakpoints are used, because JAL's immediate argument has been corrupted by the breakpoint instruction). Many years ago I developed a simple workaround for this issue that disabled branch swapping for MIPS16 code altogether. Now when I got back to it because of the microMIPS effort I have decided the old workaround is too lame to keep it alive any longer. With some shuffling of code within append_insn() I have now made the function produce DWARF-2 line information modified such that if a branch is swapped, then the corresponding location record is updated to point to the new location of the branch. This change has been successfully regression-tested for the mips-sde-elf and mips-linux-gnu targets and also fixes several mips-linux-gnu MIPS16 GDB test suite failures, e.g.: (gdb) PASS: gdb.base/auxv.exp: tbreak 64 continue Continuing. -Program received signal SIGSEGV, Segmentation fault. -0x0043a294 in ?? () -(gdb) FAIL: gdb.base/auxv.exp: continue +Temporary breakpoint 2, func2 (x=20) at [...]/gdb/testsuite/gdb.base/auxv.c:64 +64 ABORT; +(gdb) PASS: gdb.base/auxv.exp: continue info auxv 16 AT_HWCAP Machine-dependent CPU capability hints 0x0 6 AT_PAGESZ System page size 4096 Notice that (0x0043a294 >> 2) & 0xffff is 0xe8a5 which is the encoding of the MIPS16 BREAK 5 instruction. Some GDB regressions from the simple workaround still remain, because some test cases rely on stopping between a given instruction and the following branch. That unfortunately may never happen if the two instructions have been swapped, no matter what we do about line information, so these failures have to stay for the time being as shortcomings of the respective test cases. Or actually whenever the -g compilation option is used, then GCC should either use the noreorder assembly mode and schedule delay slots itself such that line boundaries are preserved or otherwise supply -O1 to GAS to override its default of -O2 and disable branch swapping, at least at GCC's -O0 optimisation level (this has a potential drawback of the toolchain producing different code depending on whether -g has or hasn't been specified, so perhaps -O1 should be passed to GAS unconditionally whenever -O0 has been selected for GCC, hmm...). While at it, I think this DWARF-2 line information is not correctly recorded for code generated as a result of fix_loongson2f_jump() being triggered. I think the call to fix_loongson2f_jump() should be moved to after the call to dwarf2_emit_insn(). This observation applies regardless of the change considered here. 2011-02-23 Maciej W. Rozycki gas/ * config/tc-mips.c (append_insn): Make sure DWARF-2 location information is properly adjusted for branches that get swapped. gas/testsuite/ * gas/mips/loc-swap.d: New test case for DWARF-2 location with branch swapping. * gas/mips/loc-swap-dis.d: Likewise. * gas/mips/mips16\@loc-swap.d: Likewise, MIPS16 version. * gas/mips/mips16\@loc-swap-dis.d: Likewise. * gas/mips/loc-swap.s: Source for the new tests. * gas/mips/mips.exp: Run the new tests. OK to apply? Maciej binutils-gas-mips-branch-swap-dwarf2.diff Index: binutils-fsf-trunk-quilt/gas/config/tc-mips.c =================================================================== --- binutils-fsf-trunk-quilt.orig/gas/config/tc-mips.c 2011-02-22 16:49:48.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/config/tc-mips.c 2011-02-22 21:59:08.000000000 +0000 @@ -2814,9 +2814,10 @@ append_insn (struct mips_cl_insn *ip, ex { unsigned long prev_pinfo, pinfo; unsigned long prev_pinfo2, pinfo2; - relax_stateT prev_insn_frag_type = 0; bfd_boolean relaxed_branch = FALSE; segment_info_type *si = seg_info (now_seg); + bfd_boolean branch_swap = FALSE; + int branch_disp = 0; if (mips_fix_loongson2f) fix_loongson2f (ip); @@ -2905,18 +2906,219 @@ append_insn (struct mips_cl_insn *ip, ex } } + /* Update the register mask information. */ + if (! mips_opts.mips16) + { + if ((pinfo & INSN_WRITE_GPR_D) || (pinfo2 & INSN2_READ_GPR_D)) + mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip); + if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0) + mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip); + if (pinfo & INSN_READ_GPR_S) + mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip); + if (pinfo & INSN_WRITE_GPR_31) + mips_gprmask |= 1 << RA; + if (pinfo2 & (INSN2_WRITE_GPR_Z | INSN2_READ_GPR_Z)) + mips_gprmask |= 1 << EXTRACT_OPERAND (RZ, *ip); + if (pinfo & INSN_WRITE_FPR_D) + mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip); + if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0) + mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip); + if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) + mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip); + if ((pinfo & INSN_READ_FPR_R) != 0) + mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip); + if (pinfo2 & (INSN2_WRITE_FPR_Z | INSN2_READ_FPR_Z)) + mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FZ, *ip); + if (pinfo & INSN_COP) + { + /* We don't keep enough information to sort these cases out. + The itbl support does keep this information however, although + we currently don't support itbl fprmats as part of the cop + instruction. May want to add this support in the future. */ + } + /* Never set the bit for $0, which is always zero. */ + mips_gprmask &= ~1 << 0; + } + else + { + if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X)) + mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip); + if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y)) + mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip); + if (pinfo & MIPS16_INSN_WRITE_Z) + mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip); + if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T)) + mips_gprmask |= 1 << TREG; + if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP)) + mips_gprmask |= 1 << SP; + if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31)) + mips_gprmask |= 1 << RA; + if (pinfo & MIPS16_INSN_WRITE_GPR_Y) + mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode); + if (pinfo & MIPS16_INSN_READ_Z) + mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip); + if (pinfo & MIPS16_INSN_READ_GPR_X) + mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip); + } + + /* Filling the branch delay slot is more complex. We try to switch + the branch with the previous instruction, which we can do if the + previous instruction does not set up a condition that the branch + tests and if the branch is not itself the target of any branch. + We don't yet optimize branch-likely instructions though.*/ + if (mips_relax.sequence != 2 + && !mips_opts.noreorder + && ((pinfo & INSN_UNCOND_BRANCH_DELAY) + || (pinfo & INSN_COND_BRANCH_DELAY)) + && mips_optimize >= 2 + /* If we have seen .set volatile or .set nomove, don't optimize. */ + && mips_opts.nomove == 0 + /* We can't swap if the previous instruction's position is fixed. */ + && !history[0].fixed_p + /* If the previous previous insn was in a .set noreorder, we can't + swap. Actually, the MIPS assembler will swap in this situation. + However, gcc configured -with-gnu-as will generate code like: + + .set noreorder + lw $4,XXX + .set reorder + INSN + bne $4,$0,foo + + in which we cannot swap the bne and INSN. If gcc is not + configured -with-gnu-as, it does not output the .set pseudo-ops. */ + && !history[1].noreorder_p + /* If the branch is itself the target of a branch, we cannot swap. + We cheat on this; all we check for is whether there is a label + on this instruction. If there are any branches to anything other + than a label, users must use .set noreorder. */ + && si->label_list == NULL + /* If the previous instruction is in a variant frag other than this + branch's one, we cannot do the swap. This does not apply to + MIPS16 code, which uses variant frags for different purposes. */ + && (mips_opts.mips16 || history[0].frag->fr_type != rs_machine_dependent) + /* Check for conflicts between the branch and the instructions + before the candidate delay slot. */ + && nops_for_insn (history + 1, ip) == 0 + /* Check for conflicts between the swapped sequence and the target + of the branch. */ + && nops_for_sequence (2, history + 1, ip, history) == 0 + /* We do not swap with a trap instruction, since it complicates + trap handlers to have the trap instruction be in a delay slot. */ + && !(prev_pinfo & INSN_TRAP) + /* If the branch reads a register that the previous instruction + sets, we cannot swap. */ + && (mips_opts.mips16 + || !(prev_pinfo & INSN_WRITE_GPR_T) + || !insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]), + MIPS_GR_REG)) + && (mips_opts.mips16 + || !(prev_pinfo & INSN_WRITE_GPR_D) + || !insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]), + MIPS_GR_REG)) + && (mips_opts.mips16 + || !(prev_pinfo2 & INSN2_WRITE_GPR_Z) + || !insn_uses_reg (ip, EXTRACT_OPERAND (RZ, history[0]), + MIPS_GR_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_X) + || !insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]), + MIPS16_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_Y) + || !insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]), + MIPS16_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_Z) + || !insn_uses_reg (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]), + MIPS16_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_T) + || !insn_uses_reg (ip, TREG, MIPS_GR_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_31) + || !insn_uses_reg (ip, RA, MIPS_GR_REG)) + && (!mips_opts.mips16 + || !(prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) + || !insn_uses_reg (ip, + MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode), + MIPS_GR_REG)) + /* If the branch writes a register that the previous instruction + sets, we cannot swap (we know that branches write only to RD + or to $31). */ + && (mips_opts.mips16 + || !(prev_pinfo & INSN_WRITE_GPR_T) + || ((!(pinfo & INSN_WRITE_GPR_D) + || (EXTRACT_OPERAND (RT, history[0]) + != EXTRACT_OPERAND (RD, *ip))) + && (!(pinfo & INSN_WRITE_GPR_31) + || EXTRACT_OPERAND (RT, history[0]) != RA))) + && (mips_opts.mips16 + || !(prev_pinfo & INSN_WRITE_GPR_D) + || ((!(pinfo & INSN_WRITE_GPR_D) + || (EXTRACT_OPERAND (RD, history[0]) + != EXTRACT_OPERAND (RD, *ip))) + && (!(pinfo & INSN_WRITE_GPR_31) + || EXTRACT_OPERAND (RD, history[0]) != RA))) + && (!mips_opts.mips16 + || !(pinfo & MIPS16_INSN_WRITE_31) + || (!(prev_pinfo & MIPS16_INSN_WRITE_31) + && (!(prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) + || MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode) != RA))) + /* If the branch writes a register that the previous instruction + reads, we cannot swap (we know that branches only write to RD + or to $31). */ + && (mips_opts.mips16 + || !(pinfo & INSN_WRITE_GPR_D) + || !insn_uses_reg (&history[0], EXTRACT_OPERAND (RD, *ip), + MIPS_GR_REG)) + && (mips_opts.mips16 + || !(pinfo & INSN_WRITE_GPR_31) + || !insn_uses_reg (&history[0], RA, MIPS_GR_REG)) + && (!mips_opts.mips16 + || !(pinfo & MIPS16_INSN_WRITE_31) + || !insn_uses_reg (&history[0], RA, MIPS_GR_REG)) + /* If one instruction sets a condition code and the other one uses + a condition code, we cannot swap. */ + && !((pinfo & INSN_READ_COND_CODE) + && (prev_pinfo & INSN_WRITE_COND_CODE)) + && !((pinfo & INSN_WRITE_COND_CODE) + && (prev_pinfo & INSN_READ_COND_CODE)) + /* If the previous instruction uses the PC, we cannot swap. */ + && !(mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC)) + /* If the previous instruction had a fixup in mips16 mode, we + cannot swap. This normally means that the previous + instruction was a 4 byte branch anyhow. */ + && !(mips_opts.mips16 && history[0].fixp[0]) + /* If the previous instruction is a sync, sync.l, or sync.p, + we cannot swap. */ + && !(prev_pinfo & INSN_SYNC) + /* If the previous instruction is an ERET or DERET, avoid the swap. */ + && history[0].insn_opcode != INSN_ERET + && history[0].insn_opcode != INSN_DERET) + { + /* It looks like we can actually do the swap. */ + branch_disp = insn_length (history); + branch_swap = TRUE; + } + #ifdef OBJ_ELF /* The value passed to dwarf2_emit_insn is the distance between the beginning of the current instruction and the address that - should be recorded in the debug tables. For MIPS16 debug info - we want to use ISA-encoded addresses, so we pass -1 for an - address higher by one than the current. */ - dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0); -#endif + should be recorded in the debug tables. This is normally the + current address. - /* Record the frag type before frag_var. */ - if (history[0].frag) - prev_insn_frag_type = history[0].frag->fr_type; + For MIPS16 debug info we want to use ISA-encoded addresses, + so we use -1 for an address higher by one than the current one. + + If the instruction produced is a branch that we will swap with + the preceding instruction, then we add the displacement by which + the branch will be moved backwards. This is more appropriate + and for MIPS16 code also prevents a debugger from placing a + breakpoint in the middle of the branch (and corrupting code if + software breakpoints are used). */ + dwarf2_emit_insn ((mips_opts.mips16 ? -1 : 0) + branch_disp); +#endif if (address_expr && *reloc_type == BFD_RELOC_16_PCREL_S2 @@ -3166,234 +3368,14 @@ append_insn (struct mips_cl_insn *ip, ex } install_insn (ip); - /* Update the register mask information. */ - if (! mips_opts.mips16) - { - if ((pinfo & INSN_WRITE_GPR_D) || (pinfo2 & INSN2_READ_GPR_D)) - mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip); - if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0) - mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip); - if (pinfo & INSN_READ_GPR_S) - mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip); - if (pinfo & INSN_WRITE_GPR_31) - mips_gprmask |= 1 << RA; - if (pinfo2 & (INSN2_WRITE_GPR_Z | INSN2_READ_GPR_Z)) - mips_gprmask |= 1 << EXTRACT_OPERAND (RZ, *ip); - if (pinfo & INSN_WRITE_FPR_D) - mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip); - if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0) - mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip); - if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) - mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip); - if ((pinfo & INSN_READ_FPR_R) != 0) - mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip); - if (pinfo2 & (INSN2_WRITE_FPR_Z | INSN2_READ_FPR_Z)) - mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FZ, *ip); - if (pinfo & INSN_COP) - { - /* We don't keep enough information to sort these cases out. - The itbl support does keep this information however, although - we currently don't support itbl fprmats as part of the cop - instruction. May want to add this support in the future. */ - } - /* Never set the bit for $0, which is always zero. */ - mips_gprmask &= ~1 << 0; - } - else - { - if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X)) - mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip); - if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y)) - mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip); - if (pinfo & MIPS16_INSN_WRITE_Z) - mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip); - if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T)) - mips_gprmask |= 1 << TREG; - if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP)) - mips_gprmask |= 1 << SP; - if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31)) - mips_gprmask |= 1 << RA; - if (pinfo & MIPS16_INSN_WRITE_GPR_Y) - mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode); - if (pinfo & MIPS16_INSN_READ_Z) - mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip); - if (pinfo & MIPS16_INSN_READ_GPR_X) - mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip); - } - if (mips_relax.sequence != 2 && !mips_opts.noreorder) { - /* Filling the branch delay slot is more complex. We try to - switch the branch with the previous instruction, which we can - do if the previous instruction does not set up a condition - that the branch tests and if the branch is not itself the - target of any branch. */ if ((pinfo & INSN_UNCOND_BRANCH_DELAY) || (pinfo & INSN_COND_BRANCH_DELAY)) { - if (mips_optimize < 2 - /* If we have seen .set volatile or .set nomove, don't - optimize. */ - || mips_opts.nomove != 0 - /* We can't swap if the previous instruction's position - is fixed. */ - || history[0].fixed_p - /* If the previous previous insn was in a .set - noreorder, we can't swap. Actually, the MIPS - assembler will swap in this situation. However, gcc - configured -with-gnu-as will generate code like - .set noreorder - lw $4,XXX - .set reorder - INSN - bne $4,$0,foo - in which we can not swap the bne and INSN. If gcc is - not configured -with-gnu-as, it does not output the - .set pseudo-ops. */ - || history[1].noreorder_p - /* If the branch is itself the target of a branch, we - can not swap. We cheat on this; all we check for is - whether there is a label on this instruction. If - there are any branches to anything other than a - label, users must use .set noreorder. */ - || si->label_list != NULL - /* If the previous instruction is in a variant frag - other than this branch's one, we cannot do the swap. - This does not apply to the mips16, which uses variant - frags for different purposes. */ - || (! mips_opts.mips16 - && prev_insn_frag_type == rs_machine_dependent) - /* Check for conflicts between the branch and the instructions - before the candidate delay slot. */ - || nops_for_insn (history + 1, ip) > 0 - /* Check for conflicts between the swapped sequence and the - target of the branch. */ - || nops_for_sequence (2, history + 1, ip, history) > 0 - /* We do not swap with a trap instruction, since it - complicates trap handlers to have the trap - instruction be in a delay slot. */ - || (prev_pinfo & INSN_TRAP) - /* If the branch reads a register that the previous - instruction sets, we can not swap. */ - || (! mips_opts.mips16 - && (prev_pinfo & INSN_WRITE_GPR_T) - && insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]), - MIPS_GR_REG)) - || (! mips_opts.mips16 - && (prev_pinfo & INSN_WRITE_GPR_D) - && insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]), - MIPS_GR_REG)) - || (! mips_opts.mips16 - && (prev_pinfo2 & INSN2_WRITE_GPR_Z) - && insn_uses_reg (ip, EXTRACT_OPERAND (RZ, history[0]), - MIPS_GR_REG)) - || (mips_opts.mips16 - && (((prev_pinfo & MIPS16_INSN_WRITE_X) - && (insn_uses_reg - (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]), - MIPS16_REG))) - || ((prev_pinfo & MIPS16_INSN_WRITE_Y) - && (insn_uses_reg - (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]), - MIPS16_REG))) - || ((prev_pinfo & MIPS16_INSN_WRITE_Z) - && (insn_uses_reg - (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]), - MIPS16_REG))) - || ((prev_pinfo & MIPS16_INSN_WRITE_T) - && insn_uses_reg (ip, TREG, MIPS_GR_REG)) - || ((prev_pinfo & MIPS16_INSN_WRITE_31) - && insn_uses_reg (ip, RA, MIPS_GR_REG)) - || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) - && insn_uses_reg (ip, - MIPS16OP_EXTRACT_REG32R - (history[0].insn_opcode), - MIPS_GR_REG)))) - /* If the branch writes a register that the previous - instruction sets, we can not swap (we know that - branches write only to RD or to $31). */ - || (! mips_opts.mips16 - && (prev_pinfo & INSN_WRITE_GPR_T) - && (((pinfo & INSN_WRITE_GPR_D) - && (EXTRACT_OPERAND (RT, history[0]) - == EXTRACT_OPERAND (RD, *ip))) - || ((pinfo & INSN_WRITE_GPR_31) - && EXTRACT_OPERAND (RT, history[0]) == RA))) - || (! mips_opts.mips16 - && (prev_pinfo & INSN_WRITE_GPR_D) - && (((pinfo & INSN_WRITE_GPR_D) - && (EXTRACT_OPERAND (RD, history[0]) - == EXTRACT_OPERAND (RD, *ip))) - || ((pinfo & INSN_WRITE_GPR_31) - && EXTRACT_OPERAND (RD, history[0]) == RA))) - || (mips_opts.mips16 - && (pinfo & MIPS16_INSN_WRITE_31) - && ((prev_pinfo & MIPS16_INSN_WRITE_31) - || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) - && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode) - == RA)))) - /* If the branch writes a register that the previous - instruction reads, we can not swap (we know that - branches only write to RD or to $31). */ - || (! mips_opts.mips16 - && (pinfo & INSN_WRITE_GPR_D) - && insn_uses_reg (&history[0], - EXTRACT_OPERAND (RD, *ip), - MIPS_GR_REG)) - || (! mips_opts.mips16 - && (pinfo & INSN_WRITE_GPR_31) - && insn_uses_reg (&history[0], RA, MIPS_GR_REG)) - || (mips_opts.mips16 - && (pinfo & MIPS16_INSN_WRITE_31) - && insn_uses_reg (&history[0], RA, MIPS_GR_REG)) - /* If one instruction sets a condition code and the - other one uses a condition code, we can not swap. */ - || ((pinfo & INSN_READ_COND_CODE) - && (prev_pinfo & INSN_WRITE_COND_CODE)) - || ((pinfo & INSN_WRITE_COND_CODE) - && (prev_pinfo & INSN_READ_COND_CODE)) - /* If the previous instruction uses the PC, we can not - swap. */ - || (mips_opts.mips16 - && (prev_pinfo & MIPS16_INSN_READ_PC)) - /* If the previous instruction had a fixup in mips16 - mode, we can not swap. This normally means that the - previous instruction was a 4 byte branch anyhow. */ - || (mips_opts.mips16 && history[0].fixp[0]) - /* If the previous instruction is a sync, sync.l, or - sync.p, we can not swap. */ - || (prev_pinfo & INSN_SYNC) - /* If the previous instruction is an ERET or - DERET, avoid the swap. */ - || (history[0].insn_opcode == INSN_ERET) - || (history[0].insn_opcode == INSN_DERET)) - { - if (mips_opts.mips16 - && (pinfo & INSN_UNCOND_BRANCH_DELAY) - && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)) - && ISA_SUPPORTS_MIPS16E) - { - /* Convert MIPS16 jr/jalr into a "compact" jump. */ - ip->insn_opcode |= 0x0080; - install_insn (ip); - insert_into_history (0, 1, ip); - } - else - { - /* We could do even better for unconditional branches to - portions of this object file; we could pick up the - instruction at the destination, put it in the delay - slot, and bump the destination address. */ - insert_into_history (0, 1, ip); - emit_nop (); - } - - if (mips_relax.sequence) - mips_relax.sizes[mips_relax.sequence - 1] += 4; - } - else + if (branch_swap) { - /* It looks like we can actually do the swap. */ + /* Do the swap now. */ struct mips_cl_insn delay = history[0]; if (mips_opts.mips16) { @@ -3421,6 +3403,31 @@ append_insn (struct mips_cl_insn *ip, ex delay.fixed_p = 1; insert_into_history (0, 1, &delay); } + else + { + if (mips_opts.mips16 + && (pinfo & INSN_UNCOND_BRANCH_DELAY) + && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)) + && ISA_SUPPORTS_MIPS16E) + { + /* Convert MIPS16 jr/jalr into a "compact" jump. */ + ip->insn_opcode |= 0x0080; + install_insn (ip); + insert_into_history (0, 1, ip); + } + else + { + /* We could do even better for unconditional branches to + portions of this object file; we could pick up the + instruction at the destination, put it in the delay + slot, and bump the destination address. */ + insert_into_history (0, 1, ip); + emit_nop (); + } + + if (mips_relax.sequence) + mips_relax.sizes[mips_relax.sequence - 1] += 4; + } /* If that was an unconditional branch, forget the previous insn information. */ Index: binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/loc-swap.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/loc-swap.d 2011-02-23 00:17:05.000000000 +0000 @@ -0,0 +1,61 @@ +#PROG: readelf +#readelf: -wl +#name: MIPS DWARF-2 location information with branch swapping +#as: -32 +#source: loc-swap.s + +# Verify that DWARF-2 location information for instructions reordered +# into a branch delay slot is updated to point to the branch instead. + +Raw dump of debug contents of section \.debug_line: + + Offset: 0x0 + Length: 67 + DWARF Version: 2 + Prologue Length: 33 + Minimum Instruction Length: 1 + Initial value of 'is_stmt': 1 + Line Base: -5 + Line Range: 14 + Opcode Base: 13 + + Opcodes: + Opcode 1 has 0 args + Opcode 2 has 1 args + Opcode 3 has 1 args + Opcode 4 has 1 args + Opcode 5 has 1 args + Opcode 6 has 0 args + Opcode 7 has 0 args + Opcode 8 has 0 args + Opcode 9 has 1 args + Opcode 10 has 0 args + Opcode 11 has 0 args + Opcode 12 has 1 args + + The Directory Table is empty\. + + The File Name Table: + Entry Dir Time Size Name + 1 0 0 0 loc-swap\.s + + Line Number Statements: + Extended opcode 2: set Address to 0x0 + Special opcode 11: advance Address by 0 to 0x0 and Line by 6 to 7 + Special opcode 63: advance Address by 4 to 0x4 and Line by 2 to 9 + Special opcode 120: advance Address by 8 to 0xc and Line by 3 to 12 + Special opcode 7: advance Address by 0 to 0xc and Line by 2 to 14 + Special opcode 120: advance Address by 8 to 0x14 and Line by 3 to 17 + Special opcode 7: advance Address by 0 to 0x14 and Line by 2 to 19 + Special opcode 120: advance Address by 8 to 0x1c and Line by 3 to 22 + Special opcode 63: advance Address by 4 to 0x20 and Line by 2 to 24 + Special opcode 120: advance Address by 8 to 0x28 and Line by 3 to 27 + Special opcode 63: advance Address by 4 to 0x2c and Line by 2 to 29 + Special opcode 120: advance Address by 8 to 0x34 and Line by 3 to 32 + Special opcode 63: advance Address by 4 to 0x38 and Line by 2 to 34 + Special opcode 120: advance Address by 8 to 0x40 and Line by 3 to 37 + Special opcode 7: advance Address by 0 to 0x40 and Line by 2 to 39 + Special opcode 120: advance Address by 8 to 0x48 and Line by 3 to 42 + Special opcode 63: advance Address by 4 to 0x4c and Line by 2 to 44 + Advance PC by 16 to 0x5c + Extended opcode 1: End of Sequence Index: binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/loc-swap.s =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/loc-swap.s 2011-02-23 00:52:51.000000000 +0000 @@ -0,0 +1,48 @@ +# Source file to test DWARF-2 location information with branch swapping. + + .file 1 "loc-swap.s" + .text +foo: + .loc 1 7 + move $4, $16 + .loc 1 9 + jr $4 + + .loc 1 12 + move $31, $16 + .loc 1 14 + jr $4 + + .loc 1 17 + move $4, $16 + .loc 1 19 + jr $31 + + .loc 1 22 + move $31, $16 + .loc 1 24 + jr $31 + + .loc 1 27 + move $4, $16 + .loc 1 29 + jalr $4 + + .loc 1 32 + move $31, $16 + .loc 1 34 + jalr $4 + + .loc 1 37 + move $4, $16 + .loc 1 39 + jal bar + + .loc 1 42 + move $31, $16 + .loc 1 44 + jal bar + +# Force at least 8 (non-delay-slot) zero bytes, to make 'objdump' print ... + .align 2 + .space 16 Index: binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips.exp =================================================================== --- binutils-fsf-trunk-quilt.orig/gas/testsuite/gas/mips/mips.exp 2011-02-22 02:17:20.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips.exp 2011-02-23 00:24:25.000000000 +0000 @@ -831,6 +831,10 @@ if { [istarget mips*-*-vxworks*] } { run_dump_test "jalr2" run_dump_test_arches "aent" [mips_arch_list_matching mips1] + + run_dump_test_arches "loc-swap" [mips_arch_list_all] + run_dump_test_arches "loc-swap-dis" \ + [mips_arch_list_all] } if $has_newabi { Index: binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips16@loc-swap-dis.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips16@loc-swap-dis.d 2011-02-22 23:51:06.000000000 +0000 @@ -0,0 +1,35 @@ +#objdump: -dr --prefix-addresses --show-raw-insn +#name: MIPS DWARF-2 location information with branch swapping disassembly +#as: -32 +#source: loc-swap.s + +# Check branch swapping with DWARF-2 location information (MIPS16). + +.*: +file format .*mips.* + +Disassembly of section \.text: +[0-9a-f]+ <[^>]*> 6790 move a0,s0 +[0-9a-f]+ <[^>]*> ec00 jr a0 +[0-9a-f]+ <[^>]*> 6500 nop +[0-9a-f]+ <[^>]*> ec00 jr a0 +[0-9a-f]+ <[^>]*> 65f8 move ra,s0 +[0-9a-f]+ <[^>]*> e820 jr ra +[0-9a-f]+ <[^>]*> 6790 move a0,s0 +[0-9a-f]+ <[^>]*> 65f8 move ra,s0 +[0-9a-f]+ <[^>]*> e820 jr ra +[0-9a-f]+ <[^>]*> 6500 nop +[0-9a-f]+ <[^>]*> 6790 move a0,s0 +[0-9a-f]+ <[^>]*> ec40 jalr a0 +[0-9a-f]+ <[^>]*> 6500 nop +[0-9a-f]+ <[^>]*> 65f8 move ra,s0 +[0-9a-f]+ <[^>]*> ec40 jalr a0 +[0-9a-f]+ <[^>]*> 6500 nop +[0-9a-f]+ <[^>]*> 1800 0000 jal 0+0000 +[ ]*[0-9a-f]+: R_MIPS16_26 bar +[0-9a-f]+ <[^>]*> 6790 move a0,s0 +[0-9a-f]+ <[^>]*> 65f8 move ra,s0 +[0-9a-f]+ <[^>]*> 1800 0000 jal 0+0000 +[ ]*[0-9a-f]+: R_MIPS16_26 bar +[0-9a-f]+ <[^>]*> 6500 nop +[0-9a-f]+ <[^>]*> 6500 nop + \.\.\. Index: binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips16@loc-swap.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils-fsf-trunk-quilt/gas/testsuite/gas/mips/mips16@loc-swap.d 2011-02-23 00:17:57.000000000 +0000 @@ -0,0 +1,61 @@ +#PROG: readelf +#readelf: -wl +#name: MIPS DWARF-2 location information with branch swapping +#as: -32 +#source: loc-swap.s + +# Verify that DWARF-2 location information for instructions reordered +# into a branch delay slot is updated to point to the branch instead. + +Raw dump of debug contents of section \.debug_line: + + Offset: 0x0 + Length: 67 + DWARF Version: 2 + Prologue Length: 33 + Minimum Instruction Length: 1 + Initial value of 'is_stmt': 1 + Line Base: -5 + Line Range: 14 + Opcode Base: 13 + + Opcodes: + Opcode 1 has 0 args + Opcode 2 has 1 args + Opcode 3 has 1 args + Opcode 4 has 1 args + Opcode 5 has 1 args + Opcode 6 has 0 args + Opcode 7 has 0 args + Opcode 8 has 0 args + Opcode 9 has 1 args + Opcode 10 has 0 args + Opcode 11 has 0 args + Opcode 12 has 1 args + + The Directory Table is empty\. + + The File Name Table: + Entry Dir Time Size Name + 1 0 0 0 loc-swap\.s + + Line Number Statements: + Extended opcode 2: set Address to 0x1 + Special opcode 11: advance Address by 0 to 0x1 and Line by 6 to 7 + Special opcode 35: advance Address by 2 to 0x3 and Line by 2 to 9 + Special opcode 64: advance Address by 4 to 0x7 and Line by 3 to 12 + Special opcode 7: advance Address by 0 to 0x7 and Line by 2 to 14 + Special opcode 64: advance Address by 4 to 0xb and Line by 3 to 17 + Special opcode 7: advance Address by 0 to 0xb and Line by 2 to 19 + Special opcode 64: advance Address by 4 to 0xf and Line by 3 to 22 + Special opcode 35: advance Address by 2 to 0x11 and Line by 2 to 24 + Special opcode 64: advance Address by 4 to 0x15 and Line by 3 to 27 + Special opcode 35: advance Address by 2 to 0x17 and Line by 2 to 29 + Special opcode 64: advance Address by 4 to 0x1b and Line by 3 to 32 + Special opcode 35: advance Address by 2 to 0x1d and Line by 2 to 34 + Special opcode 64: advance Address by 4 to 0x21 and Line by 3 to 37 + Special opcode 7: advance Address by 0 to 0x21 and Line by 2 to 39 + Special opcode 92: advance Address by 6 to 0x27 and Line by 3 to 42 + Special opcode 35: advance Address by 2 to 0x29 and Line by 2 to 44 + Advance PC by 15 to 0x38 + Extended opcode 1: End of Sequence