This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH, MIPS] Compact branch support for MIPS32R6/MIPS64R6


A full range of 'compact' branch instructions were introduced to MIPS
as part of Release 6. The compact term is used to identify the fact
that these do not have a delay slot.

http://imgtec.com/mips/architectures/mips64/

The one subtlety of compact branches is that while they do not have
a delay slot they do have a restriction on what can immediately follow
them. The restriction is referred to as a forbidden slot in the
architecture specification and exists only on the not-taken path of
a conditional compact branch. (The detail of whether the hazard exists
on a not-taken branch is not relevant to a compiler however as it
has to be accounted for anyway as we would not generate a compact
branch if it were always taken.)

The forbidden slot restriction equates to the same rule as delay slots
where control flow instructions are not allowed to be placed there. The
exact same set of instructions cannot be placed in a forbidden slot.

An additional class of branch instructions is also available in
compact form only which allow ordering conditions to be applied
between two register sources. Support for these is included in this
patch.

So how does all this work in GCC?

Compact branches are used based on a branch policy. The polices are:

never: Only use delay slot branches
optimal: Do whatever is best for the current architecture.  This will
         generally mean that delay slot branches will be used if the delay
         slot gets filled but otherwise a compact branch will be used. A
         special case here is that JAL and J will not be used in R6 code
         regardless of whether the delay slot could be filled.
always: Never emit a delay slot form of a branch if a compact form exists.
        This policy cannot apply 100% as FP branches (and MSA branches when
        committed) only have delay slot forms.

These user choices are combined with the features available in the chosen
architecture and, in particular, the optimal form will get handled like
'never' when there are no compact branches available and will get handled
like 'always' when there are no delay slot branches available.

>From an instruction description perspective we also mark each branch with
a compact_form attribute that says if it 'never' has a compact form, 'maybe'
has a compact form dependent on delay slot filling, or 'always' comes in
a compact form. A secondary attribute is also used to describe whether the
instruction has a forbidden slot hazard. This applies to conditional compact
branches and means that although they do not have a delay slot, it is still
not possible to place a branch instruction immediately after them.

The define_delay definitions are configured by a combination of the user
selected branch policy and the compact_form attribute. This means the
delay slot filler will only operate on branches that should have delay slots.

Output patterns for branches fall into two categories:

1) Predetermined to be compact or delay slot, or this has been detected at
   the point of emitting the pattern. These will generally not use any
   formatters for the 'c' or the trailing NOP that normally get automatically
   injected by the mips_print_operand_punctuation function.
2) Use instruction formatters to enable a branch to naturally become a
   delay slot or compact form depending on whether a delay slot has been
   filled. These will use the %: formatter to indicate that a 'c' can
   be added instead of inserting a NOP using the %/ formatter. I.e.
   %: and %/ should never appear in the same branch instruction pattern.

It is generally safe to rely on using the formatters to produce the correct
branch instructions as a branch instruction that can only have a compact
form will not have a define_delay and therefore will never be in a final
sequence... This then means the %: is guaranteed to emit a 'c'.

The most complicated aspect of this change is to the MIPS_CALL and
MICROMIPS_J macros. These have been rewritten from scratch as a function
that generates an instruction instead.  This code is more complicated than
ordinary 'branch' code as J becomes BC and JAL become BALC which renders
instruction formatters impossible to use. The complexities of pic/non-pic
microMIPS/MIPS and absolute/relative addressing meant that wrapping all
that up in one place made much more sense. Matching the old macros to the
new function is hard but the conversion has been done carefully with a
significant amount of focussed testing.

Some of the framework in this patch is there in preparation for microMIPSR6
which only has compact branches. The support for adding microMIPSR6 to
GCC is a trivial patch on top of this.

This has been tested on multiple configurations albeit that most
configurations were tested from an older trunk revision. A re-run of a
wide range of configurations will be done after review/before commit.
This code has also been in use as part of tools to support internal
development of the I6400 core from Imagination.

