OSDN Git Service

* config/mips/mips.md (any_gt, any_ge, any_lt, any_le): New code
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.md
index 864013a..2083cb9 100644 (file)
@@ -30,7 +30,7 @@
    (UNSPEC_GET_FNADDR           3)
    (UNSPEC_BLOCKAGE             4)
    (UNSPEC_CPRESTORE            5)
-   (UNSPEC_EH_RECEIVER          6)
+   (UNSPEC_RESTORE_GP           6)
    (UNSPEC_EH_RETURN            7)
    (UNSPEC_CONSTTABLE_INT       8)
    (UNSPEC_CONSTTABLE_FLOAT     9)
    (UNSPEC_SYNC_NEW_OP         39)
    (UNSPEC_SYNC_EXCHANGE       40)
    (UNSPEC_MEMORY_BARRIER      41)
+   (UNSPEC_SET_GOT_VERSION     42)
+   (UNSPEC_UPDATE_GOT_VERSION  43)
    
    (UNSPEC_ADDRESS_FIRST       100)
 
-   (FAKE_CALL_REGNO            79)
+   (GOT_VERSION_REGNUM         79)
 
    ;; For MIPS Paired-Singled Floating Point Instructions.
 
 ;; frsqrt2      floating point reciprocal square root step2
 ;; multi       multiword sequence (or user asm statements)
 ;; nop         no operation
+;; ghost       an instruction that produces no real code
 (define_attr "type"
-  "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop"
+  "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop,ghost"
   (cond [(eq_attr "jal" "!unset") (const_string "call")
         (eq_attr "got" "load") (const_string "load")]
        (const_string "unknown")))
         (const_string "hilo")]
        (const_string "none")))
 
-;; Indicates which SET in an instruction pattern induces a hazard.
-;; Only meaningful when "hazard" is not "none".  SINGLE means that
-;; the pattern has only one set while the other values are indexes
-;; into a PARALLEL vector.
-;;
-;; Hazardous instructions with multiple sets should generally put the
-;; hazardous set first.  The only purpose of this attribute is to force
-;; each multi-set pattern to explicitly assert that this condition holds.
-(define_attr "hazard_set" "single,0"
-  (const_string "single"))
-
 ;; Is it a single instruction?
 (define_attr "single_insn" "no,yes"
   (symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)"))
 ;; by swapping the operands.
 (define_code_iterator swapped_fcond [ge gt unge ungt])
 
+;; These code iterators allow the signed and unsigned scc operations to use
+;; the same template.
+(define_code_iterator any_gt [gt gtu])
+(define_code_iterator any_ge [ge geu])
+(define_code_iterator any_lt [lt ltu])
+(define_code_iterator any_le [le leu])
+
 ;; <u> expands to an empty string when doing a signed operation and
 ;; "u" when doing an unsigned operation.
-(define_code_attr u [(sign_extend "") (zero_extend "u")])
+(define_code_attr u [(sign_extend "") (zero_extend "u")
+                    (gt "") (gtu "u")
+                    (ge "") (geu "u")
+                    (lt "") (ltu "u")
+                    (le "") (leu "u")])
 
 ;; <su> is like <u>, but the signed form expands to "s" rather than "".
 (define_code_attr su [(sign_extend "s") (zero_extend "u")])
 (define_cpu_unit "alu" "alu")
 (define_cpu_unit "imuldiv" "imuldiv")
 
+;; Ghost instructions produce no real code and introduce no hazards.
+;; They exist purely to express an effect on dataflow.
+(define_insn_reservation "ghost" 0
+  (eq_attr "type" "ghost")
+  "nothing")
+
 (include "4k.md")
 (include "5k.md")
 (include "20kc.md")
   "<divide_condition>"
 {
   if (const_1_operand (operands[1], <MODE>mode))
-    if (!(ISA_HAS_FP4 && flag_unsafe_math_optimizations))
+    if (!(<recip_condition> && flag_unsafe_math_optimizations))
       operands[1] = force_reg (<MODE>mode, operands[1]);
 })
 
    (set_attr "mode" "<HALFMODE>")])
 
 ;; Move a constant that satisfies CONST_GP_P into operand 0.
-(define_expand "load_const_gp"
-  [(set (match_operand 0 "register_operand" "=d")
-       (const (unspec [(const_int 0)] UNSPEC_GP)))])
+(define_expand "load_const_gp_<mode>"
+  [(set (match_operand:P 0 "register_operand" "=d")
+       (const:P (unspec:P [(const_int 0)] UNSPEC_GP)))])
 
 ;; Insn to initialize $gp for n32/n64 abicalls.  Operand 0 is the offset
 ;; of _gp from the start of this function.  Operand 1 is the incoming
   [(unspec_volatile [(reg:DI 28)] UNSPEC_BLOCKAGE)]
   ""
   ""
