OSDN Git Service

2006-01-26 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / config / i386 / i386.md
index 849920e..39d542d 100644 (file)
@@ -1,6 +1,6 @@
 ;; GCC machine description for IA-32 and x86-64.
 ;; Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-;; 2001, 2002, 2003, 2004, 2005
+;; 2001, 2002, 2003, 2004, 2005, 2006
 ;; Free Software Foundation, Inc.
 ;; Mostly by William Schelter.
 ;; x86_64 support added by Jan Hubicka
    (UNSPEC_STACK_ALLOC         11)
    (UNSPEC_SET_GOT             12)
    (UNSPEC_SSE_PROLOGUE_SAVE   13)
+   (UNSPEC_REG_SAVE            14)
+   (UNSPEC_DEF_CFA             15)
 
    ; TLS support
-   (UNSPEC_TP                  15)
-   (UNSPEC_TLS_GD              16)
-   (UNSPEC_TLS_LD_BASE         17)
+   (UNSPEC_TP                  16)
+   (UNSPEC_TLS_GD              17)
+   (UNSPEC_TLS_LD_BASE         18)
+   (UNSPEC_TLSDESC             19)
 
    ; Other random patterns
    (UNSPEC_SCAS                        20)
@@ -81,6 +84,7 @@
    (UNSPEC_FLDCW               25)
    (UNSPEC_REP                 26)
    (UNSPEC_EH_RETURN           27)
+   (UNSPEC_LD_MPIC             28)     ; load_macho_picbase
 
    ; For SSE/MMX support:
    (UNSPEC_FIX_NOTRUNC         30)
 \f
 ;; Processor type.  This attribute must exactly match the processor_type
 ;; enumeration in i386.h.