gcc/
	* config/mips/mips-opts.h (mips_cb_setting): New enum.
	* config/mips/mips-protos.h: Add definitions for
	mips_output_jump and mips_output_equal_conditional_branch
	* gcc/config/mips/mips.c (MIPS_JR): Change to support the
	JIC instruction.
	(mips_emit_compare): Add support for the MIPS R6 conditional
	compact branches.
	(mips_process_sync_loop): Likewise.
	(mips_output_order_conditional_branch): Likewise.
	(mips16_build_call_stub): Change MIPS_CALL to
	mips_output_jump.
	(mips_print_operand_punctuation): Update 's' case to only
	apply to micromips r2.
	(mips_adjust_insn_length): Add support for forbidden slot
	hazards.
	(mips_avoid_hazard): Likewise.
	(mips_reorg_process_insns): Likewise.
	(mips_output_jump): New function.
	(mips_output_equal_conditional_branch): Likewise.
	(mips_output_conditional_branch): Use jrc/bc if compact
	branch support is enabled.  Ensure the forbidden slots
	between the two branch instructions is filled with a nop.
	(mips_option_override): Add support to process the compact
	branch option and set the correct defaults.  Prevent
	non-explict relocs being using for MIPS R6.
	(mips_trampoline_init): Add compact branch support.
	(mips_mult_zero_zero_cost): Allow zero initialisation of
	accumulators with TARGET_DSP.
	* gcc/config/mips/mips.h (TARGET_CB_NEVER): New define.
	(TARGET_CB_MAYBE): New define.
	(TARGET_CB_ALWAYS): New define.
	(ISA_HAS_DELAY_SLOTS): New define.
	(ISA_HAS_COMPACT_BRANCHES): New define.
	(ISA_HAS_JRC): New define.
	(MIPS_BRANCH_C): New define.
	(MIPS_CALL): Removed.
	(MICROMIPS_J): Removed.
	* config/mips/mips.md (compact_form): New attr.
	(hazard): Add support for forbidden slots.
	(define_delay): Add support for compact branches.
	(*branch_order<mode>): Likewise.
	(*branch_order<mode>_inverted): Likewise.
	(*branch_equality<mode>): Likewise.
	(*branch_equality<mode>_inverted): Likewise.
	(*jump_absolute): Likewise.
	(*jump_pic): Likewise.
	(indirect_jump): Use mips_output_jump to produce assembly output.
	(tablejump_<mode>"): Likewise.
	(*<optab>"): Likewise.
	(<optab>_internal): Likewise.
	(sibcall_internal): Likewise.
	(sibcall_value_internal): Likewise.
	(sibcall_value_multiple_internal): Likewise.
	(call_internal): Likewise.
	(call_split): Likewise.
	(call_internal_direct): Likewise.
	(call_direct_split): Likewise.
	(call_value_internal): Likewise.
	(call_value_split): Likewise.
	(call_value_internal_direct): Likewise.
	(call_value_direct_split): Likewise.
	(call_value_multiple_internal): Likewise.
	(call_value_multiple_split): Likewise.
	(mips_get_fcsr_mips16_<mode>): Likewise.
	(mips_set_fcsr_mips16_<mode>): Likewise.
	(tls_get_tp_mips16_<mode>): Likewise.
	* config/mips/mips.opt: Add -mcompact-branches option.
	* config/mips/predicates.md (order_operator): Ensure the
	conditional compact branches are only used if the ISA them.

gcc/testsuite/
	* gcc.target/mips/mips.exp (mips-dg-options): Handle the
	dependencies between ISA level and compact-branches.
	* gcc.target/mips/branch-10.c: Update expected output to allow
	compact forms of b/bal.
	* gcc.target/mips/branch-11.c: Likewise.
	* gcc.target/mips/branch-12.c: Likewise.
	* gcc.target/mips/branch-13.c: Likewise.
	* gcc.target/mips/branch-3.c: Likewise.
	* gcc.target/mips/branch-4.c: Likewise.
	* gcc.target/mips/branch-5.c: Likewise.
	* gcc.target/mips/branch-6.c: Likewise.
	* gcc.target/mips/branch-7.c: Likewise.
	* gcc.target/mips/branch-8.c: Likewise.
	* gcc.target/mips/branch-9.c: Likewise.
	* gcc.target/mips/branch-cost-1.c: Likewise.
	* gcc.target/mips/call-1.c: Likewise.
	* gcc.target/mips/call-2.c: Likewise.
	* gcc.target/mips/call-3.c: Likewise.
	* gcc.target/mips/call-4.c: Likewise.
	* gcc.target/mips/call-5.c: Likewise.
	* gcc.target/mips/call-6.c: Likewise.
	* gcc.target/mips/lazy-binding-1.c: Likewise.
	* gcc.target/mips/near-far-1.c: Likewise.
	* gcc.target/mips/near-far-2.c: Likewise.
	* gcc.target/mips/near-far-3.c: Likewise.
	* gcc.target/mips/near-far-4.c: Likewise.
	* gcc.target/mips/umips-branch-3.c: Ensure the test is
	run with compact branches allowed.

Thanks,
Matthew.

---
 gcc/config/mips/mips-opts.h                    |   6 +
 gcc/config/mips/mips-protos.h                  |   3 +
 gcc/config/mips/mips.c                         | 368 ++++++++++++++++++++++---
 gcc/config/mips/mips.h                         |  63 ++---
 gcc/config/mips/mips.md                        | 213 +++++++-------
 gcc/config/mips/mips.opt                       |  17 ++
 gcc/config/mips/predicates.md                  |  13 +-
 gcc/testsuite/gcc.target/mips/branch-10.c      |   2 +-
 gcc/testsuite/gcc.target/mips/branch-11.c      |   2 +-
 gcc/testsuite/gcc.target/mips/branch-12.c      |   2 +-
 gcc/testsuite/gcc.target/mips/branch-13.c      |   2 +-
 gcc/testsuite/gcc.target/mips/branch-3.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-4.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-5.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-6.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-7.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-8.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-9.c       |   2 +-
 gcc/testsuite/gcc.target/mips/branch-cost-1.c  |   2 +-
 gcc/testsuite/gcc.target/mips/call-1.c         |  14 +-
 gcc/testsuite/gcc.target/mips/call-2.c         |   2 +-
 gcc/testsuite/gcc.target/mips/call-3.c         |   2 +-
 gcc/testsuite/gcc.target/mips/call-4.c         |   2 +-
 gcc/testsuite/gcc.target/mips/call-5.c         |  14 +-
 gcc/testsuite/gcc.target/mips/call-6.c         |  14 +-
 gcc/testsuite/gcc.target/mips/lazy-binding-1.c |   2 +-
 gcc/testsuite/gcc.target/mips/mips.exp         |  13 +-
 gcc/testsuite/gcc.target/mips/near-far-1.c     |   2 +-
 gcc/testsuite/gcc.target/mips/near-far-2.c     |   4 +-
 gcc/testsuite/gcc.target/mips/near-far-3.c     |   2 +-
 gcc/testsuite/gcc.target/mips/near-far-4.c     |   4 +-
 gcc/testsuite/gcc.target/mips/umips-branch-3.c |   2 +-
 32 files changed, 565 insertions(+), 219 deletions(-)

diff --git a/gcc/config/mips/mips-opts.h b/gcc/config/mips/mips-opts.h
index 7988205..3c2c659 100644
--- a/gcc/config/mips/mips-opts.h
+++ b/gcc/config/mips/mips-opts.h
@@ -47,4 +47,10 @@ enum mips_r10k_cache_barrier_setting {
 #define MIPS_ARCH_OPTION_FROM_ABI -1
 #define MIPS_ARCH_OPTION_NATIVE -2
 
+/* Enumerates the setting of the -mcompact-branches= option.  */
+enum mips_cb_setting {
+  MIPS_CB_NEVER,
+  MIPS_CB_OPTIMAL,
+  MIPS_CB_ALWAYS
+};
 #endif
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index 244eb8d..e7db044 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -298,6 +298,9 @@ extern const char *mips_output_conditional_branch (rtx_insn *, rtx *,
 						   const char *, const char *);
 extern const char *mips_output_order_conditional_branch (rtx_insn *, rtx *,
 							 bool);
+extern const char *mips_output_equal_conditional_branch (rtx_insn *, rtx *,
+							 bool);
+extern const char *mips_output_jump (rtx *, int, int, bool);
 extern const char *mips_output_sync (void);
 extern const char *mips_output_sync_loop (rtx_insn *, rtx *);
 extern unsigned int mips_sync_loop_insns (rtx_insn *, rtx *);
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index c3cd52d..c0f2884 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -176,7 +176,8 @@ along with GCC; see the file COPYING3.  If not see
 /* Return the opcode to jump to register DEST.  When the JR opcode is not
    available use JALR $0, DEST.  */
 #define MIPS_JR(DEST) \
-  (((DEST) << 21) | (ISA_HAS_JR ? 0x8 : 0x9))
+  (TARGET_CB_ALWAYS ? ((0x1b << 27) | ((DEST) << 16)) \
+		    : (((DEST) << 21) | (ISA_HAS_JR ? 0x8 : 0x9)))
 
 /* Return the opcode for:
 
@@ -5194,7 +5195,8 @@ mips_allocate_fcc (machine_mode mode)
    conditions are:
 
       - EQ or NE between two registers.
-      - any comparison between a register and zero.  */
+      - any comparison between a register and zero.
+      - if compact branches are available then any condition is valid.  */
 
 static void
 mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
@@ -5216,6 +5218,44 @@ mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
 	  else
 	    *op1 = force_reg (GET_MODE (cmp_op0), cmp_op1);
 	}
+      else if (!need_eq_ne_p && TARGET_CB_MAYBE)
+	{
+	  bool swap = false;
+	  switch (*code)
+	    {
+	    case LE:
+	      swap = true;
+	      *code = GE;
+	      break;
+	    case GT:
+	      swap = true;
+	      *code = LT;
+	      break;
+	    case LEU:
+	      swap = true;
+	      *code = GEU;
+	      break;
+	    case GTU:
+	      swap = true;
+	      *code = LTU;
+	      break;
+	    case GE:
+	    case LT:
+	    case GEU:
+	    case LTU:
+	      /* Do nothing.  */
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+	  *op1 = force_reg (GET_MODE (cmp_op0), cmp_op1);
+	  if (swap)
+	    {
+	      rtx tmp = *op1;
+	      *op1 = *op0;
+	      *op0 = tmp;
+	    }
+	}
       else
 	{
 	  /* The comparison needs a separate scc instruction.  Store the
@@ -7273,7 +7313,7 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
       if (fp_ret_p)
 	{
 	  /* Now call the non-MIPS16 function.  */
-	  output_asm_insn (MIPS_CALL ("jal", &fn, 0, -1), &fn);
+	  output_asm_insn (mips_output_jump (&fn, 0, -1, true), &fn);
 	  fprintf (asm_out_file, "\t.cfi_register 31,18\n");
 
 	  /* Move the result from floating-point registers to
@@ -8380,7 +8420,7 @@ mips_pop_asm_switch (struct mips_asm_switch *asm_switch)
    '!'  Print "s" to use the short version if the delay slot contains a
 	16-bit instruction.
 
-   See also mips_init_print_operand_pucnt.  */
+   See also mips_init_print_operand_punct.  */
 
 static void
 mips_print_operand_punctuation (FILE *file, int ch)
@@ -8464,7 +8504,8 @@ mips_print_operand_punctuation (FILE *file, int ch)
 
     case ':':
       /* When final_sequence is 0, the delay slot will be a nop.  We can
-	 use the compact version for microMIPS.  */
+	 use the compact version where available.  The %: formatter will
+	 only be present if a compact form of the branch is available.  */
       if (final_sequence == 0)
 	putc ('c', file);
       break;
@@ -8472,8 +8513,9 @@ mips_print_operand_punctuation (FILE *file, int ch)
     case '!':
       /* If the delay slot instruction is short, then use the
 	 compact version.  */
-      if (final_sequence == 0
-	  || get_attr_length (final_sequence->insn (1)) == 2)
+      if (TARGET_MICROMIPS && !TARGET_INTERLINK_COMPRESSED && mips_isa_rev <= 5
+	  && (final_sequence == 0
+	      || get_attr_length (final_sequence->insn (1)) == 2))
 	putc ('s', file);
       break;
 
@@ -12944,6 +12986,7 @@ mips_adjust_insn_length (rtx_insn *insn, int length)
 	break;
 
       case HAZARD_DELAY:
+      case HAZARD_FORBIDDEN_SLOT:
 	length += NOP_INSN_LENGTH;
 	break;
 
@@ -12955,6 +12998,77 @@ mips_adjust_insn_length (rtx_insn *insn, int length)
   return length;
 }
 
+/* Return the asm template for a call.  OPERANDS are the operands, TARGET_OPNO
+   is the operand number of the target.  SIZE_OPNO is the operand number of
+   the argument size operand that can optionally hold the call attributes.  If
+   SIZE_OPNO is not -1 and the call is indirect, use the function symbol from
+   the call attributes to attach a R_MIPS_JALR relocation to the call.
+
+   When generating GOT code without explicit relocation operators, all calls
+   should use assembly macros.  Otherwise, all indirect calls should use "jr"
+   or "jalr"; we will arrange to restore $gp afterwards if necessary.  Finally,
+   we can only generate direct calls for -mabicalls by temporarily switching
+   to non-PIC mode.
+
+   For microMIPS jal(r), we try to generate jal(r)s when a 16-bit
+   instruction is in the delay slot of jal(r).
+
+   Where compact branches are available, we try to use them if the delay slot
+   has a NOP (or equivalently delay slots were not enabled for the instruction
+   anyway).  */
+
+const char *
+mips_output_jump (rtx *operands, int target_opno, int size_opno, bool link_p)
+{
+  static char buffer[300];
+  char *s = buffer;
+  bool reg_p = REG_P (operands[target_opno]);
+
+  const char *and_link = link_p ? "al" : "";
+  const char *reg = reg_p ? "r" : "";
+  const char *compact = "";
+  const char *nop = "%/";
+  const char *short_delay = link_p ? "%!" : "";
+  const char *insn_name = TARGET_CB_NEVER || reg_p ? "j" : "b";
+
+  /* Compact branches can only be described when the ISA has support for them
+     as both the compact formatter '%:' and the delay slot NOP formatter '%/'
+     work as a mutually exclusive pair.  I.e. a NOP is never required if a
+     compact form is available.  */
+  if (!final_sequence
+      && (TARGET_CB_MAYBE
+	  || (ISA_HAS_JRC && !link_p && reg_p)))
+    {
+      compact = "c";
+      nop = "";
+    }
+
+  if (TARGET_USE_GOT && !TARGET_EXPLICIT_RELOCS)
+    sprintf (s, "%%*%s%s\t%%%d%%/", insn_name, and_link, target_opno);
+  else
+    {
+      if (!reg_p && TARGET_ABICALLS_PIC2)
+	s += sprintf (s, ".option\tpic0\n\t");
+
+      if (reg_p && mips_get_pic_call_symbol (operands, size_opno))
+	{
+	  s += sprintf (s, "%%*.reloc\t1f,R_MIPS_JALR,%%%d\n1:\t", size_opno);
+	  /* Not sure why this shouldn't permit a short delay but it did not
+	     allow it before so we still don't allow it.  */
+	  short_delay = "";
+	}
+      else
+	s += sprintf (s, "%%*");
+
+      s += sprintf (s, "%s%s%s%s%s\t%%%d%s", insn_name, and_link, reg, compact, short_delay,
+					    target_opno, nop);
+
+      if (!reg_p && TARGET_ABICALLS_PIC2)
+	s += sprintf (s, "\n\t.option\tpic2");
+    }
+  return buffer;
+}
+
 /* Return the assembly code for INSN, which has the operands given by
    OPERANDS, and which branches to OPERANDS[0] if some condition is true.
    BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0]
@@ -13008,12 +13122,25 @@ mips_output_conditional_branch (rtx_insn *insn, rtx *operands,
     }
 
   /* Output the unconditional branch to TAKEN.  */
-  if (TARGET_ABSOLUTE_JUMPS)
+  if (TARGET_ABSOLUTE_JUMPS && TARGET_CB_MAYBE)
+    {
+      /* Add a hazard nop.  */
+      if (!final_sequence)
+	{
+	  output_asm_insn ("nop\t\t# hazard nop", 0);
+	  fprintf (asm_out_file, "\n");
+	}
+      output_asm_insn (MIPS_ABSOLUTE_JUMP ("bc\t%0"), &taken);
+    }
+  else if (TARGET_ABSOLUTE_JUMPS)
     output_asm_insn (MIPS_ABSOLUTE_JUMP ("j\t%0%/"), &taken);
   else
     {
       mips_output_load_label (taken);
-      output_asm_insn ("jr\t%@%]%/", 0);
+      if (TARGET_CB_MAYBE)
+	output_asm_insn ("jrc\t%@%]", 0);
+      else
+	output_asm_insn ("jr\t%@%]%/", 0);
     }
 
   /* Now deal with its delay slot; see above.  */
@@ -13027,7 +13154,7 @@ mips_output_conditional_branch (rtx_insn *insn, rtx *operands,
 			   asm_out_file, optimize, 1, NULL);
 	  final_sequence->insn (1)->set_deleted ();
 	}
-      else
+      else if (TARGET_CB_NEVER)
 	output_asm_insn ("nop", 0);
       fprintf (asm_out_file, "\n");
     }
@@ -13038,6 +13165,59 @@ mips_output_conditional_branch (rtx_insn *insn, rtx *operands,
   return "";
 }
 
+const char *
+mips_output_equal_conditional_branch (rtx_insn* insn, rtx *operands,
+				      bool inverted_p)
+{
+  const char *branch[2];
+  /* For a simple BNEZ or BEQZ microMIPSr3 branch.  */
+  if (TARGET_MICROMIPS
+      && mips_isa_rev <= 5
+      && operands[3] == const0_rtx
+      && get_attr_length (insn) <= 8)
+    {
+      if (mips_cb == MIPS_CB_OPTIMAL)
+	{
+	  branch[!inverted_p] = "%*b%C1z%:\t%2,%0";
+	  branch[inverted_p] = "%*b%N1z%:\t%2,%0";
+	}
+      else
+	{
+	  branch[!inverted_p] = "%*b%C1z\t%2,%0%/";
+	  branch[inverted_p] = "%*b%N1z\t%2,%0%/";
+	}
+    }
+  else if (TARGET_CB_MAYBE)
+    {
+      if (operands[3] == const0_rtx)
+	{
+	  branch[!inverted_p] = MIPS_BRANCH_C ("b%C1z", "%2,%0");
+	  branch[inverted_p] = MIPS_BRANCH_C ("b%N1z", "%2,%0");
+	}
+      else if (REGNO (operands[2]) != REGNO (operands[3]))
+	{
+	  branch[!inverted_p] = MIPS_BRANCH_C ("b%C1", "%2,%3,%0");
+	  branch[inverted_p] = MIPS_BRANCH_C ("b%N1", "%2,%3,%0");
+	}
+      else
+	{
+	  /* This case is degenerate.  It should not happen, but does.  */
+	  if (GET_CODE (operands[1]) == NE)
+	    inverted_p = !inverted_p;
+
+	  branch[!inverted_p] = MIPS_BRANCH_C ("b", "%0");
+	  branch[inverted_p] = "%*\t\t# branch never";
+	}
+    }
+  else
+    {
+      branch[!inverted_p] = MIPS_BRANCH ("b%C1", "%2,%z3,%0");
+      branch[inverted_p] = MIPS_BRANCH ("b%N1", "%2,%z3,%0");
+    }
+
+  return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
+}
+
 /* Return the assembly code for INSN, which branches to OPERANDS[0]
    if some ordering condition is true.  The condition is given by
    OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
@@ -13045,36 +13225,90 @@ mips_output_conditional_branch (rtx_insn *insn, rtx *operands,
    its second is always zero.  */
 
 const char *
-mips_output_order_conditional_branch (rtx_insn *insn, rtx *operands, bool inverted_p)
+mips_output_order_conditional_branch (rtx_insn *insn, rtx *operands,
+				      bool inverted_p)
 {
   const char *branch[2];
 
   /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true.
      Make BRANCH[0] branch on the inverse condition.  */
-  switch (GET_CODE (operands[1]))
+  if (operands[3] != const0_rtx)
     {
-      /* These cases are equivalent to comparisons against zero.  */
-    case LEU:
-      inverted_p = !inverted_p;
-      /* Fall through.  */
-    case GTU:
-      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
-      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
-      break;
+      /* Handle degenerate cases that should not, but do, occur.  */
+      if (REGNO (operands[2]) == REGNO (operands[3]))
+	{
+	  switch (GET_CODE (operands[1]))
+	    {
+	    case LT:
+	    case LTU:
+	      inverted_p = !inverted_p;
+	      /* Fall through.  */
+	    case GE:
+	    case GEU:
+	      branch[!inverted_p] = MIPS_BRANCH_C ("b", "%0");
+	      branch[inverted_p] = "%*\t\t# branch never";
+	      break;
+	   default:
+	      gcc_unreachable ();
+	    }
+	}
+      else
+	{
+	  branch[!inverted_p] = MIPS_BRANCH_C ("b%C1", "%2,%3,%0");
+	  branch[inverted_p] = MIPS_BRANCH_C ("b%N1", "%2,%3,%0");
+	}
+    }
+  else
+    {
+      switch (GET_CODE (operands[1]))
+	{
+	  /* These cases are equivalent to comparisons against zero.  */
+	case LEU:
+	  inverted_p = !inverted_p;
+	  /* Fall through.  */
+	case GTU:
+	  if (TARGET_CB_MAYBE)
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH_C ("bnez", "%2,%0");
+	      branch[inverted_p] = MIPS_BRANCH_C ("beqz", "%2,%0");
+	    }
+	  else
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
+	      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
+	    }
+	  break;
 
-      /* These cases are always true or always false.  */
-    case LTU:
-      inverted_p = !inverted_p;
-      /* Fall through.  */
-    case GEU:
-      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
-      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
-      break;
+	  /* These cases are always true or always false.  */
+	case LTU:
+	  inverted_p = !inverted_p;
+	  /* Fall through.  */
+	case GEU:
+	  if (TARGET_CB_MAYBE)
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH_C ("b", "%0");
+	      branch[inverted_p] = "%*\t\t# branch never";
+	    }
+	  else
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
+	      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
+	    }
+	  break;
 
