diff -Burb binutils-2.13.2.1/gas/as.c binutils/gas/as.c --- binutils-2.13.2.1/gas/as.c 2002-12-21 00:19:05.000000000 +0300 +++ binutils/gas/as.c 2004-04-01 00:47:52.000000000 +0400 @@ -212,6 +212,7 @@ #else fprintf (stderr, _("GNU assembler version %s (%s)"), VERSION, TARGET_ALIAS); #endif + fprintf (stderr, _(" with Broadcom modifications")); fprintf (stderr, "\n"); } @@ -529,10 +530,11 @@ case OPTION_VERSION: /* This output is intended to follow the GNU standards document. */ #ifdef BFD_ASSEMBLER - printf (_("GNU assembler %s\n"), BFD_VERSION_STRING); + printf (_("GNU assembler %s"), BFD_VERSION_STRING); #else - printf (_("GNU assembler %s\n"), VERSION); + printf (_("GNU assembler %s"), VERSION); #endif + printf (_(" with Broadcom modifications\n")); printf (_("Copyright 2002 Free Software Foundation, Inc.\n")); printf (_("\ This program is free software; you may redistribute it under the terms of\n\ diff -Burb binutils-2.13.2.1/gas/config/tc-mips.c binutils/gas/config/tc-mips.c --- binutils-2.13.2.1/gas/config/tc-mips.c 2002-11-06 01:03:40.000000000 +0300 +++ binutils/gas/config/tc-mips.c 2004-04-01 00:47:54.000000000 +0400 @@ -106,6 +106,13 @@ extern int target_big_endian; +/* WA_BCM4710A0 */ +#if BCM4710A0 +static int wa_bcm4710a0 = 1; +#else +#define wa_bcm4710a0 0 +#endif + /* The name of the readonly data section. */ #define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \ ? ".data" \ @@ -2130,6 +2137,8 @@ || (pinfo & INSN_COND_BRANCH_DELAY)) { if (mips_optimize < 2 + /* WA_BCM4710A0: Shortcut the whole conditional and always add nops */ + || wa_bcm4710a0 == 1 /* If we have seen .set volatile or .set nomove, don't optimize. */ || mips_opts.nomove != 0 @@ -2342,6 +2351,11 @@ instruction at the destination, put it in the delay slot, and bump the destination address. */ emit_nop (); + + /* WA_BCM4710A0: Add another nop */ + if (wa_bcm4710a0) + emit_nop (); + /* Update the previous insn information. */ prev_prev_insn = *ip; prev_insn.insn_mo = &dummy_opcode; @@ -2456,6 +2470,11 @@ into the delay slot, and increment the branch to jump to the next instruction. */ emit_nop (); + + /* WA_BCM4710A0: Add another nop */ + if (wa_bcm4710a0) + emit_nop (); + /* Update the previous insn information. */ prev_prev_insn = *ip; prev_insn.insn_mo = &dummy_opcode; @@ -2500,6 +2519,167 @@ } else if (place == NULL) { + if(wa_bcm4710a0) { + /* We took care above of adding noops when reordering, now + need to do the same when not reordering. Unless this + is already a nop. + This means swapping the insn in the delay slot with the + jump and adding the nops. */ + +#define MIPS_INSN_MOVE 0x00000021 +#define MIPS_INSN_MOVE2 0x00000025 +#define GLIBC_SET_GP 0x04100000 + + if (prev_pinfo & (INSN_UNCOND_BRANCH_DELAY | INSN_COND_BRANCH_DELAY | INSN_COND_BRANCH_LIKELY)) { + + /* Special case for glibc SET_GP macro */ + if (prev_insn.insn_opcode == GLIBC_SET_GP) { + as_warn (_("bcm4710a0: NOT adding nop to glibc SET_GP macro (0x%lx %s)"), + prev_insn.insn_opcode, prev_insn.insn_mo->name); + goto skip_nop; + } + + /* If it is a nop or just a move, let it go. XXX: we should add a flag to + pinfo to let us know which insn's are ok, i.e. they don't + cause any stalls. */ + if ((ip->insn_opcode != 0) + && ((ip->insn_opcode & ip->insn_mo->mask) != MIPS_INSN_MOVE) + && ((ip->insn_opcode & ip->insn_mo->mask) != MIPS_INSN_MOVE2)) { + + int wreg = 0, wrsh = 0, rs = 0, rt = 0; + unsigned long wrmsk = 0, temp; + char *prev_f; + + /* Bad case: we cannot move a trap */ + if (pinfo & INSN_TRAP) { + as_warn (_("bcm4710a0: Current insn (%s) is a trap, cannot swap with %s"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + + /* Another bad case: we cannot move stuff after a branch likely */ + if (pinfo & INSN_COND_BRANCH_LIKELY) { + as_warn (_("bcm4710a0: Current insn (%s) cannot be swaped with branch likely %s"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + /* Also, we cannot move if there is a cc conflict */ + if ((pinfo & INSN_WRITE_COND_CODE) && (prev_pinfo & INSN_READ_COND_CODE)) { + as_warn (_("bcm4710a0: Current insn (%s) writes CC, cannot swap with %s which reads CC"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + + /* or a hi conflict */ + if ((pinfo & INSN_WRITE_HI) && (prev_pinfo & INSN_READ_HI)) { + as_warn (_("bcm4710a0: Current insn (%s) writes HI, cannot swap with %s which reads HI"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + + /* or a lo conflict */ + if ((pinfo & INSN_WRITE_LO) && (prev_pinfo & INSN_READ_LO)) { + as_warn (_("bcm4710a0: Current insn (%s) writes LO, cannot swap with %s which reads LO"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + + /* Which register if any is modified by the current insn? */ + if (pinfo & INSN_WRITE_GPR_D) { + wreg = (ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD; + wrsh = OP_SH_RD; + wrmsk = OP_MASK_RD; + } else if (pinfo & INSN_WRITE_GPR_T) { + wreg = (ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT; + wrsh = OP_SH_RT; + wrmsk = OP_MASK_RT; + } else if (pinfo & INSN_WRITE_GPR_31) + wreg = RA; + + if (wreg) { + /* Is that reg used by the previous insn? */ + if (prev_pinfo & INSN_READ_GPR_S) + rs = (prev_insn.insn_opcode >> OP_SH_RS) & OP_MASK_RS; + if (prev_pinfo & INSN_READ_GPR_T) + rt = (prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT; + if ((wreg != rs) && (wreg != rt)) { + /* Nope, We *can* do the swap */ + wreg = 0; + } else { + /* Cannot swap without some more surgery, + but do not swap at all if it is $at */ + if (wreg == AT) { + as_warn (_("bcm4710a0: Current insn (%s) uses reg%d, cannot swap with %s"), + ip->insn_mo->name, wreg, prev_insn.insn_mo->name); + goto skip_swap; + } + /* or if $at is not avaliable */ + if (mips_opts.noat) { + as_warn (_("bcm4710a0: .set noat in effect, cannot swap %s with %s"), + ip->insn_mo->name, prev_insn.insn_mo->name); + goto skip_swap; + } + } + } + + /* Swap the previous insn (a jump) and the current one */ + assert (prev_insn_frag != NULL); + prev_f = prev_insn_frag->fr_literal + prev_insn_where; + memcpy (&temp, f, 4); + if (wreg) { + /* Modify the instruction to use at */ + temp = (temp & ~(wrmsk << wrsh)) | (AT << wrsh); + } + memcpy (f, prev_f, 4); + memcpy (prev_f, &temp, 4); + if (prev_insn_fixp[0]) { + prev_insn_fixp[0]->fx_frag = frag_now; + prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal; + } + if (prev_insn_fixp[1]) { + prev_insn_fixp[1]->fx_frag = frag_now; + prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal; + } + if (prev_insn_fixp[2]) { + prev_insn_fixp[2]->fx_frag = frag_now; + prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal; + } + if (fixp[0]) { + fixp[0]->fx_frag = prev_insn_frag; + fixp[0]->fx_where = prev_insn_where; + } + if (fixp[1]) { + fixp[1]->fx_frag = prev_insn_frag; + fixp[1]->fx_where = prev_insn_where; + } + if (fixp[2]) { + fixp[2]->fx_frag = prev_insn_frag; + fixp[2]->fx_where = prev_insn_where; + } + /* Now put a nop or move after the jump ... */ + if (wreg) { + /* A move if we modified the insn we moved. */ + md_number_to_chars (frag_more (4), + (MIPS_INSN_MOVE | (wreg << OP_SH_RD) | (AT << OP_SH_RS)), + 4); + } else { + emit_nop (); + } + } +skip_swap: + /* And another nop */ + emit_nop (); + } +skip_nop: + /* Remember stuff that is not normally remembered in the not-reordering + case so we can swap with next insn */ + prev_insn_fixp[0] = fixp[0]; + prev_insn_fixp[1] = fixp[1]; + prev_insn_fixp[2] = fixp[2]; + prev_insn_frag = frag_now; + prev_insn_where = f - frag_now->fr_literal; + } + /* We need to record a bit of information even when we are not reordering, in order to determine the base address for mips16 PC relative relocs. */ @@ -10003,8 +10183,14 @@ {"mdmx", no_argument, NULL, OPTION_MDMX}, #define OPTION_NO_MDMX (OPTION_MD_BASE + 36) {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX}, +#define OPTION_M4710A0 (OPTION_MD_BASE + 37) + {"m4710a0", no_argument, NULL, OPTION_M4710A0}, +#define OPTION_NO_M4710A0 (OPTION_MD_BASE + 38) + {"no-m4710a0", no_argument, NULL, OPTION_NO_M4710A0}, + {"mno-4710a0", no_argument, NULL, OPTION_NO_M4710A0}, + {"m4710a0kern", no_argument, NULL, OPTION_NO_M4710A0}, #ifdef OBJ_ELF -#define OPTION_ELF_BASE (OPTION_MD_BASE + 37) +#define OPTION_ELF_BASE (OPTION_MD_BASE + 39) #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0) {"KPIC", no_argument, NULL, OPTION_CALL_SHARED}, {"call_shared", no_argument, NULL, OPTION_CALL_SHARED}, @@ -10213,6 +10399,16 @@ case OPTION_NO_M3900: break; +#if BCM4710A0 + case OPTION_M4710A0: + wa_bcm4710a0 = 1; + break; + + case OPTION_NO_M4710A0: + wa_bcm4710a0 = 0; + break; +#endif + case OPTION_MDMX: mips_opts.ase_mdmx = 1; break; diff -Burb binutils-2.13.2.1/gas/config.in binutils/gas/config.in --- binutils-2.13.2.1/gas/config.in 2002-07-14 05:14:19.000000000 +0400 +++ binutils/gas/config.in 2004-04-01 00:47:52.000000000 +0400 @@ -271,3 +271,5 @@ /* Define if errno is not declared in system header files. */ #undef NEED_DECLARATION_ERRNO +/* Define to 1 for BCM4710A0 compiler workarounds */ +#undef BCM4710A0 diff -Burb binutils-2.13.2.1/gas/configure.in binutils/gas/configure.in --- binutils-2.13.2.1/gas/configure.in 2002-10-30 20:07:32.000000000 +0300 +++ binutils/gas/configure.in 2004-04-01 00:47:52.000000000 +0400 @@ -762,6 +762,13 @@ ;; esac +# With BCM4710A0 compiler workarounds +AC_ARG_WITH(bcm4710a0, +[ --with-bcm4710a0 enable BCM4710A0 compiler workarounds], +[case "${withval}" in + yes) AC_DEFINE(BCM4710A0, 1, [BCM4710A0 support?]) ;; +esac]) + # Getting this done right is going to be a bitch. Each configuration specified # with --enable-targets=... should be checked for environment, format, cpu, and # bfd_gas setting.