-  [(set_attr "type"    "unknown")
-   (set_attr "mode"    "none")
-   (set_attr "length"  "0")])
+  [(set_attr "type" "ghost")
+   (set_attr "mode" "none")
+   (set_attr "length" "0")])
 
 ;; Initialize $gp for RTP PIC.  Operand 0 is the __GOTT_BASE__ symbol
 ;; and operand 1 is the __GOTT_INDEX__ symbol.
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_expand "sgt"
-  [(set (match_operand:SI 0 "register_operand")
-       (gt:SI (match_dup 1)
-              (match_dup 2)))]
-  ""
-  { if (mips_expand_scc (GT, operands[0])) DONE; else FAIL; })
-
-(define_insn "*sgt_<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d")
-       (gt:GPR (match_operand:GPR 1 "register_operand" "d")
-               (match_operand:GPR 2 "reg_or_0_operand" "dJ")))]
-  "!TARGET_MIPS16"
-  "slt\t%0,%z2,%1"
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")])
-
-(define_insn "*sgt_<mode>_mips16"
-  [(set (match_operand:GPR 0 "register_operand" "=t")
-       (gt:GPR (match_operand:GPR 1 "register_operand" "d")
-               (match_operand:GPR 2 "register_operand" "d")))]
-  "TARGET_MIPS16"
-  "slt\t%2,%1"
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")])
-
-(define_expand "sge"
-  [(set (match_operand:SI 0 "register_operand")
-       (ge:SI (match_dup 1)
-              (match_dup 2)))]
-  ""
-  { if (mips_expand_scc (GE, operands[0])) DONE; else FAIL; })
-
-(define_insn "*sge_<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d")
-       (ge:GPR (match_operand:GPR 1 "register_operand" "d")
-               (const_int 1)))]
-  "!TARGET_MIPS16"
-  "slt\t%0,%.,%1"
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")])
-
-(define_expand "slt"
-  [(set (match_operand:SI 0 "register_operand")
-       (lt:SI (match_dup 1)
-              (match_dup 2)))]
-  ""
-  { if (mips_expand_scc (LT, operands[0])) DONE; else FAIL; })
-
-(define_insn "*slt_<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d")
-       (lt:GPR (match_operand:GPR 1 "register_operand" "d")
-               (match_operand:GPR 2 "arith_operand" "dI")))]
-  "!TARGET_MIPS16"
-  "slt\t%0,%1,%2"
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")])
-
-(define_insn "*slt_<mode>_mips16"
-  [(set (match_operand:GPR 0 "register_operand" "=t,t")
-       (lt:GPR (match_operand:GPR 1 "register_operand" "d,d")
-               (match_operand:GPR 2 "arith_operand" "d,I")))]
-  "TARGET_MIPS16"
-  "slt\t%1,%2"
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")
-   (set_attr_alternative "length"
-               [(const_int 4)
-                (if_then_else (match_operand 2 "m16_uimm8_1")
-                              (const_int 4)
-                              (const_int 8))])])
-
-(define_expand "sle"
-  [(set (match_operand:SI 0 "register_operand")
-       (le:SI (match_dup 1)
-              (match_dup 2)))]
-  ""
-  { if (mips_expand_scc (LE, operands[0])) DONE; else FAIL; })
-
-(define_insn "*sle_<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d")
-       (le:GPR (match_operand:GPR 1 "register_operand" "d")
-               (match_operand:GPR 2 "sle_operand" "")))]
-  "!TARGET_MIPS16"
-{
-  operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
-  return "slt\t%0,%1,%2";
-}
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")])
-
-(define_insn "*sle_<mode>_mips16"
-  [(set (match_operand:GPR 0 "register_operand" "=t")
-       (le:GPR (match_operand:GPR 1 "register_operand" "d")
-               (match_operand:GPR 2 "sle_operand" "")))]
-  "TARGET_MIPS16"
-{
-  operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
-  return "slt\t%1,%2";
-}
-  [(set_attr "type" "slt")
-   (set_attr "mode" "<MODE>")
-   (set (attr "length") (if_then_else (match_operand 2 "m16_uimm8_m1_1")
-                                     (const_int 4)
-                                     (const_int 8)))])
-
-(define_expand "sgtu"
+(define_expand "sgt<u>"
   [(set (match_operand:SI 0 "register_operand")
-       (gtu:SI (match_dup 1)
-               (match_dup 2)))]
+       (any_gt:SI (match_dup 1)
+                  (match_dup 2)))]
   ""
-  { if (mips_expand_scc (GTU, operands[0])) DONE; else FAIL; })
+  { if (mips_expand_scc (<CODE>, operands[0])) DONE; else FAIL; })
 