-    default:
-      branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
-      branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
-      break;
+	default:
+	  if (TARGET_CB_MAYBE)
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH_C ("b%C1z", "%2,%0");
+	      branch[inverted_p] = MIPS_BRANCH_C ("b%N1z", "%2,%0");
+	    }
+	  else
+	    {
+	      branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
+	      branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
+	    }
+	  break;
+	}
     }
   return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
 }
@@ -13277,11 +13511,18 @@ mips_process_sync_loop (rtx_insn *insn, rtx *operands)
 			       at, oldval, inclusive_mask, NULL);
 	  tmp1 = at;
 	}
-      mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL);
+      if (TARGET_CB_NEVER)
+	mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL);
 
       /* CMP = 0 [delay slot].  */
       if (cmp)
         mips_multi_add_insn ("li\t%0,0", cmp, NULL);
+
+      if (TARGET_CB_MAYBE && required_oldval == const0_rtx)
+	mips_multi_add_insn ("bnezc\t%0,2f", tmp1, NULL);
+      else if (TARGET_CB_MAYBE)
+	mips_multi_add_insn ("bnec\t%0,%1,2f", tmp1, required_oldval, NULL);
+
     }
 
   /* $TMP1 = OLDVAL & EXCLUSIVE_MASK.  */
@@ -13344,7 +13585,10 @@ mips_process_sync_loop (rtx_insn *insn, rtx *operands)
      be annulled.  To ensure this behaviour unconditionally use a NOP
      in the delay slot for the branch likely case.  */
 
-  mips_multi_add_insn ("beq%?\t%0,%.,1b%~", at, NULL);
+  if (TARGET_CB_MAYBE)
+    mips_multi_add_insn ("beqzc\t%0,1b", at, NULL);
+  else
+    mips_multi_add_insn ("beq%?\t%0,%.,1b%~", at, NULL);
 
   /* if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot].  */
   if (insn1 != SYNC_INSN1_MOVE && insn1 != SYNC_INSN1_LI && tmp3 != newval)
@@ -16625,7 +16869,7 @@ mips_orphaned_high_part_p (mips_offset_table *htab, rtx_insn *insn)
 
 static void
 mips_avoid_hazard (rtx_insn *after, rtx_insn *insn, int *hilo_delay,
-		   rtx *delayed_reg, rtx lo_reg)
+		   rtx *delayed_reg, rtx lo_reg, bool *fs_delay)
 {
   rtx pattern, set;
   int nops, ninsns;
@@ -16651,6 +16895,15 @@ mips_avoid_hazard (rtx_insn *after, rtx_insn *insn, int *hilo_delay,
     nops = 2 - *hilo_delay;
   else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern))
     nops = 1;