-(define_attr "cpu" "i386,i486,pentium,pentiumpro,k6,athlon,pentium4,k8,nocona"
+(define_attr "cpu" "i386,i486,pentium,pentiumpro,k6,athlon,pentium4,k8,nocona,generic32,generic64"
   (const (symbol_ref "ix86_tune")))
 
 ;; A basic instruction type.  Refinements due to arguments to be
              (not (match_operand 0 "memory_operand" "")))
           (const_int 0)
         (and (eq_attr "type" "imov")
-             (and (match_operand 0 "register_operand" "")
-                  (match_operand 1 "immediate_operand" "")))
+             (ior (and (match_operand 0 "register_operand" "")
+                       (match_operand 1 "immediate_operand" ""))
+                  (ior (and (match_operand 0 "ax_reg_operand" "")
+                            (match_operand 1 "memory_displacement_only_operand" ""))
+                       (and (match_operand 0 "memory_displacement_only_operand" "")
+                            (match_operand 1 "ax_reg_operand" "")))))
           (const_int 0)
         (and (eq_attr "type" "call")
              (match_operand 0 "constant_call_address_operand" ""))
   "ix86_expand_move (HImode, operands); DONE;")
 
 (define_insn "*pushhi2"
-  [(set (match_operand:HI 0 "push_operand" "=<,<")
-       (match_operand:HI 1 "general_no_elim_operand" "n,r*m"))]
+  [(set (match_operand:HI 0 "push_operand" "=X")
+       (match_operand:HI 1 "nonmemory_no_elim_operand" "rn"))]
   "!TARGET_64BIT"
-  "@
-   push{w}\t{|WORD PTR }%1
-   push{w}\t%1"
+  "push{l}\t%k1"
   [(set_attr "type" "push")
-   (set_attr "mode" "HI")])
+   (set_attr "mode" "SI")])
 
 ;; For 64BIT abi we always round up to 8 bytes.
 (define_insn "*pushhi2_rex64"
   "TARGET_64BIT"
   "push{q}\t%q1"
   [(set_attr "type" "push")
-   (set_attr "mode" "QI")])
+   (set_attr "mode" "DI")])
 
 (define_insn "*movhi_1"
   [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m")
   "ix86_expand_move (QImode, operands); DONE;")
 
 ;; emit_push_insn when it calls move_by_pieces requires an insn to
-;; "push a byte".  But actually we use pushw, which has the effect
-;; of rounding the amount pushed up to a halfword.
+;; "push a byte".  But actually we use pushl, which has the effect
+;; of rounding the amount pushed up to a word.
 
 (define_insn "*pushqi2"
-  [(set (match_operand:QI 0 "push_operand" "=X,X")
-       (match_operand:QI 1 "nonmemory_no_elim_operand" "n,r"))]
+  [(set (match_operand:QI 0 "push_operand" "=X")
+       (match_operand:QI 1 "nonmemory_no_elim_operand" "rn"))]
   "!TARGET_64BIT"
-  "@
-   push{w}\t{|word ptr }%1
-   push{w}\t%w1"
+  "push{l}\t%k1"
   [(set_attr "type" "push")
-   (set_attr "mode" "HI")])
+   (set_attr "mode" "SI")])
 
 ;; For 64BIT abi we always round up to 8 bytes.
 (define_insn "*pushqi2_rex64"
   "TARGET_64BIT"
   "push{q}\t%q1"
   [(set_attr "type" "push")
-   (set_attr "mode" "QI")])
+   (set_attr "mode" "DI")])
 
 ;; Situation is quite tricky about when to choose full sized (SImode) move
 ;; over QImode moves.  For Q_REG -> Q_REG move we use full size only for
 ;; partial register stall can be caused there.  Then we use movzx.
 (define_insn "*movqi_1"
   [(set (match_operand:QI 0 "nonimmediate_operand" "=q,q ,q ,r,r ,?r,m")
-       (match_operand:QI 1 "general_operand"      " q,qn,qm,q,rn,,qn"))]
+       (match_operand:QI 1 "general_operand"      " q,qn,qm,q,rn,qm,qn"))]
   "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
 {
   switch (get_attr_type (insn))
     }
 }
   [(set (attr "type")
-     (cond [(eq_attr "alternative" "5")
+     (cond [(and (eq_attr "alternative" "5")
+                (not (match_operand:QI 1 "aligned_operand" "")))
              (const_string "imovx")
            (ne (symbol_ref "optimize_size") (const_int 0))
              (const_string "imov")
                      (eq (symbol_ref "TARGET_QIMODE_MATH")
                          (const_int 0))))
              (const_string "imov")
-           (eq_attr "alternative" "3")
+           (eq_attr "alternative" "3,5")
              (const_string "imovx")
            (and (ne (symbol_ref "TARGET_MOVX")
                     (const_int 0))
               (const_string "SI")
             (and (eq_attr "type" "imov")
                  (and (eq_attr "alternative" "0,1")
-                      (ne (symbol_ref "TARGET_PARTIAL_REG_DEPENDENCY")
-                          (const_int 0))))
+                      (and (ne (symbol_ref "TARGET_PARTIAL_REG_DEPENDENCY")
+                               (const_int 0))
+                           (and (eq (symbol_ref "optimize_size")
+                                    (const_int 0))
+                                (eq (symbol_ref "TARGET_PARTIAL_REG_STALL")
+                                    (const_int 0))))))
               (const_string "SI")
             ;; Avoid partial register stalls when not using QImode arithmetic
             (and (eq_attr "type" "imov")
 (define_split
   [(set (match_operand:DI 0 "push_operand" "")
         (match_operand:DI 1 "immediate_operand" ""))]
-  "TARGET_64BIT && (flag_peephole2 ? flow2_completed : reload_completed)
+  "TARGET_64BIT && ((optimize > 0 && flag_peephole2)
+                   ? flow2_completed : reload_completed)
    && !symbolic_operand (operands[1], DImode)
    && !x86_64_immediate_operand (operands[1], DImode)"
   [(set (match_dup 0) (match_dup 1))
 (define_split
   [(set (match_operand:DI 0 "memory_operand" "")
         (match_operand:DI 1 "immediate_operand" ""))]
-  "TARGET_64BIT && (flag_peephole2 ? flow2_completed : reload_completed)
+  "TARGET_64BIT && ((optimize > 0 && flag_peephole2)
+                   ? flow2_completed : reload_completed)
    && !symbolic_operand (operands[1], DImode)
    && !x86_64_immediate_operand (operands[1], DImode)"
   [(set (match_dup 2) (match_dup 3))
       gcc_unreachable ();
     }
 }
-  [(set_attr "type" "ssemov,ssemov,ssemov")
+  [(set_attr "type" "sselog1,ssemov,ssemov")
    (set (attr "mode")
-        (cond [(eq (symbol_ref "TARGET_SSE2") (const_int 0))
+       (cond [(ior (eq (symbol_ref "TARGET_SSE2") (const_int 0))
+                   (ne (symbol_ref "optimize_size") (const_int 0)))
                 (const_string "V4SF")
-
-              (eq_attr "alternative" "0,1")
-                (if_then_else
-                  (ne (symbol_ref "optimize_size")
-                      (const_int 0))
-                  (const_string "V4SF")
-                  (const_string "TI"))
-              (eq_attr "alternative" "2")
-                (if_then_else
-                  (ne (symbol_ref "optimize_size")
-                      (const_int 0))
-                  (const_string "V4SF")
-                  (const_string "TI"))]
-              (const_string "TI")))])
+              (and (eq_attr "alternative" "2")
+                   (ne (symbol_ref "TARGET_SSE_TYPELESS_STORES")
+                       (const_int 0)))
+                (const_string "V4SF")]
+             (const_string "TI")))])
 
 (define_insn "*movti_rex64"
   [(set (match_operand:TI 0 "nonimmediate_operand" "=r,o,x,x,xm")
       gcc_unreachable ();
     }
 }
-  [(set_attr "type" "*,*,ssemov,ssemov,ssemov")
+  [(set_attr "type" "*,*,sselog1,ssemov,ssemov")
    (set (attr "mode")
         (cond [(eq_attr "alternative" "2,3")
                 (if_then_else
       gcc_unreachable ();
     }
 }
-  [(set_attr "type" "fmov,fmov,fmov,imov,imov,ssemov,ssemov,ssemov,ssemov,mmxmov,mmxmov,mmxmov")
+  [(set_attr "type" "fmov,fmov,fmov,imov,imov,sselog1,ssemov,ssemov,ssemov,mmxmov,mmxmov,mmxmov")
    (set (attr "mode")
         (cond [(eq_attr "alternative" "3,4,9,10")
                 (const_string "SI")
       gcc_unreachable ();
     }
 }
-  [(set_attr "type" "fmov,fmov,fmov,multi,multi,ssemov,ssemov,ssemov,ssemov")
+  [(set_attr "type" "fmov,fmov,fmov,multi,multi,sselog1,ssemov,ssemov,ssemov")
    (set (attr "mode")
         (cond [(eq_attr "alternative" "0,1,2")
                 (const_string "DF")
       gcc_unreachable();
     }
 }
-  [(set_attr "type" "fmov,fmov,fmov,multi,multi,ssemov,ssemov,ssemov,ssemov")
+  [(set_attr "type" "fmov,fmov,fmov,multi,multi,sselog1,ssemov,ssemov,ssemov")
    (set (attr "mode")
         (cond [(eq_attr "alternative" "0,1,2")
                 (const_string "DF")
       gcc_unreachable ();
     }
 }
-  [(set_attr "type" "*,*,ssemov,ssemov,ssemov")
+  [(set_attr "type" "*,*,sselog1,ssemov,ssemov")
    (set (attr "mode")
         (cond [(eq_attr "alternative" "2,3")
                 (if_then_else
   [(set_attr "type" "imovx,alu1")
    (set_attr "mode" "HI")])
 
-(define_insn "*zero_extendqihi2_movzbw"
+; zero extend to SImode here to avoid partial register stalls
+(define_insn "*zero_extendqihi2_movzbl"
   [(set (match_operand:HI 0 "register_operand" "=r")
      (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
   "(!TARGET_ZERO_EXTEND_WITH_AND || optimize_size) && reload_completed"
-  "movz{bw|x}\t{%1, %0|%0, %1}"
+  "movz{bl|x}\t{%1, %k0|%k0, %k1}"
   [(set_attr "type" "imovx")
-   (set_attr "mode" "HI")])
+   (set_attr "mode" "SI")])
 
 ;; For the movzbw case strip only the clobber
 (define_split
   /* ??? Needed for compress_float_constant since all fp constants
      are LEGITIMATE_CONSTANT_P.  */
   if (GET_CODE (operands[1]) == CONST_DOUBLE)
-    operands[1] = validize_mem (force_const_mem (SFmode, operands[1]));
+    {
+      if ((!TARGET_SSE2 || TARGET_MIX_SSE_I387)
+         && standard_80387_constant_p (operands[1]) > 0)
+       {
+         operands[1] = simplify_const_unary_operation
+           (FLOAT_EXTEND, DFmode, operands[1], SFmode);
+         emit_move_insn_1 (operands[0], operands[1]);
+         DONE;
+       }
+      operands[1] = validize_mem (force_const_mem (SFmode, operands[1]));
+    }
   if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
     operands[1] = force_reg (SFmode, operands[1]);
 })
   /* ??? Needed for compress_float_constant since all fp constants
      are LEGITIMATE_CONSTANT_P.  */
   if (GET_CODE (operands[1]) == CONST_DOUBLE)
-    operands[1] = validize_mem (force_const_mem (SFmode, operands[1]));
+    {
+      if (standard_80387_constant_p (operands[1]) > 0)
+       {
+         operands[1] = simplify_const_unary_operation
+           (FLOAT_EXTEND, XFmode, operands[1], SFmode);
+         emit_move_insn_1 (operands[0], operands[1]);
+         DONE;
+       }
+      operands[1] = validize_mem (force_const_mem (SFmode, operands[1]));
+    }
   if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
     operands[1] = force_reg (SFmode, operands[1]);
 })
   /* ??? Needed for compress_float_constant since all fp constants
      are LEGITIMATE_CONSTANT_P.  */
   if (GET_CODE (operands[1]) == CONST_DOUBLE)
-    operands[1] = validize_mem (force_const_mem (DFmode, operands[1]));
+    {
+      if (standard_80387_constant_p (operands[1]) > 0)
+       {
+         operands[1] = simplify_const_unary_operation
+           (FLOAT_EXTEND, XFmode, operands[1], DFmode);
+         emit_move_insn_1 (operands[0], operands[1]);
+         DONE;
+       }
+      operands[1] = validize_mem (force_const_mem (DFmode, operands[1]));
+    }
   if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
     operands[1] = force_reg (DFmode, operands[1]);
 })
   [(match_scratch:DF 2 "Y")
    (set (match_operand:SSEMODEI24 0 "register_operand" "")
        (fix:SSEMODEI24 (match_operand:DF 1 "memory_operand" "")))]
-  "TARGET_K8 && !optimize_size"
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (fix:SSEMODEI24 (match_dup 2)))]
   "")
   [(match_scratch:SF 2 "x")
    (set (match_operand:SSEMODEI24 0 "register_operand" "")
        (fix:SSEMODEI24 (match_operand:SF 1 "memory_operand" "")))]
-  "TARGET_K8 && !optimize_size"
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (fix:SSEMODEI24 (match_dup 2)))]
   "")
                   (match_operand:SI 2 "const_int_operand" ""))
                 (const_int 0)))]
   "ix86_match_ccmode (insn, CCNOmode)
+   && INTVAL (operands[1]) > 0
+   && INTVAL (operands[2]) >= 0
+   && INTVAL (operands[1]) + INTVAL (operands[2]) <= 32
    && (GET_MODE (operands[0]) == SImode
        || (TARGET_64BIT && GET_MODE (operands[0]) == DImode)
        || GET_MODE (operands[0]) == HImode
                 (const_int 0)))]
   "TARGET_64BIT
    && ix86_match_ccmode (insn, CCNOmode)
-   /* The code below cannot deal with constants outside HOST_WIDE_INT.  */
-   && INTVAL (operands[1]) + INTVAL (operands[2]) < HOST_BITS_PER_WIDE_INT
+   && INTVAL (operands[1]) > 0
+   && INTVAL (operands[2]) >= 0
    /* Ensure that resulting mask is zero or sign extended operand.  */
    && (INTVAL (operands[1]) + INTVAL (operands[2]) <= 32
        || (INTVAL (operands[1]) + INTVAL (operands[2]) == 64
       val = gen_lowpart (QImode, val);
     }
 
-  mask  = ((HOST_WIDE_INT)1 << (pos + len)) - 1;
-  mask &= ~(((HOST_WIDE_INT)1 << pos) - 1);
+  if (len == HOST_BITS_PER_WIDE_INT)
+    mask = -1;
+  else
+    mask = ((HOST_WIDE_INT)1 << len) - 1;
+  mask <<= pos;
 
   operands[2] = gen_rtx_AND (mode, val, gen_int_mode (mask, mode));
 })
 
 (define_expand "absxf2"
   [(set (match_operand:XF 0 "nonimmediate_operand" "")
-       (neg:XF (match_operand:XF 1 "nonimmediate_operand" "")))]
+       (abs:XF (match_operand:XF 1 "nonimmediate_operand" "")))]
   "TARGET_80387"
   "ix86_expand_fp_absneg_operator (ABS, XFmode, operands); DONE;")
 
        (ashift:DI (match_operand:DI 1 "nonmemory_operand" "")
                   (match_operand:QI 2 "nonmemory_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && (flag_peephole2 ? flow2_completed : reload_completed)"
+  "!TARGET_64BIT && ((optimize > 0 && flag_peephole2)
+                    ? flow2_completed : reload_completed)"
   [(const_int 0)]
   "ix86_split_ashl (operands, NULL_RTX, DImode); DONE;")
 
   [(const_int 0)]
 {
   rtx pat, clob;
-  emit_move_insn (operands[1], operands[0]);
+  emit_move_insn (operands[0], operands[1]);
   pat = gen_rtx_SET (VOIDmode, operands[0],
                     gen_rtx_ASHIFT (GET_MODE (operands[0]),
                                     operands[0], operands[2]));
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:QI 2 "nonmemory_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && (flag_peephole2 ? flow2_completed : reload_completed)"
+  "!TARGET_64BIT && ((optimize > 0 && flag_peephole2)
+                    ? flow2_completed : reload_completed)"
   [(const_int 0)]
   "ix86_split_ashr (operands, NULL_RTX, DImode); DONE;")
 
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:QI 2 "nonmemory_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && (flag_peephole2 ? flow2_completed : reload_completed)"
+  "!TARGET_64BIT && ((optimize > 0 && flag_peephole2)
+                    ? flow2_completed : reload_completed)"
   [(const_int 0)]
   "ix86_split_lshr (operands, NULL_RTX, DImode); DONE;")
 
        (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
    (clobber (reg:CC FLAGS_REG))]
   "!TARGET_64BIT"