-(define_insn "*sgtu_<mode>"
+(define_insn "*sgt<u>_<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=d")
-       (gtu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (match_operand:GPR 2 "reg_or_0_operand" "dJ")))]
+       (any_gt:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (match_operand:GPR 2 "reg_or_0_operand" "dJ")))]
   "!TARGET_MIPS16"
-  "sltu\t%0,%z2,%1"
+  "slt<u>\t%0,%z2,%1"
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_insn "*sgtu_<mode>_mips16"
+(define_insn "*sgt<u>_<mode>_mips16"
   [(set (match_operand:GPR 0 "register_operand" "=t")
-       (gtu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (match_operand:GPR 2 "register_operand" "d")))]
+       (any_gt:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (match_operand:GPR 2 "register_operand" "d")))]
   "TARGET_MIPS16"
-  "sltu\t%2,%1"
+  "slt<u>\t%2,%1"
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_expand "sgeu"
+(define_expand "sge<u>"
   [(set (match_operand:SI 0 "register_operand")
-        (geu:SI (match_dup 1)
-                (match_dup 2)))]
+       (any_ge:SI (match_dup 1)
+                  (match_dup 2)))]
   ""
-  { if (mips_expand_scc (GEU, operands[0])) DONE; else FAIL; })
+  { if (mips_expand_scc (<CODE>, operands[0])) DONE; else FAIL; })
 
-(define_insn "*sge_<mode>"
+(define_insn "*sge<u>_<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=d")
-       (geu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (const_int 1)))]
+       (any_ge:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (const_int 1)))]
   "!TARGET_MIPS16"
-  "sltu\t%0,%.,%1"
+  "slt<u>\t%0,%.,%1"
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_expand "sltu"
+(define_expand "slt<u>"
   [(set (match_operand:SI 0 "register_operand")
-       (ltu:SI (match_dup 1)
-               (match_dup 2)))]
+       (any_lt:SI (match_dup 1)
+                  (match_dup 2)))]
   ""
-  { if (mips_expand_scc (LTU, operands[0])) DONE; else FAIL; })
+  { if (mips_expand_scc (<CODE>, operands[0])) DONE; else FAIL; })
 
-(define_insn "*sltu_<mode>"
+(define_insn "*slt<u>_<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=d")
-       (ltu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (match_operand:GPR 2 "arith_operand" "dI")))]
+       (any_lt:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (match_operand:GPR 2 "arith_operand" "dI")))]
   "!TARGET_MIPS16"
-  "sltu\t%0,%1,%2"
+  "slt<u>\t%0,%1,%2"
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_insn "*sltu_<mode>_mips16"
+(define_insn "*slt<u>_<mode>_mips16"
   [(set (match_operand:GPR 0 "register_operand" "=t,t")
-       (ltu:GPR (match_operand:GPR 1 "register_operand" "d,d")
-                (match_operand:GPR 2 "arith_operand" "d,I")))]
+       (any_lt:GPR (match_operand:GPR 1 "register_operand" "d,d")
+                   (match_operand:GPR 2 "arith_operand" "d,I")))]
   "TARGET_MIPS16"
-  "sltu\t%1,%2"
+  "slt<u>\t%1,%2"
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")
    (set_attr_alternative "length"
                               (const_int 4)
                               (const_int 8))])])
 
-(define_expand "sleu"
+(define_expand "sle<u>"
   [(set (match_operand:SI 0 "register_operand")
-       (leu:SI (match_dup 1)
-               (match_dup 2)))]
+       (any_le:SI (match_dup 1)
+                  (match_dup 2)))]
   ""
-  { if (mips_expand_scc (LEU, operands[0])) DONE; else FAIL; })
+  { if (mips_expand_scc (<CODE>, operands[0])) DONE; else FAIL; })
 
-(define_insn "*sleu_<mode>"
+(define_insn "*sle<u>_<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=d")
-       (leu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (match_operand:GPR 2 "sleu_operand" "")))]
+       (any_le:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (match_operand:GPR 2 "sle_operand" "")))]
   "!TARGET_MIPS16"
 {
   operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
-  return "sltu\t%0,%1,%2";
+  return "slt<u>\t%0,%1,%2";
 }
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")])
 
-(define_insn "*sleu_<mode>_mips16"
+(define_insn "*sle<u>_<mode>_mips16"
   [(set (match_operand:GPR 0 "register_operand" "=t")
-       (leu:GPR (match_operand:GPR 1 "register_operand" "d")
-                (match_operand:GPR 2 "sleu_operand" "")))]
+       (any_le:GPR (match_operand:GPR 1 "register_operand" "d")
+                   (match_operand:GPR 2 "sle_operand" "")))]
   "TARGET_MIPS16"
 {
   operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
-  return "sltu\t%1,%2";
+  return "slt<u>\t%1,%2";
 }
   [(set_attr "type" "slt")
    (set_attr "mode" "<MODE>")
   [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
   ""
   ""
-  [(set_attr "type"    "unknown")
-   (set_attr "mode"    "none")
-   (set_attr "length"  "0")])
+  [(set_attr "type" "ghost")
+   (set_attr "mode" "none")
+   (set_attr "length" "0")])
 
 (define_expand "epilogue"
   [(const_int 2)]
   DONE;
 })
 