+  /* If processing a forbidden slot hazard then a NOP is required if the
+     branch instruction was not in a sequence (as the sequence would
+     imply it is not actually a compact branch anyway) and the current
+     insn is not an inline asm, and can't go in a delay slot.  */
+  else if (*fs_delay && get_attr_can_delay (insn) == CAN_DELAY_NO
+	   && GET_CODE (PATTERN (after)) != SEQUENCE
+	   && GET_CODE (pattern) != ASM_INPUT
+	   && asm_noperands (pattern) < 0)
+    nops = 1;
   else
     nops = 0;
 
@@ -16663,12 +16916,18 @@ mips_avoid_hazard (rtx_insn *after, rtx_insn *insn, int *hilo_delay,
   /* Set up the state for the next instruction.  */
   *hilo_delay += ninsns;
   *delayed_reg = 0;
+  *fs_delay = false;
   if (INSN_CODE (insn) >= 0)
     switch (get_attr_hazard (insn))
       {
       case HAZARD_NONE:
 	break;
 
+      case HAZARD_FORBIDDEN_SLOT:
+	if (TARGET_CB_MAYBE)
+	  *fs_delay = true;
+	break;
+
       case HAZARD_HILO:
 	*hilo_delay = 0;
 	break;
@@ -16692,6 +16951,7 @@ mips_reorg_process_insns (void)
   rtx_insn *insn, *last_insn, *subinsn, *next_insn;
   rtx lo_reg, delayed_reg;
   int hilo_delay;
+  bool fs_delay;
 
   /* Force all instructions to be split into their final form.  */
   split_all_insns_noflow ();
@@ -16760,6 +17020,7 @@ mips_reorg_process_insns (void)
   hilo_delay = 2;
   delayed_reg = 0;
   lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
+  fs_delay = false;
 
   /* Make a second pass over the instructions.  Delete orphaned
      high-part relocations or turn them into NOPs.  Avoid hazards
@@ -16783,7 +17044,7 @@ mips_reorg_process_insns (void)
 			INSN_CODE (subinsn) = CODE_FOR_nop;
 		      }
 		    mips_avoid_hazard (last_insn, subinsn, &hilo_delay,
-				       &delayed_reg, lo_reg);
+				       &delayed_reg, lo_reg, &fs_delay);
 		  }
 	      last_insn = insn;
 	    }
@@ -16804,7 +17065,7 @@ mips_reorg_process_insns (void)
 	      else
 		{
 		  mips_avoid_hazard (last_insn, insn, &hilo_delay,
-				     &delayed_reg, lo_reg);
+				     &delayed_reg, lo_reg, &fs_delay);
 		  last_insn = insn;
 		}
 	    }
@@ -17669,6 +17930,27 @@ mips_option_override (void)
       target_flags |= MASK_ODD_SPREG;
     }
 
+  if (!ISA_HAS_COMPACT_BRANCHES && mips_cb == MIPS_CB_ALWAYS)
+    {
+      error ("unsupported combination: %qs%s %s",
+	      mips_arch_info->name, TARGET_MICROMIPS ? " -mmicromips" : "",
+	      "-mcompact-branches=always");
+    }
+  else if (!ISA_HAS_DELAY_SLOTS && mips_cb == MIPS_CB_NEVER)
+    {
+      error ("unsupported combination: %qs%s %s",
+	      mips_arch_info->name, TARGET_MICROMIPS ? " -mmicromips" : "",
+	      "-mcompact-branches=never");
+    }
+
+  /* Require explicit relocs for MIPS R6 onwards.  This enables simplification
+     of the compact branch and jump support through the backend.  */
+  if (!TARGET_EXPLICIT_RELOCS && mips_isa_rev >= 6)
+    {
+      error ("unsupported combination: %qs %s",
+	     mips_arch_info->name, "-mno-explicit-relocs");
+    }
+
   /* The effect of -mabicalls isn't defined for the EABI.  */
   if (mips_abi == ABI_EABI && TARGET_ABICALLS)
     {
@@ -18688,6 +18970,18 @@ mips_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
 
 #undef OP
 
+  /* If we are using compact branches we don't have delay slots so
+     place the instruction that was in the delay slot before the JRC
+     instruction.  */
+
+  if (TARGET_CB_ALWAYS)
+    {
+      rtx temp;
+      temp = trampoline[i-2];
+      trampoline[i-2] = trampoline[i-1];
+      trampoline[i-1] = temp;
+    }
+
   /* Copy the trampoline code.  Leave any padding uninitialized.  */
   for (j = 0; j < i; j++)
     {
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 5bc562e..04fe6d0 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -92,6 +92,23 @@ struct mips_cpu_info {
 /* True if we are generating position-independent VxWorks RTP code.  */
 #define TARGET_RTP_PIC (TARGET_VXWORKS_RTP && flag_pic)
 
+/* Set based on a combination of compact branch policy and ISA support.  */
+#define TARGET_CB_NEVER (mips_cb == MIPS_CB_NEVER	\
+			 || (mips_cb == MIPS_CB_OPTIMAL \
+			     && !ISA_HAS_COMPACT_BRANCHES))
+#define TARGET_CB_MAYBE (TARGET_CB_ALWAYS		\
+			 || (mips_cb == MIPS_CB_OPTIMAL \
+			     && ISA_HAS_COMPACT_BRANCHES))
+#define TARGET_CB_ALWAYS (mips_cb == MIPS_CB_ALWAYS	\
+			 || (mips_cb == MIPS_CB_OPTIMAL \
+			     && !ISA_HAS_DELAY_SLOTS))
+
+/* Special handling for JRC that exists in microMIPSR3 as well as R6
+   ISAs with full compact branch support.  */
+#define ISA_HAS_JRC ((ISA_HAS_COMPACT_BRANCHES		\
+		      || TARGET_MICROMIPS)		\
+		     && mips_cb != MIPS_CB_NEVER)
+
 /* True if the output file is marked as ".abicalls; .option pic0"
    (-call_nonpic).  */
 #define TARGET_ABICALLS_PIC0 \