-  { return output_set_got (operands[0]); }
+  { return output_set_got (operands[0], NULL_RTX); }
+  [(set_attr "type" "multi")
+   (set_attr "length" "12")])
+
+(define_insn "set_got_labelled"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (unspec:SI [(label_ref (match_operand 1 "" ""))]
+        UNSPEC_SET_GOT))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT"
+  { return output_set_got (operands[0], operands[1]); }
   [(set_attr "type" "multi")
    (set_attr "length" "12")])
 
       operands[2] = gen_reg_rtx (Pmode);
       emit_insn (gen_set_got (operands[2]));
     }
+  if (TARGET_GNU2_TLS)
+    {
+       emit_insn (gen_tls_dynamic_gnu2_32
+                 (operands[0], operands[1], operands[2]));
+       DONE;
+    }
   operands[3] = ix86_tls_get_addr ();
 })
 
                         UNSPEC_TLS_GD)])]
   ""
 {
+  if (TARGET_GNU2_TLS)
+    {
+       emit_insn (gen_tls_dynamic_gnu2_64
+                 (operands[0], operands[1]));
+       DONE;
+    }
   operands[2] = ix86_tls_get_addr ();
 })
 
       operands[1] = gen_reg_rtx (Pmode);
       emit_insn (gen_set_got (operands[1]));
     }
