diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 878a422fb846a896205ae751334f58be31dfd6d8..a399390cd199c5cfe41bf31bf37675314cf7d391 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,12 @@
+2002-12-12  Alexandre Oliva  <aoliva@redhat.com>
+
+	* config/mips/mips.c (mips_output_conditional_branch): Support
+	PIC-safe out-of-range branch and branch-likely.
+	* config/mips/mips.md (attr length): PIC-safe out-of-range
+	branches are longer.
+	("jump"): Support PIC-safe out-of-range-for-branch jumps.  Remove
+	unused code to support indirect jumps.
+
 2002-12-11  John David Anglin  <dave@hiauly1.hia.nrc.ca>
 
 	* pa.h (BIGGEST_ALIGNMENT): Change 32-bit value to 64 bits.
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index fa326abc92838fff9d7cae765e2ef52b8c30ce7b..2dcf69d3f43a5d6577de56acfa57ba9e5d17589c 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -10177,6 +10177,8 @@ mips_output_conditional_branch (insn,
 
     case 12:
     case 16:
+    case 24:
+    case 28:
       {
 	/* Generate a reversed conditional branch around ` j'
 	   instruction:
@@ -10184,18 +10186,41 @@ mips_output_conditional_branch (insn,
 		.set noreorder
 		.set nomacro
 		bc    l
-		nop
+		delay_slot or #nop
 		j     target
+		#nop
+	     l:
 		.set macro
 		.set reorder
+
+	   If the original branch was a likely branch, the delay slot
+	   must be executed only if the branch is taken, so generate:
+
+		.set noreorder
+		.set nomacro
+		bc    l
+		#nop
+		j     target
+		delay slot or #nop
 	     l:
+		.set macro
+		.set reorder
+	   
+	   When generating non-embedded PIC, instead of:
+
+	        j     target
+
+	   we emit:
 
+	        .set noat
+	        la    $at, target
+		jr    $at
+		.set at
 	*/
 
         rtx orig_target;
 	rtx target = gen_label_rtx ();
 
-        output_asm_insn ("%(%<", 0);
         orig_target = operands[1];
         operands[1] = target;
 	/* Generate the reversed comparison.  This takes four
@@ -10210,13 +10235,29 @@ mips_output_conditional_branch (insn,
 		   op1,
 		   op2);
         output_asm_insn (buffer, operands);
-        operands[1] = orig_target;
 
-	output_asm_insn ("nop\n\tj\t%1", operands);
+        if (length != 16 && length != 28 && ! mips_branch_likely)
+          {
+            /* Output delay slot instruction.  */
+            rtx insn = final_sequence;
+            final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
+                             optimize, 0, 1);
+            INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
+          }
+	else
+	  output_asm_insn ("%#", 0);
 
-        if (length == 16)
-	  output_asm_insn ("nop", 0);
-        else
+	if (length <= 16)
+	  output_asm_insn ("j\t%0", &orig_target);
+	else
+	  {
+	    if (Pmode == DImode)
+	      output_asm_insn ("%[dla\t%@,%0\n\tjr\t%@%]", &orig_target);
+	    else
+	      output_asm_insn ("%[la\t%@,%0\n\tjr\t%@%]", &orig_target);
+	  }
+
+        if (length != 16 && length != 28 && mips_branch_likely)
           {
             /* Output delay slot instruction.  */
             rtx insn = final_sequence;
@@ -10224,9 +10265,12 @@ mips_output_conditional_branch (insn,
                              optimize, 0, 1);
             INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
           }
-	output_asm_insn ("%>%)", 0);
+	else
+	  output_asm_insn ("%#", 0);
+
         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
                                    CODE_LABEL_NUMBER (target));
+
         return "";
       }
 
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 5d47e068b08d37254a62a64e6b41b788ade82c59..cc6cda53dba0df563a65e77939a664150ef0a674 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -105,9 +105,12 @@
    (cond [(eq_attr "type" "branch")
           (cond [(lt (abs (minus (match_dup 1) (plus (pc) (const_int 4))))
                      (const_int 131072))
-                 (const_int 4)]
-	         (const_int 12))]
-          (const_int 4)))
+                 (const_int 4)
+		 (ne (symbol_ref "flag_pic && ! TARGET_EMBEDDED_PIC")
+		     (const_int 0))
+		 (const_int 24)
+		 ] (const_int 12))
+	  ] (const_int 4)))
 
 ;; Attribute describing the processor.  This attribute must match exactly
 ;; with the processor_type enumeration in mips.h.
@@ -9592,18 +9595,31 @@ move\\t%0,%z4\\n\\
   "!TARGET_MIPS16"
   "*
 {
-  if (GET_CODE (operands[0]) == REG)
-    return \"%*j\\t%0\";
-  /* ??? I don't know why this is necessary.  This works around an
-     assembler problem that appears when a label is defined, then referenced
-     in a switch table, then used in a `j' instruction.  */
-  else if (mips_abi != ABI_32 && mips_abi != ABI_O64)
-    return \"%*b\\t%l0\";
+  if (flag_pic && ! TARGET_EMBEDDED_PIC)
+    {
+      if (get_attr_length (insn) <= 8)
+	return \"%*b\\t%l0\";
+      else if (Pmode == DImode)
+	return \"%[dla\\t%@,%l0\;%*jr\\t%@%]\";
+      else
+	return \"%[la\\t%@,%l0\;%*jr\\t%@%]\";
+    }
   else
     return \"%*j\\t%l0\";
 }"
   [(set_attr "type"	"jump")
-   (set_attr "mode"	"none")])
+   (set_attr "mode"	"none")
+   (set (attr "length")
+	;; we can't use `j' when emitting non-embedded PIC, so we emit
+	;; branch, if it's in range, or load the address of the branch
+	;; target into $at in a PIC-compatible way and then jump to it.
+	(if_then_else 
+	 (ior (eq (symbol_ref "flag_pic && ! TARGET_EMBEDDED_PIC")
+		  (const_int 0))
+	      (lt (abs (minus (match_dup 0)
+			      (plus (pc) (const_int 4))))
+		  (const_int 131072)))
+	 (const_int 4) (const_int 16)))])
 
 ;; We need a different insn for the mips16, because a mips16 branch
 ;; does not have a delay slot.
@@ -9611,7 +9627,7 @@ move\\t%0,%z4\\n\\
 (define_insn ""
   [(set (pc)
 	(label_ref (match_operand 0 "" "")))]
-  "TARGET_MIPS16 && GET_CODE (operands[0]) != REG"
+  "TARGET_MIPS16"
   "b\\t%l0"
   [(set_attr "type"	"branch")
    (set_attr "mode"	"none")