@@ -871,6 +888,10 @@ struct mips_cpu_info {
 
 #define ISA_HAS_JR		(mips_isa_rev <= 5)
 
+#define ISA_HAS_DELAY_SLOTS	1
+
+#define ISA_HAS_COMPACT_BRANCHES (mips_isa_rev >= 6)
+
 /* ISA has branch likely instructions (e.g. mips2).  */
 /* Disable branchlikely for tx39 until compare rewrite.  They haven't
    been generated up to this point.  */
@@ -2641,6 +2662,9 @@ typedef struct mips_args {
 #define MIPS_BRANCH(OPCODE, OPERANDS) \
   "%*" OPCODE "%?\t" OPERANDS "%/"
 
+#define MIPS_BRANCH_C(OPCODE, OPERANDS) \
+  "%*" OPCODE "%:\t" OPERANDS
+
 /* Return an asm string that forces INSN to be treated as an absolute
    J or JAL instruction instead of an assembler macro.  */
 #define MIPS_ABSOLUTE_JUMP(INSN) \
@@ -2648,45 +2672,6 @@ typedef struct mips_args {
    ? ".option\tpic0\n\t" INSN "\n\t.option\tpic2"		\
    : INSN)
 
-/* Return the asm template for a call.  INSN is the instruction's mnemonic
-   ("j" or "jal"), OPERANDS are its operands, TARGET_OPNO is the operand
-   number of the target.  SIZE_OPNO is the operand number of the argument size
-   operand that can optionally hold the call attributes.  If SIZE_OPNO is not
-   -1 and the call is indirect, use the function symbol from the call
-   attributes to attach a R_MIPS_JALR relocation to the call.
-
-   When generating GOT code without explicit relocation operators,
-   all calls should use assembly macros.  Otherwise, all indirect
-   calls should use "jr" or "jalr"; we will arrange to restore $gp
-   afterwards if necessary.  Finally, we can only generate direct
-   calls for -mabicalls by temporarily switching to non-PIC mode.
-
-   For microMIPS jal(r), we try to generate jal(r)s when a 16-bit
-   instruction is in the delay slot of jal(r).  */
-#define MIPS_CALL(INSN, OPERANDS, TARGET_OPNO, SIZE_OPNO)	\
-  (TARGET_USE_GOT && !TARGET_EXPLICIT_RELOCS			\
-   ? "%*" INSN "\t%" #TARGET_OPNO "%/"				\
-   : REG_P (OPERANDS[TARGET_OPNO])				\
-   ? (mips_get_pic_call_symbol (OPERANDS, SIZE_OPNO)		\
-      ? ("%*.reloc\t1f,R_MIPS_JALR,%" #SIZE_OPNO "\n"		\
-	 "1:\t" INSN "r\t%" #TARGET_OPNO "%/")			\
-      : TARGET_MICROMIPS && !TARGET_INTERLINK_COMPRESSED	\
-      ? "%*" INSN "r%!\t%" #TARGET_OPNO "%/"			\
-      : "%*" INSN "r\t%" #TARGET_OPNO "%/")			\
-   : TARGET_MICROMIPS && !TARGET_INTERLINK_COMPRESSED		\
-     ? MIPS_ABSOLUTE_JUMP ("%*" INSN "%!\t%" #TARGET_OPNO "%/")	\
-     : MIPS_ABSOLUTE_JUMP ("%*" INSN "\t%" #TARGET_OPNO "%/"))	\
-
-/* Similar to MIPS_CALL, but this is for MICROMIPS "j" to generate
-   "jrc" when nop is in the delay slot of "jr".  */
-
-#define MICROMIPS_J(INSN, OPERANDS, OPNO)			\
-  (TARGET_USE_GOT && !TARGET_EXPLICIT_RELOCS			\
-   ? "%*j\t%" #OPNO "%/"					\
-   : REG_P (OPERANDS[OPNO])					\
-   ? "%*jr%:\t%" #OPNO						\
-   : MIPS_ABSOLUTE_JUMP ("%*" INSN "\t%" #OPNO "%/"))
-
 

 /* Control the assembler format that we output.  */
 
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index cde2b4e..2b624fb 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -410,6 +410,15 @@ (define_attr "type"
 	 (eq_attr "sync_mem" "!none") (const_string "syncloop")]
 	(const_string "unknown")))
 
+(define_attr "compact_form" "always,maybe,never"
+  (cond [(eq_attr "jal" "direct")
+	 (const_string "always")
+	 (eq_attr "jal" "indirect")
+	 (const_string "maybe")
+	 (eq_attr "type" "jump")
+	 (const_string "maybe")]
+	(const_string "never")))
+
 ;; Mode for conversion types (fcvt)
 ;; I2S          integer to float single (SI/DI to SF)
 ;; I2D          integer to float double (SI/DI to DF)
@@ -695,7 +704,7 @@ (define_enum_attr "cpu" "processor"
 ;; DELAY means that the next instruction cannot read the result
 ;; of this one.  HILO means that the next two instructions cannot
 ;; write to HI or LO.
-(define_attr "hazard" "none,delay,hilo"
+(define_attr "hazard" "none,delay,hilo,forbidden_slot"
   (cond [(and (eq_attr "type" "load,fpload,fpidxload")
 	      (match_test "ISA_HAS_LOAD_DELAY"))
 	 (const_string "delay")
@@ -1046,21 +1055,37 @@ (define_delay (and (eq_attr "type" "branch")
    (nil)
    (eq_attr "can_delay" "yes")])
 
-;; Branches that don't have likely variants do not annul on false.
+;; Branches that have delay slots and don't have likely variants do
+;; not annul on false.
 (define_delay (and (eq_attr "type" "branch")
 		   (not (match_test "TARGET_MIPS16"))
+		   (ior (match_test "TARGET_CB_NEVER")
+			(and (eq_attr "compact_form" "maybe")
+			     (not (match_test "TARGET_CB_ALWAYS")))
+			(eq_attr "compact_form" "never"))
 		   (eq_attr "branch_likely" "no"))
   [(eq_attr "can_delay" "yes")
    (nil)
    (nil)])
 
-(define_delay (eq_attr "type" "jump")
+(define_delay (and (eq_attr "type" "jump")
+		   (ior (match_test "TARGET_CB_NEVER")
+			(and (eq_attr "compact_form" "maybe")
+			     (not (match_test "TARGET_CB_ALWAYS")))
+			(eq_attr "compact_form" "never")))
   [(eq_attr "can_delay" "yes")
    (nil)
    (nil)])
 
+;; Call type instructions should never have a compact form as the
+;; type is only used for MIPS16 patterns.  For safety put the compact
+;; branch detection condition in anyway.
 (define_delay (and (eq_attr "type" "call")
-		   (eq_attr "jal_macro" "no"))
+		   (eq_attr "jal_macro" "no")
+		   (ior (match_test "TARGET_CB_NEVER")
+			(and (eq_attr "compact_form" "maybe")
+			     (not (match_test "TARGET_CB_ALWAYS")))
+			(eq_attr "compact_form" "never")))
   [(eq_attr "can_delay" "yes")
    (nil)
    (nil)])
@@ -5813,25 +5838,29 @@ (define_insn "*branch_order<mode>"
   [(set (pc)
 	(if_then_else
 	 (match_operator 1 "order_operator"
-			 [(match_operand:GPR 2 "register_operand" "d")
-			  (const_int 0)])
+			 [(match_operand:GPR 2 "register_operand" "d,d")
+			  (match_operand:GPR 3 "reg_or_0_operand" "J,d")])
 	 (label_ref (match_operand 0 "" ""))
 	 (pc)))]
   "!TARGET_MIPS16"
   { return mips_output_order_conditional_branch (insn, operands, false); }
-  [(set_attr "type" "branch")])
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe,always")
+   (set_attr "hazard" "forbidden_slot")])
 
 (define_insn "*branch_order<mode>_inverted"
   [(set (pc)
 	(if_then_else
 	 (match_operator 1 "order_operator"
-			 [(match_operand:GPR 2 "register_operand" "d")
-			  (const_int 0)])
+			 [(match_operand:GPR 2 "register_operand" "d,d")
+			  (match_operand:GPR 3 "reg_or_0_operand" "J,d")])
 	 (pc)
 	 (label_ref (match_operand 0 "" ""))))]
   "!TARGET_MIPS16"
   { return mips_output_order_conditional_branch (insn, operands, true); }
-  [(set_attr "type" "branch")])
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe,always")
+   (set_attr "hazard" "forbidden_slot")])
 
 ;; Conditional branch on equality comparison.
 
@@ -5844,20 +5873,10 @@ (define_insn "*branch_equality<mode>"
 	 (label_ref (match_operand 0 "" ""))
 	 (pc)))]
   "!TARGET_MIPS16"
-{
-  /* For a simple BNEZ or BEQZ microMIPS branch.  */
-  if (TARGET_MICROMIPS
-      && operands[3] == const0_rtx
-      && get_attr_length (insn) <= 8)
-    return mips_output_conditional_branch (insn, operands,
-					   "%*b%C1z%:\t%2,%0",
-					   "%*b%N1z%:\t%2,%0");
-
-  return mips_output_conditional_branch (insn, operands,
-					 MIPS_BRANCH ("b%C1", "%2,%z3,%0"),
-					 MIPS_BRANCH ("b%N1", "%2,%z3,%0"));
-}
-  [(set_attr "type" "branch")])
+  { return mips_output_equal_conditional_branch (insn, operands, false); }
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe")
+   (set_attr "hazard" "forbidden_slot")])
 
 (define_insn "*branch_equality<mode>_inverted"
   [(set (pc)
@@ -5868,20 +5887,10 @@ (define_insn "*branch_equality<mode>_inverted"
 	 (pc)
 	 (label_ref (match_operand 0 "" ""))))]
   "!TARGET_MIPS16"
-{
-  /* For a simple BNEZ or BEQZ microMIPS branch.  */
-  if (TARGET_MICROMIPS
-      && operands[3] == const0_rtx
-      && get_attr_length (insn) <= 8)
-    return mips_output_conditional_branch (insn, operands,
-					   "%*b%N0z%:\t%2,%1",
-					   "%*b%C0z%:\t%2,%1");
-
-  return mips_output_conditional_branch (insn, operands,
-					 MIPS_BRANCH ("b%N1", "%2,%z3,%0"),
-					 MIPS_BRANCH ("b%C1", "%2,%z3,%0"));
-}
-  [(set_attr "type" "branch")])
+  { return mips_output_equal_conditional_branch (insn, operands, true); }
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe")
+   (set_attr "hazard" "forbidden_slot")])
 
 ;; MIPS16 branches
 