+  if (TARGET_GNU2_TLS)
+    {
+       emit_insn (gen_tls_dynamic_gnu2_32
+                 (operands[0], ix86_tls_module_base (), operands[1]));
+       DONE;
+    }
   operands[2] = ix86_tls_get_addr ();
 })
 
              (unspec:DI [(const_int 0)] UNSPEC_TLS_LD_BASE)])]
   ""
 {
+  if (TARGET_GNU2_TLS)
+    {
+       emit_insn (gen_tls_dynamic_gnu2_64
+                 (operands[0], ix86_tls_module_base ()));
+       DONE;
+    }
   operands[1] = ix86_tls_get_addr ();
 })
 
    (set_attr "length" "7")
    (set_attr "memory" "load")
    (set_attr "imm_disp" "false")])
+
+;; GNU2 TLS patterns can be split.
+
+(define_expand "tls_dynamic_gnu2_32"
+  [(set (match_dup 3)
+       (plus:SI (match_operand:SI 2 "register_operand" "")
+                (const:SI
+                 (unspec:SI [(match_operand:SI 1 "tls_symbolic_operand" "")]
+                            UNSPEC_TLSDESC))))
+   (parallel
+    [(set (match_operand:SI 0 "register_operand" "")
+         (unspec:SI [(match_dup 1) (match_dup 3)
+                     (match_dup 2) (reg:SI SP_REG)]
+                     UNSPEC_TLSDESC))
+     (clobber (reg:CC FLAGS_REG))])]
+  "!TARGET_64BIT && TARGET_GNU2_TLS"
+{
+  operands[3] = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+  ix86_tls_descriptor_calls_expanded_in_cfun = true;
+})
+
+(define_insn "*tls_dynamic_lea_32"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (match_operand:SI 1 "register_operand" "b")
+                (const:SI
+                 (unspec:SI [(match_operand:SI 2 "tls_symbolic_operand" "")]
+                             UNSPEC_TLSDESC))))]
+  "!TARGET_64BIT && TARGET_GNU2_TLS"
+  "lea{l}\t{%a2@TLSDESC(%1), %0|%0, %a2@TLSDESC[%1]}"
+  [(set_attr "type" "lea")
+   (set_attr "mode" "SI")
+   (set_attr "length" "6")
+   (set_attr "length_address" "4")])
+
+(define_insn "*tls_dynamic_call_32"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+       (unspec:SI [(match_operand:SI 1 "tls_symbolic_operand" "")
+                   (match_operand:SI 2 "register_operand" "0")
+                   ;; we have to make sure %ebx still points to the GOT
+                   (match_operand:SI 3 "register_operand" "b")
+                   (reg:SI SP_REG)]
+                  UNSPEC_TLSDESC))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT && TARGET_GNU2_TLS"
+  "call\t{*%a1@TLSCALL(%2)|[DWORD PTR [%2+%a1@TLSCALL]]}"
+  [(set_attr "type" "call")
+   (set_attr "length" "2")
+   (set_attr "length_address" "0")])
+
+(define_insn_and_split "*tls_dynamic_gnu2_combine_32"
+  [(set (match_operand:SI 0 "register_operand" "=&a")
+       (plus:SI
+        (plus:SI (match_operand:SI 3 "tp_or_register_operand" "ir")
+                 (unspec:SI [(match_operand:SI 4 "tls_modbase_operand" "")
+                             (match_operand:SI 5 "" "")
+                             (match_operand:SI 2 "register_operand" "b")
+                             (reg:SI SP_REG)]
+                            UNSPEC_TLSDESC))
+        (const:SI (unspec:SI
+                   [(match_operand:SI 1 "tls_symbolic_operand" "")]
+                   UNSPEC_DTPOFF))))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT && TARGET_GNU2_TLS"
+  "#"
+  ""
+  [(parallel
+    [(set (match_dup 0)
+         (plus:SI (match_dup 3)
+                  (match_dup 5)))
+     (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[5] = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+  emit_insn (gen_tls_dynamic_gnu2_32 (operands[5], operands[1], operands[2]));
+})
+
+(define_expand "tls_dynamic_gnu2_64"
+  [(set (match_dup 2)
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                  UNSPEC_TLSDESC))
+   (parallel
+    [(set (match_operand:DI 0 "register_operand" "")
+         (unspec:DI [(match_dup 1) (match_dup 2) (reg:DI SP_REG)]
+                    UNSPEC_TLSDESC))
+     (clobber (reg:CC FLAGS_REG))])]
+  "TARGET_64BIT && TARGET_GNU2_TLS"
+{
+  operands[2] = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+  ix86_tls_descriptor_calls_expanded_in_cfun = true;
+})
+
+(define_insn "*tls_dynamic_lea_64"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                  UNSPEC_TLSDESC))]
+  "TARGET_64BIT && TARGET_GNU2_TLS"
+  "lea{q}\t{%a1@TLSDESC(%%rip), %0|%0, %a1@TLSDESC[%%rip]}"
+  [(set_attr "type" "lea")
+   (set_attr "mode" "DI")
+   (set_attr "length" "7")
+   (set_attr "length_address" "4")])
+
+(define_insn "*tls_dynamic_call_64"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")
+                   (match_operand:DI 2 "register_operand" "0")
+                   (reg:DI SP_REG)]
+                  UNSPEC_TLSDESC))
+   (clobber (reg:CC FLAGS_REG))]
+  "TARGET_64BIT && TARGET_GNU2_TLS"
+  "call\t{*%a1@TLSCALL(%2)|[QWORD PTR [%2+%a1@TLSCALL]]}"
+  [(set_attr "type" "call")
+   (set_attr "length" "2")
+   (set_attr "length_address" "0")])
+
+(define_insn_and_split "*tls_dynamic_gnu2_combine_64"
+  [(set (match_operand:DI 0 "register_operand" "=&a")
+       (plus:DI
+        (plus:DI (match_operand:DI 2 "tp_or_register_operand" "ir")
+                 (unspec:DI [(match_operand:DI 3 "tls_modbase_operand" "")
+                             (match_operand:DI 4 "" "")
+                             (reg:DI SP_REG)]
+                             UNSPEC_TLSDESC))
+        (const:DI (unspec:DI
+                   [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                   UNSPEC_DTPOFF))))
+   (clobber (reg:CC FLAGS_REG))]
+  "TARGET_64BIT && TARGET_GNU2_TLS"
+  "#"
+  ""
+  [(parallel
+    [(set (match_dup 0)
+         (plus:DI (match_dup 2)
+                  (match_dup 4)))
+     (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[4] = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+  emit_insn (gen_tls_dynamic_gnu2_64 (operands[4], operands[1]));
+})
+
+;;
 \f
 ;; These patterns match the binary 387 instructions for addM3, subM3,
 ;; mulM3 and divM3.  There are three patterns for each of DFmode and
   [(label_ref (match_operand 0 "" ""))]
   "!TARGET_64BIT && flag_pic"
 {
-  emit_insn (gen_set_got (pic_offset_table_rtx));
+  if (TARGET_MACHO)
+    {
+      rtx xops[3];
+      rtx picreg = gen_rtx_REG (Pmode, PIC_OFFSET_TABLE_REGNUM);
+      rtx label_rtx = gen_label_rtx ();
+      emit_insn (gen_set_got_labelled (pic_offset_table_rtx, label_rtx));
+      xops[0] = xops[1] = picreg;
+      xops[2] = gen_rtx_CONST (SImode,
+                 gen_rtx_MINUS (SImode,
+                   gen_rtx_LABEL_REF (SImode, label_rtx),
+                   gen_rtx_SYMBOL_REF (SImode, GOT_SYMBOL_NAME)));
+      ix86_expand_binary_operator (MINUS, SImode, xops);
+    }
+  else
+    emit_insn (gen_set_got (pic_offset_table_rtx));
   DONE;
 })
 \f
   [(set (match_operand:SI 0 "push_operand" "")
        (match_operand:SI 1 "memory_operand" ""))
    (match_scratch:SI 2 "r")]
-  "! optimize_size && ! TARGET_PUSH_MEMORY"
+  "!optimize_size && !TARGET_PUSH_MEMORY
+   && !RTX_FRAME_RELATED_P (peep2_next_insn (0))"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (match_dup 2))]
   "")
   [(set (match_operand:DI 0 "push_operand" "")
        (match_operand:DI 1 "memory_operand" ""))
    (match_scratch:DI 2 "r")]
-  "! optimize_size && ! TARGET_PUSH_MEMORY"
+  "!optimize_size && !TARGET_PUSH_MEMORY
+   && !RTX_FRAME_RELATED_P (peep2_next_insn (0))"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (match_dup 2))]
   "")
   [(set (match_operand:SF 0 "push_operand" "")
        (match_operand:SF 1 "memory_operand" ""))
    (match_scratch:SF 2 "r")]
-  "! optimize_size && ! TARGET_PUSH_MEMORY"
+  "!optimize_size && !TARGET_PUSH_MEMORY
+   && !RTX_FRAME_RELATED_P (peep2_next_insn (0))"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (match_dup 2))]
   "")
   [(set (match_operand:HI 0 "push_operand" "")
        (match_operand:HI 1 "memory_operand" ""))
    (match_scratch:HI 2 "r")]