-(define_insn_and_split "exception_receiver"
+(define_expand "exception_receiver"
+  [(const_int 0)]
+  "TARGET_USE_GOT"
+{
+  /* See the comment above load_call<mode> for details.  */
+  emit_insn (gen_set_got_version ());
+
+  /* If we have a call-clobbered $gp, restore it from its save slot.  */
+  if (HAVE_restore_gp)
+    emit_insn (gen_restore_gp ());
+  DONE;
+})
+
+(define_expand "nonlocal_goto_receiver"
+  [(const_int 0)]
+  "TARGET_USE_GOT"
+{
+  /* See the comment above load_call<mode> for details.  */
+  emit_insn (gen_set_got_version ());
+  DONE;
+})
+
+;; Restore $gp from its .cprestore stack slot.  The instruction remains
+;; volatile until all uses of $28 are exposed.
+(define_insn_and_split "restore_gp"
   [(set (reg:SI 28)
-       (unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))]
+       (unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))]
   "TARGET_CALL_CLOBBERED_GP"
   "#"
   "&& reload_completed"
 ;; potentially modify the GOT entry.  And once a stub has been called,
 ;; we must not call it again.
 ;;
-;; We represent this restriction using an imaginary fixed register that
-;; is set by the GOT load and used by the call.  By making this register
-;; call-clobbered, and by making the GOT load the only way of setting
-;; the register, we ensure that the load cannot be moved past a call.
+;; We represent this restriction using an imaginary, fixed, call-saved
+;; register called GOT_VERSION_REGNUM.  The idea is to make the register
+;; live throughout the function and to change its value after every
+;; potential call site.  This stops any rtx value that uses the register
+;; from being computed before an earlier call.  To do this, we:
+;;
+;;    - Ensure that the register is live on entry to the function,
+;;     so that it is never thought to be used uninitalized.
+;;
+;;    - Ensure that the register is live on exit from the function,
+;;     so that it is live throughout.
+;;
+;;    - Make each call (lazily-bound or not) use the current value
+;;     of GOT_VERSION_REGNUM, so that updates of the register are
+;;     not moved across call boundaries.
+;;
+;;    - Add "ghost" definitions of the register to the beginning of
+;;     blocks reached by EH and ABNORMAL_CALL edges, because those
+;;     edges may involve calls that normal paths don't.  (E.g. the
+;;     unwinding code that handles a non-call exception may change
+;;     lazily-bound GOT entries.)  We do this by making the
+;;     exception_receiver and nonlocal_goto_receiver expanders emit
+;;     a set_got_version instruction.
+;;
+;;    - After each call (lazily-bound or not), use a "ghost"
+;;     update_got_version instruction to change the register's value.
+;;     This instruction mimics the _possible_ effect of the dynamic
+;;     resolver during the call and it remains live even if the call
+;;     itself becomes dead.
+;;
+;;    - Leave GOT_VERSION_REGNUM out of all register classes.
+;;     The register is therefore not a valid register_operand
+;;     and cannot be moved to or from other registers.
 (define_insn "load_call<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
        (unspec:P [(match_operand:P 1 "register_operand" "r")
-                  (match_operand:P 2 "immediate_operand" "")]
-                 UNSPEC_LOAD_CALL))
-   (set (reg:P FAKE_CALL_REGNO)
-       (unspec:P [(match_dup 2)] UNSPEC_LOAD_CALL))]
+                  (match_operand:P 2 "immediate_operand" "")
+                  (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
   "TARGET_USE_GOT"
   "<load>\t%0,%R2(%1)"
   [(set_attr "type" "load")
    (set_attr "mode" "<MODE>")
-   (set_attr "hazard_set" "0")
    (set_attr "length" "4")])
 
+(define_insn "set_got_version"
+  [(set (reg:SI GOT_VERSION_REGNUM)
+       (unspec_volatile:SI [(const_int 0)] UNSPEC_SET_GOT_VERSION))]
+  "TARGET_USE_GOT"
+  ""
+  [(set_attr "length" "0")
+   (set_attr "type" "ghost")])
+
+(define_insn "update_got_version"
+  [(set (reg:SI GOT_VERSION_REGNUM)
+       (unspec:SI [(reg:SI GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))]
+  "TARGET_USE_GOT"
+  ""
+  [(set_attr "length" "0")
+   (set_attr "type" "ghost")])
+
 ;; Sibling calls.  All these patterns use jump instructions.
 
 ;; If TARGET_SIBCALLS, call_insn_operand will only accept constant