@@ -6176,11 +6185,22 @@ (define_insn "*jump_absolute"
   "!TARGET_MIPS16 && TARGET_ABSOLUTE_JUMPS"
 {
   if (get_attr_length (insn) <= 8)
-    return "%*b\t%l0%/";
+    {
+      if (TARGET_CB_MAYBE)
+	return MIPS_ABSOLUTE_JUMP ("%*b%:\t%l0");
+      else
+	return MIPS_ABSOLUTE_JUMP ("%*b\t%l0%/");
+    }
   else
-    return MIPS_ABSOLUTE_JUMP ("%*j\t%l0%/");
+    {
+      if (TARGET_CB_MAYBE && !final_sequence)
+	return MIPS_ABSOLUTE_JUMP ("%*bc\t%l0");
+      else
+	return MIPS_ABSOLUTE_JUMP ("%*j\t%l0%/");
+    }
 }
-  [(set_attr "type" "branch")])
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe")])
 
 (define_insn "*jump_pic"
   [(set (pc)
@@ -6188,14 +6208,23 @@ (define_insn "*jump_pic"
   "!TARGET_MIPS16 && !TARGET_ABSOLUTE_JUMPS"
 {
   if (get_attr_length (insn) <= 8)
-    return "%*b\t%l0%/";
+    {
+      if (TARGET_CB_MAYBE)
+	return "%*b%:\t%l0";
+      else
+	return "%*b\t%l0%/";
+    }
   else
     {
       mips_output_load_label (operands[0]);
-      return "%*jr\t%@%/%]";
+      if (TARGET_CB_MAYBE)
+	return "%*jr%:\t%@%]";
+      else
+	return "%*jr\t%@%/%]";
     }
 }
-  [(set_attr "type" "branch")])
+  [(set_attr "type" "branch")
+   (set_attr "compact_form" "maybe")])
 
 ;; We need a different insn for the mips16, because a mips16 branch
 ;; does not have a delay slot.
@@ -6242,12 +6271,9 @@ (define_expand "indirect_jump"
 (define_insn "indirect_jump_<mode>"
   [(set (pc) (match_operand:P 0 "register_operand" "d"))]
   ""
-{
-  if (TARGET_MICROMIPS)
-    return "%*jr%:\t%0";
-  else
-    return "%*j\t%0%/";
-}
+  {
+    return mips_output_jump (operands, 0, -1, false);
+  }
   [(set_attr "type" "jump")
    (set_attr "mode" "none")])
 
@@ -6291,12 +6317,9 @@ (define_insn "tablejump_<mode>"
 	(match_operand:P 0 "register_operand" "d"))
    (use (label_ref (match_operand 1 "" "")))]
   ""
-{
-  if (TARGET_MICROMIPS)
-    return "%*jr%:\t%0";
-  else
-    return "%*j\t%0%/";
-}
+  {
+    return mips_output_jump (operands, 0, -1, false);
+  }
   [(set_attr "type" "jump")
    (set_attr "mode" "none")])
 
@@ -6508,10 +6531,8 @@ (define_insn "*<optab>"
   [(any_return)]
   ""
   {
-    if (TARGET_MICROMIPS)
-      return "%*jr%:\t$31";
-    else
-      return "%*j\t$31%/";
+    operands[0] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
+    return mips_output_jump (operands, 0, -1, false);
   }
   [(set_attr "type"	"jump")
    (set_attr "mode"	"none")])
@@ -6522,12 +6543,10 @@ (define_insn "<optab>_internal"
   [(any_return)
    (use (match_operand 0 "pmode_register_operand" ""))]
   ""
-{
-  if (TARGET_MICROMIPS)
-    return "%*jr%:\t%0";
-  else
-    return "%*j\t%0%/";
-}
+  {
+    operands[0] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
+    return mips_output_jump (operands, 0, -1, false);
+  }
   [(set_attr "type"	"jump")
    (set_attr "mode"	"none")])
 
@@ -6783,12 +6802,7 @@ (define_insn "sibcall_internal"
   [(call (mem:SI (match_operand 0 "call_insn_operand" "j,S"))
 	 (match_operand 1 "" ""))]
   "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
-{
-  if (TARGET_MICROMIPS)
-    return MICROMIPS_J ("j", operands, 0);
-  else
-    return MIPS_CALL ("j", operands, 0, 1);
-}
+  { return mips_output_jump (operands, 0, 1, false); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -6809,12 +6823,7 @@ (define_insn "sibcall_value_internal"
         (call (mem:SI (match_operand 1 "call_insn_operand" "j,S"))
               (match_operand 2 "" "")))]
   "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
-{
-  if (TARGET_MICROMIPS)
-    return MICROMIPS_J ("j", operands, 1);
-  else
-    return MIPS_CALL ("j", operands, 1, 2);
-}
+  { return mips_output_jump (operands, 1, 2, false); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -6826,12 +6835,7 @@ (define_insn "sibcall_value_multiple_internal"
 	(call (mem:SI (match_dup 1))
 	      (match_dup 2)))]
   "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
-{
-  if (TARGET_MICROMIPS)
-    return MICROMIPS_J ("j", operands, 1);
-  else
-    return MIPS_CALL ("j", operands, 1, 2);
-}
+  { return mips_output_jump (operands, 1, 2, false); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -6887,7 +6891,10 @@ (define_insn_and_split "call_internal"
 	 (match_operand 1 "" ""))
    (clobber (reg:SI RETURN_ADDR_REGNUM))]
   ""
-  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0, 1); }
+  {
+    return (TARGET_SPLIT_CALLS ? "#"
+	    : mips_output_jump (operands, 0, 1, true));
+  }
   "reload_completed && TARGET_SPLIT_CALLS"
   [(const_int 0)]
 {
@@ -6902,7 +6909,7 @@ (define_insn "call_split"
    (clobber (reg:SI RETURN_ADDR_REGNUM))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
-  { return MIPS_CALL ("jal", operands, 0, 1); }
+  { return mips_output_jump (operands, 0, 1, true); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -6916,7 +6923,10 @@ (define_insn_and_split "call_internal_direct"
    (const_int 1)
    (clobber (reg:SI RETURN_ADDR_REGNUM))]
   ""
-  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0, -1); }
+  {
+    return (TARGET_SPLIT_CALLS ? "#"
+	    : mips_output_jump (operands, 0, -1, true));
+  }
   "reload_completed && TARGET_SPLIT_CALLS"
   [(const_int 0)]
 {
@@ -6933,7 +6943,7 @@ (define_insn "call_direct_split"
    (clobber (reg:SI RETURN_ADDR_REGNUM))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
-  { return MIPS_CALL ("jal", operands, 0, -1); }
+  { return mips_output_jump (operands, 0, -1, true); }
   [(set_attr "jal" "direct")
    (set_attr "jal_macro" "no")])
 
@@ -6956,7 +6966,10 @@ (define_insn_and_split "call_value_internal"
               (match_operand 2 "" "")))
    (clobber (reg:SI RETURN_ADDR_REGNUM))]
   ""
-  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, 2); }
+  {
+    return (TARGET_SPLIT_CALLS ? "#"
+	    : mips_output_jump (operands, 1, 2, true));
+  }
   "reload_completed && TARGET_SPLIT_CALLS"
   [(const_int 0)]
 {
@@ -6974,7 +6987,7 @@ (define_insn "call_value_split"
    (clobber (reg:SI RETURN_ADDR_REGNUM))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
-  { return MIPS_CALL ("jal", operands, 1, 2); }
+  { return mips_output_jump (operands, 1, 2, true); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -6986,7 +6999,10 @@ (define_insn_and_split "call_value_internal_direct"
    (const_int 1)
    (clobber (reg:SI RETURN_ADDR_REGNUM))]
   ""
-  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, -1); }
+  {
+    return (TARGET_SPLIT_CALLS ? "#"
+	    : mips_output_jump (operands, 1, -1, true));
+  }
   "reload_completed && TARGET_SPLIT_CALLS"
   [(const_int 0)]
 {
@@ -7005,7 +7021,7 @@ (define_insn "call_value_direct_split"
    (clobber (reg:SI RETURN_ADDR_REGNUM))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
-  { return MIPS_CALL ("jal", operands, 1, -1); }
+  { return mips_output_jump (operands, 1, -1, true); }
   [(set_attr "jal" "direct")
    (set_attr "jal_macro" "no")])
 
@@ -7019,7 +7035,10 @@ (define_insn_and_split "call_value_multiple_internal"
 	      (match_dup 2)))
    (clobber (reg:SI RETURN_ADDR_REGNUM))]
   ""
-  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, 2); }
+  {
+    return (TARGET_SPLIT_CALLS ? "#"
+	    : mips_output_jump (operands, 1, 2, true));
+  }
   "reload_completed && TARGET_SPLIT_CALLS"
   [(const_int 0)]
 {
@@ -7040,7 +7059,7 @@ (define_insn "call_value_multiple_split"
    (clobber (reg:SI RETURN_ADDR_REGNUM))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
-  { return MIPS_CALL ("jal", operands, 1, 2); }
+  { return mips_output_jump (operands, 1, 2, true); }
   [(set_attr "jal" "indirect,direct")
    (set_attr "jal_macro" "no")])
 
@@ -7411,7 +7430,7 @@ (define_insn "*tls_get_tp_mips16_call_<mode>"
    (clobber (reg:P PIC_FUNCTION_ADDR_REGNUM))
    (clobber (reg:P RETURN_ADDR_REGNUM))]
   "HAVE_AS_TLS && TARGET_MIPS16"
-  { return MIPS_CALL ("jal", operands, 0, -1); }
+  { return mips_output_jump (operands, 0, -1, true); }
   [(set_attr "type" "call")
    (set_attr "insn_count" "3")
    (set_attr "mode" "<MODE>")])
@@ -7452,7 +7471,7 @@ (define_insn "mips_get_fcsr_mips16_<mode>"
    (clobber (reg:P PIC_FUNCTION_ADDR_REGNUM))
    (clobber (reg:P RETURN_ADDR_REGNUM))]
   "TARGET_HARD_FLOAT_ABI && TARGET_MIPS16"
-  { return MIPS_CALL ("jal", operands, 0, -1); }
+  { return mips_output_jump (operands, 0, -1, true); }
   [(set_attr "type" "call")
    (set_attr "insn_count" "3")])
 
@@ -7482,7 +7501,7 @@ (define_insn "mips_set_fcsr_mips16_<mode>"
    (clobber (reg:P PIC_FUNCTION_ADDR_REGNUM))
    (clobber (reg:P RETURN_ADDR_REGNUM))]
   "TARGET_HARD_FLOAT_ABI && TARGET_MIPS16"
-  { return MIPS_CALL ("jal", operands, 0, -1); }
+  { return mips_output_jump (operands, 0, -1, true); }
   [(set_attr "type" "call")
    (set_attr "insn_count" "3")])
 
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
index 348c6e0..84887d1 100644
--- a/gcc/config/mips/mips.opt
+++ b/gcc/config/mips/mips.opt
@@ -418,3 +418,20 @@ Driver
 mload-store-pairs
 Target Report Var(TARGET_LOAD_STORE_PAIRS) Init(1)
 Enable load/store bonding.
+
+mcompact-branches=
+Target RejectNegative JoinedOrMissing Var(mips_cb) Report Enum(mips_cb_setting) Init(MIPS_CB_OPTIMAL)
+Specify the compact branch usage policy
+
+Enum
+Name(mips_cb_setting) Type(enum mips_cb_setting)
+Policies available for use with -mcompact-branches=:
+
+EnumValue
+Enum(mips_cb_setting) String(never) Value(MIPS_CB_NEVER)
+
+EnumValue
+Enum(mips_cb_setting) String(optimal) Value(MIPS_CB_OPTIMAL)
+
+EnumValue
+Enum(mips_cb_setting) String(always) Value(MIPS_CB_ALWAYS)
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
index 4929c3d..3259232 100644
--- a/gcc/config/mips/predicates.md
+++ b/gcc/config/mips/predicates.md
@@ -475,7 +475,18 @@ (define_predicate "trap_comparison_operator"
   (match_code "eq,ne,lt,ltu,ge,geu"))
 
 (define_predicate "order_operator"
-  (match_code "lt,ltu,le,leu,ge,geu,gt,gtu"))
+  (match_code "lt,ltu,le,leu,ge,geu,gt,gtu")
+{
+  if (XEXP (op, 1) == const0_rtx)
+    return true;
+
+  if (TARGET_CB_MAYBE
+      && (GET_CODE (op) == LT || GET_CODE (op) == LTU
+	  || GET_CODE (op) == GE || GET_CODE (op) == GEU))
+    return true;
+
+  return false;
+})
 
 ;; For NE, cstore uses sltu instructions in which the first operand is $0.
 ;; This isn't possible in mips16 code.
diff --git a/gcc/testsuite/gcc.target/mips/branch-10.c b/gcc/testsuite/gcc.target/mips/branch-10.c
index eb21c16..9428254 100644
--- a/gcc/testsuite/gcc.target/mips/branch-10.c
+++ b/gcc/testsuite/gcc.target/mips/branch-10.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=n32" } */
 /* { dg-final { scan-assembler-not "(\\\$28|%gp_rel|%got)" } } */