-  "! optimize_size && ! TARGET_PUSH_MEMORY"
+  "!optimize_size && !TARGET_PUSH_MEMORY
+   && !RTX_FRAME_RELATED_P (peep2_next_insn (0))"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (match_dup 2))]
   "")
   [(set (match_operand:QI 0 "push_operand" "")
        (match_operand:QI 1 "memory_operand" ""))
    (match_scratch:QI 2 "q")]
-  "! optimize_size && ! TARGET_PUSH_MEMORY"
+  "!optimize_size && !TARGET_PUSH_MEMORY
+   && !RTX_FRAME_RELATED_P (peep2_next_insn (0))"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0) (match_dup 2))]
   "")
                   (mult:DI (match_operand:DI 1 "memory_operand" "")
                            (match_operand:DI 2 "immediate_operand" "")))
              (clobber (reg:CC FLAGS_REG))])]
-  "TARGET_K8 && !optimize_size
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size
    && (GET_CODE (operands[2]) != CONST_INT
        || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))"
   [(set (match_dup 3) (match_dup 1))
                   (mult:SI (match_operand:SI 1 "memory_operand" "")
                            (match_operand:SI 2 "immediate_operand" "")))
              (clobber (reg:CC FLAGS_REG))])]
-  "TARGET_K8 && !optimize_size
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size
    && (GET_CODE (operands[2]) != CONST_INT
        || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))"
   [(set (match_dup 3) (match_dup 1))
                     (mult:SI (match_operand:SI 1 "memory_operand" "")
                              (match_operand:SI 2 "immediate_operand" ""))))
              (clobber (reg:CC FLAGS_REG))])]
-  "TARGET_K8 && !optimize_size
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size
    && (GET_CODE (operands[2]) != CONST_INT
        || !CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K'))"
   [(set (match_dup 3) (match_dup 1))
                            (match_operand:DI 2 "const_int_operand" "")))
              (clobber (reg:CC FLAGS_REG))])
    (match_scratch:DI 3 "r")]
-  "TARGET_K8 && !optimize_size
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size
    && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')"
   [(set (match_dup 3) (match_dup 2))
    (parallel [(set (match_dup 0) (mult:DI (match_dup 0) (match_dup 3)))
                            (match_operand:SI 2 "const_int_operand" "")))
              (clobber (reg:CC FLAGS_REG))])
    (match_scratch:SI 3 "r")]
-  "TARGET_K8 && !optimize_size
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size
    && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')"
   [(set (match_dup 3) (match_dup 2))
    (parallel [(set (match_dup 0) (mult:SI (match_dup 0) (match_dup 3)))
                            (match_operand:HI 2 "immediate_operand" "")))
              (clobber (reg:CC FLAGS_REG))])
    (match_scratch:HI 3 "r")]
-  "TARGET_K8 && !optimize_size"
+  "(TARGET_K8 || TARGET_GENERIC64) && !optimize_size"
   [(set (match_dup 3) (match_dup 2))
    (parallel [(set (match_dup 0) (mult:HI (match_dup 0) (match_dup 3)))
              (clobber (reg:CC FLAGS_REG))])]
   if (!rtx_equal_p (operands[0], operands[1]))
     emit_move_insn (operands[0], operands[1]);
 })