-/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-11.c b/gcc/testsuite/gcc.target/mips/branch-11.c
index bd8e834..9238d9c 100644
--- a/gcc/testsuite/gcc.target/mips/branch-11.c
+++ b/gcc/testsuite/gcc.target/mips/branch-11.c
@@ -4,7 +4,7 @@
 /* { dg-final { scan-assembler "\taddiu\t\\\$28,\\\$28,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
 /* { dg-final { scan-assembler "\tlw\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$28\\)\n" } } */
 /* { dg-final { scan-assembler "\taddiu\t\\\$1,\\\$1,%got_ofst\\(\[^)\]*\\)\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-12.c b/gcc/testsuite/gcc.target/mips/branch-12.c
index 4944634..97261ac 100644
--- a/gcc/testsuite/gcc.target/mips/branch-12.c
+++ b/gcc/testsuite/gcc.target/mips/branch-12.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=64" } */
 /* { dg-final { scan-assembler-not "(\\\$28|%gp_rel|%got)" } } */
-/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-13.c b/gcc/testsuite/gcc.target/mips/branch-13.c
index f5269b9..5ea5f1b 100644
--- a/gcc/testsuite/gcc.target/mips/branch-13.c
+++ b/gcc/testsuite/gcc.target/mips/branch-13.c
@@ -4,7 +4,7 @@
 /* { dg-final { scan-assembler "\tdaddiu\t\\\$28,\\\$28,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
 /* { dg-final { scan-assembler "\tld\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$28\\)\n" } } */
 /* { dg-final { scan-assembler "\tdaddiu\t\\\$1,\\\$1,%got_ofst\\(\[^)\]*\\)\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-3.c b/gcc/testsuite/gcc.target/mips/branch-3.c
index 69300f6..310812a 100644
--- a/gcc/testsuite/gcc.target/mips/branch-3.c
+++ b/gcc/testsuite/gcc.target/mips/branch-3.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=32" } */
 /* { dg-final { scan-assembler "\t\\.cpload\t\\\$25\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 /* { dg-final { scan-assembler-not "\\.cprestore" } } */
 
 #include "branch-helper.h"
diff --git a/gcc/testsuite/gcc.target/mips/branch-4.c b/gcc/testsuite/gcc.target/mips/branch-4.c
index 29f5c9f..9dec904 100644
--- a/gcc/testsuite/gcc.target/mips/branch-4.c
+++ b/gcc/testsuite/gcc.target/mips/branch-4.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=n32" } */
 /* { dg-final { scan-assembler-not "(\\\$25|\\\$28|%gp_rel|%got)" } } */
-/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-5.c b/gcc/testsuite/gcc.target/mips/branch-5.c
index 0538646..60daf27 100644
--- a/gcc/testsuite/gcc.target/mips/branch-5.c
+++ b/gcc/testsuite/gcc.target/mips/branch-5.c
@@ -1,7 +1,7 @@
 /* { dg-options "-mshared -mabi=n32" } */
 /* { dg-final { scan-assembler "\taddiu\t\\\$3,\\\$3,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
 /* { dg-final { scan-assembler "\tlw\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$3\\)\\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 /* { dg-final { scan-assembler-not "\\\$28" } } */
 
 #include "branch-helper.h"
diff --git a/gcc/testsuite/gcc.target/mips/branch-6.c b/gcc/testsuite/gcc.target/mips/branch-6.c
index 19baee1..4262ba7 100644
--- a/gcc/testsuite/gcc.target/mips/branch-6.c
+++ b/gcc/testsuite/gcc.target/mips/branch-6.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=64" } */
 /* { dg-final { scan-assembler-not "(\\\$25|\\\$28|%gp_rel|%got)" } } */
-/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-7.c b/gcc/testsuite/gcc.target/mips/branch-7.c
index 16c6d8e..a0c28a2 100644
--- a/gcc/testsuite/gcc.target/mips/branch-7.c
+++ b/gcc/testsuite/gcc.target/mips/branch-7.c
@@ -1,7 +1,7 @@
 /* { dg-options "-mshared -mabi=64" } */
 /* { dg-final { scan-assembler "\tdaddiu\t\\\$3,\\\$3,%lo\\(%neg\\(%gp_rel\\(foo\\)\\)\\)\n" } } */
 /* { dg-final { scan-assembler "\tld\t\\\$1,%got_page\\(\[^)\]*\\)\\(\\\$3\\)\\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 /* { dg-final { scan-assembler-not "\\\$28" } } */
 
 #include "branch-helper.h"
diff --git a/gcc/testsuite/gcc.target/mips/branch-8.c b/gcc/testsuite/gcc.target/mips/branch-8.c
index 2e46844..5a5494e 100644
--- a/gcc/testsuite/gcc.target/mips/branch-8.c
+++ b/gcc/testsuite/gcc.target/mips/branch-8.c
@@ -1,6 +1,6 @@
 /* { dg-options "-mshared -mabi=32" } */
 /* { dg-final { scan-assembler-not "(\\\$28|cpload|cprestore)" } } */
-/* { dg-final { scan-assembler-not "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler-not "\tjrc?\t\\\$1\n" } } */
 
 #include "branch-helper.h"
 
diff --git a/gcc/testsuite/gcc.target/mips/branch-9.c b/gcc/testsuite/gcc.target/mips/branch-9.c
index b87f2ba..88a6d9a 100644
--- a/gcc/testsuite/gcc.target/mips/branch-9.c
+++ b/gcc/testsuite/gcc.target/mips/branch-9.c
@@ -4,7 +4,7 @@
 /* { dg-final { scan-assembler "\tlw\t\\\$1,16\\(\\\$(fp|sp)\\)\n" } } */
 /* { dg-final { scan-assembler "\tlw\t\\\$1,%got\\(\[^)\]*\\)\\(\\\$1\\)\n" } } */
 /* { dg-final { scan-assembler "\taddiu\t\\\$1,\\\$1,%lo\\(\[^)\]*\\)\n" } } */
-/* { dg-final { scan-assembler "\tjr\t\\\$1\n" } } */
+/* { dg-final { scan-assembler "\tjrc?\t\\\$1\n" } } */
 /* { dg-final { scan-assembler-not "\\\$28" } } */
 
 #include "branch-helper.h"
diff --git a/gcc/testsuite/gcc.target/mips/branch-cost-1.c b/gcc/testsuite/gcc.target/mips/branch-cost-1.c
index f72f2ac..61c3029 100644
--- a/gcc/testsuite/gcc.target/mips/branch-cost-1.c
+++ b/gcc/testsuite/gcc.target/mips/branch-cost-1.c
@@ -6,4 +6,4 @@ foo (int x, int y, int z, int k)
   return x == k ? x + y : z - x;
 }
 /* { dg-final { scan-assembler-not "\t(movz|movn)\t" } } */
-/* { dg-final { scan-assembler "\t(bne|beq)\t" } } */
+/* { dg-final { scan-assembler "\t(bnec?|beqc?)\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/call-1.c b/gcc/testsuite/gcc.target/mips/call-1.c
index a00126e..46a2536 100644
--- a/gcc/testsuite/gcc.target/mips/call-1.c
+++ b/gcc/testsuite/gcc.target/mips/call-1.c
@@ -1,12 +1,12 @@
 /* { dg-options "-mrelax-pic-calls -mshared -foptimize-sibling-calls -mabi=32" } */
 /* { dg-skip-if "requires -foptimize-sibling-calls" { *-*-* } { "-O0" } { "" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalrs?\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalrs?\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalrs?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail\n1:)?\tjrc?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail2\n1:)?\tjrc?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail3\n1:)?\tjrc?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail4\n1:)?\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalrc?s?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalrc?s?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalrc?s?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail\n1:\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail2\n1:\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail3\n1:\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail4\n1:\tjrc?\t" } } */
 
 __attribute__ ((noinline)) static void staticfunc () { asm (""); }
 int normal ();
diff --git a/gcc/testsuite/gcc.target/mips/call-2.c b/gcc/testsuite/gcc.target/mips/call-2.c
index 58cc2c6..175933c 100644
--- a/gcc/testsuite/gcc.target/mips/call-2.c
+++ b/gcc/testsuite/gcc.target/mips/call-2.c
@@ -1,6 +1,6 @@
 /* See through some simple data-flow.  */
 /* { dg-options "-mrelax-pic-calls" } */
-/* { dg-final { scan-assembler-times "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalrs?\t" 2 } } */
+/* { dg-final { scan-assembler-times "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalrc?s?\t" 2 } } */
 
 extern void g (void);
 
diff --git a/gcc/testsuite/gcc.target/mips/call-3.c b/gcc/testsuite/gcc.target/mips/call-3.c
index 4a662e3..08cf336 100644
--- a/gcc/testsuite/gcc.target/mips/call-3.c
+++ b/gcc/testsuite/gcc.target/mips/call-3.c
@@ -1,5 +1,5 @@
 /* { dg-options "-mrelax-pic-calls -mno-shared" } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalrs?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalrc?s?\t" } } */
 /* { dg-require-visibility "" } */
 
 __attribute__ ((visibility ("hidden"))) void g ();
diff --git a/gcc/testsuite/gcc.target/mips/call-4.c b/gcc/testsuite/gcc.target/mips/call-4.c
index a343c42..bf357c7 100644
--- a/gcc/testsuite/gcc.target/mips/call-4.c
+++ b/gcc/testsuite/gcc.target/mips/call-4.c
@@ -1,6 +1,6 @@
 /* See through some simple data-flow.  */
 /* { dg-options "-mrelax-pic-calls" } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalr\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,g\n1:\tjalrc?\t" } } */
 
 extern void g (void);
 