+
+;; After splitting up read-modify operations, array accesses with memory
+;; operands might end up in form:
+;;  sall    $2, %eax
+;;  movl    4(%esp), %edx
+;;  addl    %edx, %eax
+;; instead of pre-splitting:
+;;  sall    $2, %eax
+;;  addl    4(%esp), %eax
+;; Turn it into:
+;;  movl    4(%esp), %edx
+;;  leal    (%edx,%eax,4), %eax
+
+(define_peephole2
+  [(parallel [(set (match_operand 0 "register_operand" "")
+                  (ashift (match_operand 1 "register_operand" "")
+                          (match_operand 2 "const_int_operand" "")))
+              (clobber (reg:CC FLAGS_REG))])
+   (set (match_operand 3 "register_operand")
+        (match_operand 4 "x86_64_general_operand" ""))
+   (parallel [(set (match_operand 5 "register_operand" "")
+                  (plus (match_operand 6 "register_operand" "")
+                        (match_operand 7 "register_operand" "")))
+                  (clobber (reg:CC FLAGS_REG))])]
+  "INTVAL (operands[2]) >= 0 && INTVAL (operands[2]) <= 3
+   /* Validate MODE for lea.  */
+   && ((!TARGET_PARTIAL_REG_STALL
+       && (GET_MODE (operands[0]) == QImode
+           || GET_MODE (operands[0]) == HImode))
+       || GET_MODE (operands[0]) == SImode 
+       || (TARGET_64BIT && GET_MODE (operands[0]) == DImode))
+   /* We reorder load and the shift.  */
+   && !rtx_equal_p (operands[1], operands[3])
+   && !reg_overlap_mentioned_p (operands[0], operands[4])
+   /* Last PLUS must consist of operand 0 and 3.  */
+   && !rtx_equal_p (operands[0], operands[3])
+   && (rtx_equal_p (operands[3], operands[6])
+       || rtx_equal_p (operands[3], operands[7]))
+   && (rtx_equal_p (operands[0], operands[6])
+       || rtx_equal_p (operands[0], operands[7]))
+   /* The intermediate operand 0 must die or be same as output.  */
+   && (rtx_equal_p (operands[0], operands[5])
+       || peep2_reg_dead_p (3, operands[0]))"
+  [(set (match_dup 3) (match_dup 4))
+   (set (match_dup 0) (match_dup 1))]
+{
+  enum machine_mode mode = GET_MODE (operands[5]) == DImode ? DImode : SImode;
+  int scale = 1 << INTVAL (operands[2]);
+  rtx index = gen_lowpart (Pmode, operands[1]);
+  rtx base = gen_lowpart (Pmode, operands[3]);
+  rtx dest = gen_lowpart (mode, operands[5]);
+
+  operands[1] = gen_rtx_PLUS (Pmode, base,
+                             gen_rtx_MULT (Pmode, index, GEN_INT (scale)));
+  if (mode != Pmode)
+    operands[1] = gen_rtx_SUBREG (mode, operands[1], 0);
+  operands[0] = dest;
+})
 \f
 ;; Call-value patterns last so that the wildcard operand does not
 ;; disrupt insn-recog's switch tables.