diff --git a/gcc/testsuite/gcc.target/mips/call-5.c b/gcc/testsuite/gcc.target/mips/call-5.c
index d8d84d3..f6ebae9 100644
--- a/gcc/testsuite/gcc.target/mips/call-5.c
+++ b/gcc/testsuite/gcc.target/mips/call-5.c
@@ -2,13 +2,13 @@
    in this case (PR target/57260).  */
 /* { dg-options "-mrelax-pic-calls -mshared -foptimize-sibling-calls -mabi=n32" } */
 /* { dg-skip-if "requires -foptimize-sibling-calls" { *-*-* } { "-O0" } { "" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail2\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail3\n1:)?\tjrc?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail4\n1:)?\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail2\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail3\n1:\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail4\n1:\tjrc?\t" } } */
 
 __attribute__ ((noinline)) static void staticfunc () { asm (""); }
 int normal ();
diff --git a/gcc/testsuite/gcc.target/mips/call-6.c b/gcc/testsuite/gcc.target/mips/call-6.c
index e6c90d7..00f4a1e 100644
--- a/gcc/testsuite/gcc.target/mips/call-6.c
+++ b/gcc/testsuite/gcc.target/mips/call-6.c
@@ -1,13 +1,13 @@
 /* Like call-5.c, but for n64.  */
 /* { dg-options "-mrelax-pic-calls -mshared -foptimize-sibling-calls -mabi=64" } */
 /* { dg-skip-if "requires -foptimize-sibling-calls" { *-*-* } { "-O0" } { "" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail2\n1:\tjalr\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail3\n1:)?\tjrc?\t" } } */
-/* { dg-final { scan-assembler "(\\.reloc\t1f,R_MIPS_JALR,tail4\n1:)?\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,normal2\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,staticfunc\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail2\n1:\tjalrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail3\n1:\tjrc?\t" } } */
+/* { dg-final { scan-assembler "\\.reloc\t1f,R_MIPS_JALR,tail4\n1:\tjrc?\t" } } */
 
 __attribute__ ((noinline)) static void staticfunc () { asm (""); }
 int normal ();
diff --git a/gcc/testsuite/gcc.target/mips/lazy-binding-1.c b/gcc/testsuite/gcc.target/mips/lazy-binding-1.c
index a305948..a112781 100644
--- a/gcc/testsuite/gcc.target/mips/lazy-binding-1.c
+++ b/gcc/testsuite/gcc.target/mips/lazy-binding-1.c
@@ -19,6 +19,6 @@ foo (int n)
 /* There should be exactly five uses of $25: one to set up $gp, two to
    load the address of bar (), and two to call it.  */
 /* { dg-final { scan-assembler-times "\tl.\t\\\$25,%call16\\\(bar\\\)" 2 } } */
-/* { dg-final { scan-assembler-times "\tjalrs?\t\\\$25" 2 } } */
+/* { dg-final { scan-assembler-times "\tjalrc?s?\t\\\$25" 2 } } */
 /* { dg-final { scan-assembler "(\\\$28,|\t.cpload\t)\\\$25" } } */
 /* { dg-final { scan-assembler-times "\\\$25" 5 } } */
diff --git a/gcc/testsuite/gcc.target/mips/mips.exp b/gcc/testsuite/gcc.target/mips/mips.exp
index b3617e4..1ac22a5 100644
--- a/gcc/testsuite/gcc.target/mips/mips.exp
+++ b/gcc/testsuite/gcc.target/mips/mips.exp
@@ -243,6 +243,7 @@ set mips_option_groups {
     mips16 "-mips16|-mno-mips16|-mflip-mips16"
     mips3d "-mips3d|-mno-mips3d"
     pic "-f(no-|)(pic|PIC)"
+    cb "-mcompact-branches=.*"
     profiling "-pg"
     small-data "-G[0-9]+"
     warnings "-w"
@@ -1068,8 +1069,10 @@ proc mips-dg-options { args } {
 	# We need a revision 6 or better ISA for:
 	#
 	#   - When the LSA instruction is required
+	#   - When only using compact branches
 	if { $isa_rev < 6
-	     && ([mips_have_test_option_p options "HAS_LSA"]) } {
+	     && ([mips_have_test_option_p options "HAS_LSA"]
+		 || [mips_have_test_option_p options "-mcompact-branches=always"]) } {
 	    if { $gp_size == 32 } {
 		mips_make_test_option options "-mips32r6"
 	    } else {
@@ -1164,6 +1167,9 @@ proc mips-dg-options { args } {
 		mips_make_test_option options "-mips64r5"
 	    }
 	    mips_make_test_option options "-mnan=2008"
+	    if { [mips_have_option_p options "-mcompact-branches=always"] } {
+		mips_make_test_option options "-mcompact-branches=optimal"
+	    }
 	# Check whether we need to switch from a 32-bit processor to the
 	# "nearest" 64-bit processor.
 	} elseif { $gp_size == 64 && [mips_32bit_arch_p $arch] } {
@@ -1307,6 +1313,11 @@ proc mips-dg-options { args } {
 	    mips_make_test_option options "-mno-micromips"
 	    mips_make_test_option options "-mnan=legacy"
 	}
+	if { $isa_rev < 6 } {
+	    if { [mips_have_option_p options "-mcompact-branches=always"] } {
+		mips_make_test_option options "-mcompact-branches=optimal"
+	    }
+	}
         if { $isa_rev > 5 } {
 	    mips_make_test_option options "-mno-dsp"
 	    mips_make_test_option options "-mno-mips16"
diff --git a/gcc/testsuite/gcc.target/mips/near-far-1.c b/gcc/testsuite/gcc.target/mips/near-far-1.c
index 8806e93..b746cf6 100644
--- a/gcc/testsuite/gcc.target/mips/near-far-1.c
+++ b/gcc/testsuite/gcc.target/mips/near-far-1.c
@@ -16,5 +16,5 @@ int test ()
 
 /* { dg-final { scan-assembler-not "\tjal\tlong_call_func\n" } } */
 /* { dg-final { scan-assembler-not "\tjal\tfar_func\n" } } */
-/* { dg-final { scan-assembler     "\tjal(|s)\tnear_func\n" } } */
+/* { dg-final { scan-assembler     "\t(jal(|s)|balc)\tnear_func\n" } } */
 /* { dg-final { scan-assembler-not "\tjal\tnormal_func\n" } } */
diff --git a/gcc/testsuite/gcc.target/mips/near-far-2.c b/gcc/testsuite/gcc.target/mips/near-far-2.c
index b4062a7..2c140e2 100644
--- a/gcc/testsuite/gcc.target/mips/near-far-2.c
+++ b/gcc/testsuite/gcc.target/mips/near-far-2.c
@@ -16,5 +16,5 @@ int test ()
 
 /* { dg-final { scan-assembler-not "\tjal(|s)\tlong_call_func\n" } } */
 /* { dg-final { scan-assembler-not "\tjal(|s)\tfar_func\n" } } */
-/* { dg-final { scan-assembler     "\tjal(|s)\tnear_func\n" } } */
-/* { dg-final { scan-assembler     "\tjal(|s)\tnormal_func\n" } } */
+/* { dg-final { scan-assembler     "\t(jal(|s)|balc)\tnear_func\n" } } */
+/* { dg-final { scan-assembler     "\t(jal(|s)|balc)\tnormal_func\n" } } */
diff --git a/gcc/testsuite/gcc.target/mips/near-far-3.c b/gcc/testsuite/gcc.target/mips/near-far-3.c
index e6af939..7bf3e14 100644
--- a/gcc/testsuite/gcc.target/mips/near-far-3.c
+++ b/gcc/testsuite/gcc.target/mips/near-far-3.c
@@ -13,5 +13,5 @@ NOMIPS16 int test4 () { return normal_func (); }
 
 /* { dg-final { scan-assembler-not "\tj\tlong_call_func\n" } } */
 /* { dg-final { scan-assembler-not "\tj\tfar_func\n" } } */
-/* { dg-final { scan-assembler     "\tj(|al|als)\tnear_func\n" } } */
+/* { dg-final { scan-assembler     "\t(j(|al|als)|b(|al)c)\tnear_func\n" } } */
 /* { dg-final { scan-assembler-not "\tj\tnormal_func\n" } } */
diff --git a/gcc/testsuite/gcc.target/mips/near-far-4.c b/gcc/testsuite/gcc.target/mips/near-far-4.c
index 969f68f..cd12a1d 100644
--- a/gcc/testsuite/gcc.target/mips/near-far-4.c
+++ b/gcc/testsuite/gcc.target/mips/near-far-4.c
@@ -13,5 +13,5 @@ NOMIPS16 int test4 () { return normal_func (); }
 
 /* { dg-final { scan-assembler-not "\tj\tlong_call_func\n" } } */
 /* { dg-final { scan-assembler-not "\tj\tfar_func\n" } } */
-/* { dg-final { scan-assembler     "\tj(|al|als)\tnear_func\n" } } */
-/* { dg-final { scan-assembler     "\tj(|al|als)\tnormal_func\n" } } */
+/* { dg-final { scan-assembler     "\t(j(|al|als)|b(|al)c)\tnear_func\n" } } */
+/* { dg-final { scan-assembler     "\t(j(|al|als)|b(|al)c)\tnormal_func\n" } } */
diff --git a/gcc/testsuite/gcc.target/mips/umips-branch-3.c b/gcc/testsuite/gcc.target/mips/umips-branch-3.c
index 8717362..74465c9 100644
--- a/gcc/testsuite/gcc.target/mips/umips-branch-3.c
+++ b/gcc/testsuite/gcc.target/mips/umips-branch-3.c
@@ -1,4 +1,4 @@
-/* { dg-options "(-mmicromips)" } */
+/* { dg-options "(-mmicromips) -mcompact-branches=optimal" } */
 /* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
 
 void MICROMIPS
-- 
2.2.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]