]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/config/msp430/msp430.md
Update copyright years.
[thirdparty/gcc.git] / gcc / config / msp430 / msp430.md
index b6602fbca66acab35e95795c56b9d5992a36af7b..0357694e1f22bd0a20eb098eb178500e925c267e 100644 (file)
@@ -1,5 +1,5 @@
 ;;  Machine Description for TI MSP43* processors
-;;  Copyright (C) 2013-2020 Free Software Foundation, Inc.
+;;  Copyright (C) 2013-2023 Free Software Foundation, Inc.
 ;;  Contributed by Red Hat.
 
 ;; This file is part of GCC.
    UNS_DELAY_END
   ])
 
-;; This is an approximation.
-(define_attr "length" "" (const_int 4))
+;; Instruction length is calculated by examining the type and number of
+;; operands.
+;; Whether the insn uses the 430X extension word, or is a 430X address
+;; instruction also has an effect.
+;; "Cheap" source operands do not contribute to the overall length of the insn
+;; and are register (Rn), indirect post-increment (@Rn+) and indirect register
+;; (@Rn).
+;; The lengths of instructions in bytes are:
+;; Single-op 430: Cheap op == 2
+;; (also CALLA)   Other op == 4
+;; Double-op 430: Source is not cheap == 2
+;;  (also MOVA,   Dest is register == 2
+;;   CMPA, ADDA,  Dest is not a register == 4
+;;   SUBA)       (sum the source and dest cost)
+;; Single-op 430X: For insn names ending in 'X' add 2 to single-op 430 cost.
+;; Double-op 430X: Insn name ends in 'M' == 2
+;;                Others have the same cost as double-op 430 but add 2.
+;;
+;; The insn type describes whether it is a single or double operand MSP430
+;; instruction (some single-operand GCC instructions are actually
+;; double-operand on the target).
+;; "triple" and "cmp" types use the costs of a double operand type but
+;; instead assume that the src operand is in op2, and also cmp types assume the
+;; dst operand is in op1.
+;; This attribute also describes which operands are safe to examine
+;; when calculating the length or extension.  GCC will segfault trying to
+;; examine a non-existant operand of an insn.
+(define_attr "type" "none,single,double,triple,cmp" (const_string "none"))
+
+;; The M extension is for instructions like RRAM - they always
+;; only, and the operand must be a register.
+(define_attr "extension" "none,x,a,m"
+ (cond [(eq_attr "type" "none")
+       (const_string "none")
+       (match_operand 0 "msp430_high_memory_operand" "")
+       (const_string "x")
+       (and (eq_attr "type" "double")
+            (match_operand 1 "msp430_high_memory_operand" ""))
+       (const_string "x")
+       (and (ior (eq_attr "type" "triple") (eq_attr "type" "cmp"))
+            (ior (match_operand 1 "msp430_high_memory_operand" "")
+                 (match_operand 2 "msp430_high_memory_operand" "")))
+       (const_string "x")]
+       (const_string "none")))
+
+;; Multiply the default length by this constant value.
+(define_attr "length_multiplier" "" (const_int 1))
+
+;; Add an additional amount to the total length of the insn.
+(define_attr "extra_length" "" (const_int 0))
+
+;; FIXME for some reason if we move the addition of 2 for extension == x to
+;; ADJUST_INSN_LENGTH, codesize gets much worse.
+(define_attr "length" ""
+ (cond [(eq_attr "extension" "m")
+       (const_int 2)
+       (eq_attr "type" "single")
+       (plus (if_then_else (match_operand 0 "msp430_cheap_operand" "")
+                           (const_int 2)
+                           (const_int 4))
+             (if_then_else (eq_attr "extension" "x")
+                           (const_int 2)
+                           (const_int 0)))
+       (eq_attr "type" "double")
+       (plus (plus (if_then_else (match_operand 0 "register_operand" "")
+                                  (const_int 2)
+                                  (const_int 4))
+                   (if_then_else (match_operand 1 "msp430_cheap_operand" "")
+                                 (const_int 0)
+                                 (const_int 2)))
+             (if_then_else (eq_attr "extension" "x")
+                           (const_int 2)
+                           (const_int 0)))
+       (eq_attr "type" "triple")
+       (plus (plus (if_then_else (match_operand 0 "register_operand" "")
+                                  (const_int 2)
+                                  (const_int 4))
+                   (if_then_else (match_operand 2 "msp430_cheap_operand" "")
+                                 (const_int 0)
+                                 (const_int 2)))
+             (if_then_else (eq_attr "extension" "x")
+                           (const_int 2)
+                           (const_int 0)))
+       (eq_attr "type" "cmp")
+       (plus (plus (if_then_else (match_operand 1 "register_operand" "")
+                                  (const_int 2)
+                                  (const_int 4))
+                   (if_then_else (match_operand 2 "msp430_cheap_operand" "")
+                                 (const_int 0)
+                                 (const_int 2)))
+             (if_then_else (eq_attr "extension" "x")
+                           (const_int 2)
+                           (const_int 0)))]
+  (const_int 2)))
 
 (include "predicates.md")
 (include "constraints.md")
 
 (define_mode_iterator QHI [QI HI PSI])
+(define_mode_iterator HPSI [HI PSI])
+(define_mode_iterator HDI [HI PSI SI DI])
+
+;; Mapping of all shift operators
+(define_code_iterator any_shift [ashift ashiftrt lshiftrt])
+
+;; Base name for define_insn
+(define_code_attr shift_insn
+  [(ashift "ashl") (lshiftrt "lshr") (ashiftrt "ashr")])
 
 ;; There are two basic "family" tests we do here:
 ;;
        (match_operand:HI 0 "register_operand" "r"))]
   ""
   "PUSH\t%0"
-  )
+  [(set_attr "type" "single")]
+)
 
 (define_insn "pusha"
   [(set (mem:PSI (pre_dec:PSI (reg:PSI SP_REGNO)))
        (match_operand:PSI 0 "register_operand" "r"))]
   "TARGET_LARGE"
   "PUSHX.A\t%0"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extension" "x")]
+)
 
 (define_insn "pushm"
   [(unspec_volatile [(match_operand 0 "register_operand" "r")
                     (match_operand 1 "immediate_operand" "n")] UNS_PUSHM)]
   ""
   "PUSHM%b0\t%1, %0"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extension" "m")]
+)
 
 (define_insn "pop"
   [(set (match_operand:HI 0 "register_operand" "=r")
        (mem:HI (post_inc:HI (reg:HI SP_REGNO))))]
   ""
   "POP\t%0"
-  )
+  [(set_attr "type" "single")]
+)
 
 (define_insn "popa"
   [(set (match_operand:PSI 0 "register_operand" "=r")
        (mem:PSI (post_inc:PSI (reg:PSI SP_REGNO))))]
   "TARGET_LARGE"
   "POPX.A\t%0"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extension" "x")]
+)
 
 ;; This is nasty.  Operand0 is bogus.  It is only there so that we can get a
 ;; mode for the %b0 to work.  We should use operand1 for this, but that does
                     (match_operand 2 "immediate_operand" "i")] UNS_POPM)]
   ""
   "POPM%b0\t%2, r%J1"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extension" "m")]
+)
 
 ;; The next two patterns are here to support a "feature" of how GCC implements
 ;; varargs.  When a function uses varargs and the *second* to last named
       return \"SUBA\t#2, r1 { MOVX.A\t2(r1), 0(r1)\";
     return \"SUB\t#2, r1 { MOV.W\t2(r1), 0(r1)\";
   "
+  [(set (attr "length")
+       (if_then_else (match_test "TARGET_LARGE")
+                     (const_int 8)
+                     (const_int 6)))]
 )
 
 (define_insn "swap_and_shrink"
   "* return TARGET_LARGE
           ? \"MOVX.A\t0(r1), 2(r1) { ADDA\t#2, SP\"
           : \"MOV.W\t0(r1), 2(r1) { ADD\t#2, SP\";
-  ")
+  "
+  [(set (attr "length")
+       (if_then_else (match_test "TARGET_LARGE")
+                     (const_int 10)
+                     (const_int 8)))]
+)
 
 ; I set LOAD_EXTEND_OP and WORD_REGISTER_OPERATIONS, but gcc puts in a
 ; zero_extend anyway.  Catch it here.
   "@
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "movqi_topbyte"
        (subreg:QI (match_operand:PSI 1 "msp430_general_operand" "r") 2))]
   "msp430x"
   "PUSHM.A\t#1,%1 { POPM.W\t#1,%0 { POPM.W\t#1,%0"
+  [(set_attr "length" "6")
+   (set_attr "type" "double")]
 )
 
 (define_insn "movqi"
   "@
   MOV.B\t%1, %0
   MOVX.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "movhi"
   MOV.B\t%1, %0
   MOV.W\t%1, %0
   MOVX.W\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_expand "movsi"
        (match_operand:SI 1 "general_operand"))]
   ""
   ""
-  )
+)
 
 (define_insn_and_split "movsi_s"
   [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
    (set (match_operand:HI 3 "msp430_general_dst_nonv_operand")
        (match_operand:HI 5 "general_operand"))]
   "msp430_split_movsi (operands);"
-  )
+  [(set_attr "type" "double")]
+)
 
 (define_insn_and_split "movsi_x"
   [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
    (set (match_operand:HI 3 "msp430_general_dst_nonv_operand")
        (match_operand:HI 5 "general_operand"))]
   "msp430_split_movsi (operands);"
+  [(set_attr "type" "double")]
 )
 
 ;; FIXME: Some MOVX.A cases can be done with MOVA, this is only a few of them.
   MOV.W\t%1, %0
   MOVA\t%1, %0
   MOVA\t%1, %0
-  MOVX.A\t%1, %0")
+  MOVX.A\t%1, %0"
+  [(set_attr "extension" "none,none,a,a,x")
+   (set_attr "type" "double")]
+)
 
 ; This pattern is identical to the truncsipsi2 pattern except
 ; that it uses a SUBREG instead of a TRUNC.  It is needed in
        (subreg:PSI (match_operand:SI 1 "register_operand" "r") 0))]
   "msp430x"
   "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A #1, %0 ; Move reg-pair %L1:%H1 into pointer %0"
+  [(set_attr "length" "6")
+   (set_attr "type" "double")]
 )
 
 ;; Produced when converting a pointer to an integer via a union, eg gcc.dg/pr47201.c.
        (subreg:HI (match_operand:PSI 1 "msp430_symbol_operand" "i") 0))]
   "msp430x"
   "MOVA\t%1, %0"
+  [(set_attr "extension" "a")
+   (set_attr "type" "double")]
 )
 
 ;;------------------------------------------------------------
   "@
   ADDA\t%2, %0
   ADDX.A\t%2, %0"
+  [(set_attr "extension" "a,x")
+   (set_attr "type" "triple")]
 )
 
 (define_insn "addqi3"
   "@
    ADD.B\t%2, %0
    ADDX.B\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 (define_insn "addhi3"
   "@
    ADD.W\t%2, %0
    ADDX.W\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 ; This pattern is needed in order to avoid reload problems.
                 (match_operand       2 "general_operand" "rmi")))]
   ""
   "ADD%X2.W\t%L2, %L0 { ADDC%X2.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0"
+  [(set (attr "length")
+       (if_then_else (match_operand 2 "register_operand" "")
+                     (const_int 10)
+                     (if_then_else (match_operand 2 "msp430_high_memory_operand" "")
+                                   (const_int 18)
+                                   (const_int 14))))
+   (set_attr "type" "triple")]
 )
 
 (define_insn "addsi3"
   "@
    ADD\t%L2, %L0 { ADDC\t%H2, %H0
    ADDX\t%L2, %L0 { ADDCX\t%H2, %H0"
+  [(set_attr "length_multiplier" "2")
+   (set_attr "type" "triple")]
 )
 
 ; Version of addhi that exposes the carry operations, for SImode adds.
   "@
    ADD\t%2, %1 ; cy
    ADDX\t%2, %1 ; cy"
-  )
+  [(set_attr "type" "triple")]
+)
 
 (define_insn "addhi3_cy_i"
   [(set (match_operand:HI         0 "msp430_general_dst_nonv_operand" "=r,rm")
   "@
    ADD\t%2, %1 ; cy
    ADD%X0\t%2, %1 ; cy"
-  )
+  [(set_attr "type" "triple")]
+)
 
 ; Version of addhi that adds the carry, for SImode adds.
 (define_insn "addchi4_cy"
   "@
    ADDC\t%2, %1
    ADDCX\t%2, %1"
-  )
+  [(set_attr "type" "triple")]
+)
 
 ; Split an SImode add into two HImode adds, keeping track of the carry
 ; so that gcc knows when it can and can't optimize away the two
   if (msp430_split_addsi (operands))
     FAIL;
   "
-  )
+)
 
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
   SUBX.A\t%2, %0
   MOVX.A\t%1, %0 { SUBX.A\t%2, %0
   MOVX.A\t%1, %0 { SUBA\t%2, %0"
+  [(set_attr "type" "triple")
+   (set_attr "extension" "a,x,x,x")
+   (set_attr "length_multiplier" "1,1,2,2")]
 )
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
   SUBX.B\t%2, %0
   MOV%X2.B\t%1, %0 { SUB%X2.B\t%2, %0
   MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0"
+  [(set_attr "length_multiplier" "1,1,2,2")
+   (set_attr "type" "triple")]
 )
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
   SUBX.W\t%2, %0
   MOV%X2.W\t%1, %0 { SUB%X2.W\t%2, %0
   MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0"
+  [(set_attr "length_multiplier" "1,1,2,2")
+   (set_attr "type" "triple")]
 )
 
 (define_insn "subsi3"
   "@
   SUB\t%L2, %L0 { SUBC\t%H2, %H0
   SUBX\t%L2, %L0 { SUBCX\t%H2, %H0"
+  [(set_attr "length_multiplier" "2")
+   (set_attr "type" "triple")]
 )
 
 (define_insn "*bic<mode>_cg"
   "@
    BIC%x0%b0\t#%I2, %0
    BIC%X0%b0\t#%I2, %0"
+  [(set_attr "length" "2")     ; Smaller length achieved by using constant generator
+   (set_attr "type" "double")]
 )
 
 (define_insn "bic<mode>3"
   "@
    BIC%x0%b0\t%1, %0
    BICX%b0\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "and<mode>3"
    AND%x0.B\t%2, %0
    AND%x0%b0\t%2, %0
    ANDX%b0\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 (define_insn "ior<mode>3"
   "@
    BIS%x0%b0\t%2, %0
    BISX%b0\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 (define_insn "xor<mode>3"
   "@
    XOR%x0%b0\t%2, %0
    XORX%b0\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 ;; Macro : XOR #~0, %0
   "@
    INV%x0%b0\t%0
    INV%X0%b0\t%0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "extendqihi2"
   "@
    SXT%X0\t%0
    SXT%X0\t%0"
+  [(set_attr "type" "single")]
+)
+
+(define_insn "extendqipsi2"
+  [(set (match_operand:PSI                0 "msp430_general_dst_operand" "=r,m")
+       (sign_extend:PSI (match_operand:QI 1 "msp430_general_operand" "0,0")))]
+  ""
+  "@
+  SXT\t%0
+  SXTX.A\t%0"
+  [(set_attr "type" "single")
+   (set_attr "extension" "none,x")]
 )
 
 ;; ------------------------
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0
    AND%X0\t#0xff, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "zero_extendqipsi2"
   "@
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "zero_extendqisi2"
   "@
   CLR\t%H0
   MOV%X1.B\t%1,%L0 { CLR\t%H0"
+  [(set_attr "extra_length" "2")
+   (set_attr "length_multiplier" "1,2")
+   (set_attr "type" "double")]
 )
 
 (define_insn "zero_extendhipsi2"
   MOV.W\t%1, %0
   MOV%X1\t%1, %0
   MOVX.A\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "zero_extendhisi2"
   "@
   MOV%X0.W\t#0,%H0
   MOV.W\t%1,%L0 { MOV.W\t#0,%H0"
+  [(set_attr "length_multiplier" "1,2")
+   (set_attr "type" "double")]
 )
 
 (define_insn "zero_extendhisipsi2"
   "@
    AND.W\t#-1,%0
    MOV.W\t%1,%0"
+  [(set_attr "length" "4,2")
+   (set_attr "type" "double")]
 )
 
 ; Nasty - we are sign-extending a 20-bit PSI value in one register into
     else \
       return \"PUSHM.A\t#1, %1 { POPX.W\t%L0 { POPX.W\t%H0 ; move pointer in %1 into reg-pair %L0:%H0\";
   MOVX.A %1, %0"
+  [(set (attr "length")
+    (cond [(match_test "REGNO (operands[1]) == SP_REGNO")
+          (const_int 18)
+          (eq_attr "alternative" "1")
+          (const_int 6)]
+          (const_int 10)))
+   (set_attr "type" "double")]
 )
 
 ;; Below are unnamed insn patterns to catch pointer manipulation insns
        (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0)))]
   "msp430x"
   "MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn ""
   "@
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
+;; The next three insns emit identical assembly code.
+;; They take a QImode and shift it in SImode.  Only shift counts <= 8
+;; are handled since that is the simple case where the high 16-bits (i.e. the
+;; high register) are always 0.
 (define_insn ""
-  [(set (match_operand:SI 0 "register_operand" "=r")
-       (ashift:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "rm"))
-                  (match_operand:HI 2 "immediate_operand" "M")))]
+  [(set (match_operand:SI                           0 "register_operand" "=r,r,r")
+       (ashift:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "0,rm,rm"))
+                  (match_operand:HI                 2 "const_1_to_8_operand" "M,M,i")))]
   "msp430x"
-  "MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
+  "@
+  RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
+  [(set_attr "length" "4,*,*")
+   (set_attr "extra_length" "0,4,6")
+   (set_attr "type" "double")]
 )
 
-;; We are taking a char and shifting it and putting the result in 2 registers.
-;; the high register will always be for 0 shift counts < 8.
 (define_insn ""
-  [(set (match_operand:SI 0 "register_operand" "=r")
-       (ashift:SI (zero_extend:SI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))
-                  (match_operand:HI 2 "immediate_operand" "M")))]
+  [(set (match_operand:SI                                      0 "register_operand" "=r,r,r")
+       (ashift:SI (zero_extend:SI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0))
+                  (match_operand:HI                            2 "const_1_to_8_operand" "M,M,i")))]
   "msp430x"
-  "MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
+  "@
+  RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
+  [(set_attr "length" "4,*,*")
+   (set_attr "extra_length" "0,4,6")
+   (set_attr "type" "double")]
 )
 
 ;; Same as above but with a NOP sign_extend round the subreg
 (define_insn ""
-  [(set (match_operand:SI 0 "register_operand" "=r")
-       (ashift:SI (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0)))
-                  (match_operand:HI 2 "immediate_operand" "M")))]
+  [(set (match_operand:SI                                                       0 "register_operand" "=r,r,r")
+       (ashift:SI (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0)))
+                  (match_operand:HI                                             2 "const_1_to_8_operand" "M,M,i")))]
   "msp430x"
-  "MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
+  "@
+  RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
+  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
+  [(set_attr "length" "4,*,*")
+   (set_attr "extra_length" "0,4,6")
+   (set_attr "type" "double")]
 )
 
 (define_insn ""
        (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))))]
   "msp430x"
   "MOV%X1.B %1, %L0 { CLR %H0"
+  [(set_attr "extra_length" "4")
+   (set_attr "type" "double")]
 )
 
 (define_insn ""
-  [(set (match_operand:PSI 0 "register_operand" "=r")
-       (ashift:PSI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))
-                   (match_operand:HI 2 "immediate_operand" "M")))]
+  [(set (match_operand:PSI                                       0 "register_operand" "=r,r,r")
+       (ashift:PSI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0))
+                   (match_operand:HI                             2 "const_1_to_19_operand" "M,M,i")))]
   "msp430x"
-  "MOV%X1.B %1, %0 { RLAM.W %2, %0"
+  "@
+  RLAM.W %2, %0
+  MOV%X1.B %1, %0 { RLAM.W %2, %0
+  MOV%X1.B %1, %0 { RPT %2 { RLAX.A %0"
+  [(set_attr "length" "2,*,*")
+   (set_attr "extra_length" "0,2,4")
+   (set_attr "type" "double")]
 )
 ;; END msp430 pointer manipulation combine insn patterns
 
        (truncate:HI (match_operand:PSI 1 "register_operand"      "r")))]
   ""
   "MOVX\t%1, %0"
+  [(set_attr "extension" "m")
+   (set_attr "type" "double")]
 )
 
 (define_insn "extendhisi2"
   [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=r")
        (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))]
   ""
-  { return msp430x_extendhisi (operands); }
+  { msp430x_extendhisi (operands, 0); return ""; }
+  [(set (attr "length")
+       (symbol_ref "msp430x_extendhisi (operands, 1)"))
+   (set_attr "type" "double")]
 )
 
 (define_insn "extendhipsi2"
        (subreg:PSI (sign_extend:SI (match_operand:HI 1 "general_operand" "0")) 0))]
   "msp430x"
   "RLAM.A #4, %0 { RRAM.A #4, %0"
+  [(set_attr "length_multiplier" "2")
+   (set_attr "extension" "m")
+   (set_attr "type" "double")]
 )
 
 ;; Look for cases where integer/pointer conversions are suboptimal due
                   (const_int 1)))]
   "msp430x"
   "RLAM.A #4, %0 { RRAM.A #3, %0"
+  [(set_attr "length_multiplier" "2")
+   (set_attr "extension" "m")
+   (set_attr "type" "double")]
 )
 
 (define_insn "extend_and_shift2_hipsi2"
                   (const_int 2)))]
   "msp430x"
   "RLAM.A #4, %0 { RRAM.A #2, %0"
+  [(set_attr "length_multiplier" "2")
+   (set_attr "extension" "m")
+   (set_attr "type" "double")]
 )
 
 ;; We also need to be able to sign-extend pointer types (eg ptrdiff_t).
     else
       return \"MOV.W\t%1, %L0 { MOVX.A\t%1, %H0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\";
   "
+  [(set_attr "length" "10")
+   (set_attr "type" "double")]
 )
 
 ; See the movsipsi2 pattern above for another way that GCC performs this
        (truncate:PSI (match_operand:SI 1 "register_operand" "r")))]
   ""
   "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A\t#1, %L0"
+  [(set_attr "length" "6")
+   (set_attr "type" "single")]
 )
 
 ;;------------------------------------------------------------
 ;; Note - we ignore shift counts of less than one or more than 15.
 ;; This is permitted by the ISO C99 standard as such shifts result
 ;; in "undefined" behavior.  [6.5.7 (3)]
+;;
+;; We avoid emitting insns in msp430_expand_shift, since we would have to handle
+;; many extra cases such as op0 != op1, or, op0 or op1 in memory.  Instead we
+;; let reload coerce op0 and op1 into the same register.
 
-;; signed A << C
-
-(define_expand "ashlhi3"
-  [(set (match_operand:HI           0 "msp430_general_dst_nonv_operand")
-       (ashift:HI (match_operand:HI 1 "general_operand")
-                  (match_operand:HI 2 "general_operand")))]
+(define_expand "<shift_insn><mode>3"
+  [(set (match_operand:HDI               0 "msp430_general_dst_nonv_operand")
+       (any_shift:HDI (match_operand:HDI 1 "general_operand")
+                      (match_operand:HDI 2 "general_operand")))]
   ""
   {
-    if ((GET_CODE (operands[1]) == SUBREG
-        && REG_P (XEXP (operands[1], 0)))
-       || MEM_P (operands[1]))
-      operands[1] = force_reg (HImode, operands[1]);
-    if (msp430x
-        && REG_P (operands[0])
-        && REG_P (operands[1])
-        && CONST_INT_P (operands[2]))
-      emit_insn (gen_430x_shift_left (operands[0], operands[1], operands[2]));
-    else if (CONST_INT_P (operands[2])
-            && INTVAL (operands[2]) == 1)
-      emit_insn (gen_slli_1 (operands[0], operands[1]));
-    else                
-      /* The const variants of mspabi shifts have larger code size than the
-        generic version, so use the generic version if optimizing for
-        size.  */
-      msp430_expand_helper (operands, \"__mspabi_slli\", !optimize_size);
-    DONE;
+    if (msp430_expand_shift (<CODE>, <MODE>mode, operands))
+      DONE;
+    /* Otherwise, fallthrough.  */
   }
 )
 
-(define_insn "slli_1"
-  [(set (match_operand:HI           0 "msp430_general_dst_nonv_operand" "=rm")
-       (ashift:HI (match_operand:HI 1 "general_operand"       "0")
-                  (const_int 1)))]
-  ""
-  "RLA%X0.W\t%0" ;; Note - this is a macro for ADD
-)
-
-(define_insn "430x_shift_left"
-  [(set (match_operand:HI            0 "register_operand" "=r")
-       (ashift:HI (match_operand:HI 1 "register_operand"  "0")
-                  (match_operand    2 "immediate_operand" "n")))]
-  "msp430x"
-  "*
-  if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 5)
-    return \"RLAM.W\t%2, %0\";
-  else if (INTVAL (operands[2]) >= 5 && INTVAL (operands[2]) < 16)
-    return \"RPT\t%2 { RLAX.W\t%0\";
-  return \"# nop left shift\";
-  "
-)
-
-(define_insn "slll_1"
-  [(set (match_operand:SI           0 "msp430_general_dst_nonv_operand" "=rm")
-       (ashift:SI (match_operand:SI 1 "general_operand"       "0")
-                  (const_int 1)))]
-  ""
-  "RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
-)
-
-(define_insn "slll_2"
-  [(set (match_operand:SI           0 "msp430_general_dst_nonv_operand" "=rm")
-       (ashift:SI (match_operand:SI 1 "general_operand"       "0")
-                  (const_int 2)))]
-  ""
-  "RLA%X0.W\t%L0 { RLC%X0.W\t%H0 { RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
-)
-
-(define_expand "ashlsi3"
-  [(set (match_operand:SI           0 "msp430_general_dst_nonv_operand")
-       (ashift:SI (match_operand:SI 1 "general_operand")
-                  (match_operand:SI 2 "general_operand")))]
-  ""
-  "msp430_expand_helper (operands, \"__mspabi_slll\", !optimize_size);
-   DONE;"
-)
-
-(define_expand "ashldi3"
-  [(set (match_operand:DI           0 "msp430_general_dst_nonv_operand")
-       (ashift:DI (match_operand:DI 1 "general_operand")
-                  (match_operand:DI 2 "general_operand")))]
-  ""
-  {
-    /* No const_variant for 64-bit shifts.  */
-    msp430_expand_helper (operands, \"__mspabi_sllll\", false);
-    DONE;
-  }
+;; All 430 HImode constant shifts
+(define_insn "<shift_insn>hi3_430"
+  [(set (match_operand:HI              0 "msp430_general_dst_nonv_operand" "=rm")
+       (any_shift:HI (match_operand:HI 1 "general_operand"       "0")
+                     (match_operand:HI 2 "const_int_operand"     "n")))]
+  "!msp430x"
+  "* msp430_output_asm_shift_insns (<CODE>, HImode, operands, false); return \"\";"
+  [(set (attr "length")
+       (symbol_ref "msp430_output_asm_shift_insns (<CODE>, HImode, operands, true)"))
+   (set_attr "type" "single")]
 )
 
-;;----------
-
-;; signed A >> C
-
-(define_expand "ashrhi3"
-  [(set (match_operand:HI             0 "msp430_general_dst_nonv_operand")
-       (ashiftrt:HI (match_operand:HI 1 "general_operand")
-                    (match_operand:HI 2 "general_operand")))]
+;; All 430 and 430X SImode constant shifts
+(define_insn "<shift_insn>si3_const"
+  [(set (match_operand:SI              0 "msp430_general_dst_nonv_operand" "=rm")
+       (any_shift:SI (match_operand:SI 1 "general_operand"       "0")
+                     (match_operand:SI 2 "const_int_operand"     "n")))]
   ""
-  {
-    if ((GET_CODE (operands[1]) == SUBREG
-        && REG_P (XEXP (operands[1], 0)))
-       || MEM_P (operands[1]))
-      operands[1] = force_reg (HImode, operands[1]);
-    if (msp430x
-        && REG_P (operands[0])
-        && REG_P (operands[1])
-        && CONST_INT_P (operands[2]))
-      emit_insn (gen_430x_arithmetic_shift_right (operands[0], operands[1], operands[2]));
-    else if (CONST_INT_P (operands[2])
-            && INTVAL (operands[2]) == 1)
-      emit_insn (gen_srai_1 (operands[0], operands[1]));
-    else                
-       msp430_expand_helper (operands, \"__mspabi_srai\", !optimize_size);
-   DONE;
-   }
-)
-
-(define_insn "srai_1"
-  [(set (match_operand:HI             0 "msp430_general_dst_operand" "=rm")
-       (ashiftrt:HI (match_operand:HI 1 "msp430_general_operand"      "0")
-                    (const_int 1)))]
-  ""
-  "RRA%X0.W\t%0"
-)
-
-(define_insn "430x_arithmetic_shift_right"
-  [(set (match_operand:HI              0 "register_operand" "=r")
-       (ashiftrt:HI (match_operand:HI 1 "register_operand"  "0")
-                    (match_operand    2 "immediate_operand" "n")))]
-  "msp430x"
-  "*
-  if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 5)
-    return \"RRAM.W\t%2, %0\";
-  else if (INTVAL (operands[2]) >= 5 && INTVAL (operands[2]) < 16)
-    return \"RPT\t%2 { RRAX.W\t%0\";
-  return \"# nop arith right shift\";
-  "
-)
-
-(define_insn "srap_1"
-  [(set (match_operand:PSI              0 "register_operand" "=r")
-       (ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
-                     (const_int 1)))]
-  "msp430x"
-  "RRAM.A #1,%0"
+  "* msp430_output_asm_shift_insns (<CODE>, SImode, operands, false); return \"\";"
+  [(set (attr "length")
+       (symbol_ref "msp430_output_asm_shift_insns (<CODE>, SImode, operands, true)"))
+   (set_attr "type" "single")]
 )
 
-(define_insn "srap_2"
-  [(set (match_operand:PSI              0 "register_operand" "=r")
-       (ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
-                     (const_int 2)))]
+(define_insn "ashl<mode>3_430x"
+  [(set (match_operand:HPSI             0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
+       (ashift:HPSI (match_operand:HPSI 1 "general_operand"                 "0 ,0,0,0")
+                    (match_operand:HPSI 2 "const_int_operand"               "M ,P,K,i")))]
   "msp430x"
-  "RRAM.A #2,%0"
-)
-
-(define_insn "sral_1"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand" "=rm")
-       (ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
-                    (const_int 1)))]
-  ""
-  "RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_insn "sral_2"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand" "=rm")
-       (ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
-                    (const_int 2)))]
-  ""
-  "RRA%X0.W\t%H0 { RRC%X0.W\t%L0 { RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_expand "ashrsi3"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand")
-       (ashiftrt:SI (match_operand:SI 1 "general_operand")
-                    (match_operand:SI 2 "general_operand")))]
-  ""
-  "msp430_expand_helper (operands, \"__mspabi_sral\", !optimize_size);
-   DONE;"
-)
-
-(define_expand "ashrdi3"
-  [(set (match_operand:DI           0 "msp430_general_dst_nonv_operand")
-       (ashift:DI (match_operand:DI 1 "general_operand")
-                  (match_operand:DI 2 "general_operand")))]
-  ""
-  {
-    /* No const_variant for 64-bit shifts.  */
-    msp430_expand_helper (operands, \"__mspabi_srall\", false);
-    DONE;
-  }
-)
-
-;;----------
-
-;; unsigned A >> C
-
-(define_expand "lshrhi3"
-  [(set (match_operand:HI             0 "msp430_general_dst_nonv_operand")
-       (lshiftrt:HI (match_operand:HI 1 "general_operand")
-                    (match_operand:HI 2 "general_operand")))]
-  ""
-  {
-    if ((GET_CODE (operands[1]) == SUBREG
-        && REG_P (XEXP (operands[1], 0)))
-       || MEM_P (operands[1]))
-      operands[1] = force_reg (HImode, operands[1]);
-    if (msp430x
-        && REG_P (operands[0])
-        && REG_P (operands[1])
-        && CONST_INT_P (operands[2]))
-      emit_insn (gen_430x_logical_shift_right (operands[0], operands[1], operands[2]));
-    else if (CONST_INT_P (operands[2])
-            && INTVAL (operands[2]) == 1)
-      emit_insn (gen_srli_1 (operands[0], operands[1]));
-    else                
-      msp430_expand_helper (operands, \"__mspabi_srli\", !optimize_size);
-    DONE;
-  }
-)
-
-(define_insn "srli_1"
-  [(set (match_operand:HI             0 "msp430_general_dst_nonv_operand" "=rm")
-       (lshiftrt:HI (match_operand:HI 1 "general_operand"       "0")
-                    (const_int 1)))]
-  ""
-  "CLRC { RRC%X0.W\t%0"
-)
-
-(define_insn "430x_logical_shift_right"
-  [(set (match_operand:HI              0 "register_operand" "=r")
-       (lshiftrt:HI (match_operand:HI 1 "register_operand"  "0")
-                    (match_operand    2 "immediate_operand" "n")))]
+  "@
+  RLAM%b0\t%2, %0
+  RPT\t%2 { RLAX%b0\t%0
+  RPT\t#16 { RLAX%b0\t%0 { RPT\t%W2 { RLAX%b0\t%0
+  # undefined behavior left shift of %1 by %2"
+  [(set_attr "length" "2,4,8,0")
+   (set_attr "type" "single")]
+)
+
+(define_insn "ashr<mode>3_430x"
+  [(set (match_operand:HPSI               0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
+       (ashiftrt:HPSI (match_operand:HPSI 1 "general_operand"               "0,0,0,0")
+                      (match_operand:HPSI 2 "const_int_operand"             "M,P,K,i")))]
   "msp430x"
-  {
-    return msp430x_logical_shift_right (operands[2]);
-  }
-)
-
-(define_insn "srlp_1"
-  [(set (match_operand:PSI              0 "register_operand" "=r")
-       (lshiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
-                     (const_int 1)))]
-  ""
-  "RRUM.A #1,%0"
-)
-
-(define_insn "srll_1"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand" "=rm")
-       (lshiftrt:SI (match_operand:SI 1 "general_operand"       "0")
-                    (const_int 1)))]
-  ""
-  "CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_insn "srll_2x"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand" "=r")
-       (lshiftrt:SI (match_operand:SI 1 "general_operand"       "0")
-                    (const_int 2)))]
+  "@
+  RRAM%b0\t%2, %0
+  RPT\t%2 { RRAX%b0\t%0
+  RPT\t#16 { RRAX%b0\t%0 { RPT\t%W2 { RRAX%b0\t%0
+  # undefined behavior arithmetic right shift of %1 by %2"
+  [(set_attr "length" "2,4,8,0")
+   (set_attr "type" "single")]
+)
+
+(define_insn "lshr<mode>3_430x"
+  [(set (match_operand:HPSI               0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
+       (lshiftrt:HPSI (match_operand:HPSI 1 "general_operand"               "0,0,0,0")
+                      (match_operand:HPSI 2 "const_int_operand"             "M,P,K,i")))]
   "msp430x"
-  "RRUX.W\t%H0 { RRC.W\t%L0 { RRUX.W\t%H0 { RRC.W\t%L0"
-)
-
-(define_expand "lshrsi3"
-  [(set (match_operand:SI             0 "msp430_general_dst_nonv_operand")
-       (lshiftrt:SI (match_operand:SI 1 "general_operand")
-                    (match_operand:SI 2 "general_operand")))]
-  ""
-  "msp430_expand_helper (operands, \"__mspabi_srll\", !optimize_size);
-   DONE;"
-)
-
-(define_expand "lshrdi3"
-  [(set (match_operand:DI           0 "msp430_general_dst_nonv_operand")
-       (ashift:DI (match_operand:DI 1 "general_operand")
-                  (match_operand:DI 2 "general_operand")))]
-  ""
-  {
-    /* No const_variant for 64-bit shifts.  */
-    msp430_expand_helper (operands, \"__mspabi_srlll\", false);
-    DONE;
-  }
+  "@
+  RRUM%b0\t%2, %0
+  RPT\t%2 { RRUX%b0\t%0
+  RPT\t#16 { RRUX%b0\t%0 { RPT\t%W2 { RRUX%b0\t%0
+  # undefined behavior logical right shift of %1 by %2"
+  [(set_attr "length" "2,4,8,0")
+   (set_attr "type" "single")]
 )
 
 ;;------------------------------------------------------------
   [(const_int 0)]
   ""
   "msp430_expand_prologue (); DONE;"
-  )
+)
 
 (define_expand "epilogue"
   [(const_int 0)]
   ""
   "msp430_expand_epilogue (0); DONE;"
-  )
+)
 
 (define_insn "epilogue_helper"
   [(set (pc)
-        (unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER))
+       (unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER))
    (return)]
-  ""
+  "!msp430x"
   "BR%Q0\t#__mspabi_func_epilog_%J0"
-  )
+  [(set_attr "length" "2")]
+)
 
 (define_insn "prologue_start_marker"
   [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_START_MARKER)]
   ""
   "; start of prologue"
-  )
+  [(set_attr "length" "0")]
+)
 
 (define_insn "prologue_end_marker"
   [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_END_MARKER)]
   ""
   "; end of prologue"
-  )
+  [(set_attr "length" "0")]
+)
 
 (define_insn "epilogue_start_marker"
   [(unspec_volatile [(const_int 0)] UNS_EPILOGUE_START_MARKER)]
   ""
   "; start of epilogue"
-  )
+  [(set_attr "length" "0")]
+)
 
 ;; This makes the linker add a call to exit() after the call to main()
 ;; in crt0
   [(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)]
   ""
   ".refsym\t__crt0_call_exit"
-  )
+  [(set_attr "length" "0")]
+)
 
 ;;------------------------------------------------------------
 ;; Jumps
         (match_operand 1 ""))]
   ""
   "CALL%Q0\t%0"
+  [(set_attr "extension" "none")
+   (set_attr "type" "single")]
 )
 
 (define_expand "call_value"
              (match_operand 2 "")))]
   ""
   "CALL%Q0\t%1"
+  [(set_attr "extension" "none")
+   (set_attr "type" "single")]
 )
 
 (define_insn "msp430_return"
   [(return)]
   ""
   { return msp430_is_interrupt_func () ? "RETI" : (TARGET_LARGE ? "RETA" : "RET"); }
+  [(set_attr "length" "2")]
 )
 
 ;; This pattern is NOT, as expected, a return pattern.  It's called
   "reload_completed"
   [(const_int 0)]
   "msp430_expand_epilogue (1); DONE;"
-  )
+  [(set_attr "length" "40")]
+)
 
 (define_insn "jump"
   [(set (pc)
        (label_ref (match_operand 0 "" "")))]
   ""
   "BR%Q0\t#%l0"
+  [(set_attr "length" "4")]
 )
 
 ;; FIXME: GCC currently (8/feb/2013) cannot handle symbol_refs
        (match_operand 0 "nonimmediate_operand" "rYl"))]
   ""
   "BR%Q0\t%0"
+  [(set (attr "length")
+       (if_then_else (match_operand 0 "register_operand" "")
+                     (const_int 2)
+                     (const_int 4)))]
 )
 
 ;;------------------------------------------------------------
   )]
   ""
   "msp430_fixup_compare_operands (<MODE>mode, operands);"
-  )
+)
 
 (define_insn "cbranchpsi4_real"
   [(set (pc) (if_then_else
              (match_operator                     0 "msp430_cmp_operator"
                              [(match_operand:PSI 1 "msp430_general_dst_nonv_operand" "r,rYs,rm")
                               (match_operand:PSI 2 "general_operand"      "rLs,rYsi,rmi")])
-              (label_ref (match_operand           3 "" ""))
+             (label_ref (match_operand           3 "" ""))
              (pc)))
    (clobber (reg:BI CARRY))
    ]
   CMP%Q0\t%2, %1 { J%0\t%l3
   CMPX.A\t%2, %1 { J%0\t%l3
   CMPX.A\t%2, %1 { J%0\t%l3"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "cbranchqi4_real"
   [(set (pc) (if_then_else
   "@
    CMP.B\t%2, %1 { J%0\t%l3
    CMPX.B\t%2, %1 { J%0\t%l3"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "cbranchhi4_real"
   [(set (pc) (if_then_else
    (clobber (reg:BI CARRY))
    ]
   ""
-  "*
-    /* This is nasty.  If we are splitting code between low and high memory
-       then we do not want the linker to increase the size of sections by
-       relaxing out of range jump instructions.  (Since relaxation occurs
-       after section placement).  So we have to generate pessimal branches
-       here.  But we only want to do this when really necessary.
-
-       FIXME: Do we need code in the other cbranch patterns ?  */
-    if (msp430_do_not_relax_short_jumps () && get_attr_length (insn) > 6)
-      {
-        return which_alternative == 0 ?
-            \"CMP.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\" :
-           \"CMPX.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\";
-      }
-
-    return which_alternative == 0 ?
-         \"CMP.W\t%2, %1 { J%0\t%l3\" :
-        \"CMPX.W\t%2, %1 { J%0\t%l3\";
-  "
-  [(set (attr "length")
-       (if_then_else
-         (and (ge (minus (match_dup 3) (pc)) (const_int -510))
-              (le (minus (match_dup 3) (pc)) (const_int 510)))
-         (const_int 6)
-         (const_int 10))
-       )]
-  )
+  "@
+   CMP.W\t%2, %1 { J%0\t%l3
+   CMPX.W\t%2, %1 { J%0\t%l3"
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "cbranchpsi4_reversed"
   [(set (pc) (if_then_else
   CMP%Q0\t%1, %2 { J%R0\t%l3
   CMPX.A\t%1, %2 { J%R0\t%l3
   CMPX.A\t%1, %2 { J%R0\t%l3"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "cbranchqi4_reversed"
   [(set (pc) (if_then_else
   "@
    CMP.B\t%1, %2 { J%R0\t%l3
    CMPX.B\t%1, %2 { J%R0\t%l3"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "cbranchhi4_reversed"
   [(set (pc) (if_then_else
   "@
    CMP.W\t%1, %2 { J%R0\t%l3
    CMPX.W\t%1, %2 { J%R0\t%l3"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "cmp")]
+)
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
              (ne (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
                           (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
                  (const_int 0))
-              (label_ref (match_operand 2 "" ""))
+             (label_ref (match_operand 2 "" ""))
              (pc)))
    (clobber (reg:BI CARRY))
    ]
   "@
    BIT%x0%b0\t%1, %0 { JNE\t%l2
    BITX%b0\t%1, %0 { JNE\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
              (eq (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
                           (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
                  (const_int 0))
-              (label_ref (match_operand 2 "" ""))
+             (label_ref (match_operand 2 "" ""))
              (pc)))
    (clobber (reg:BI CARRY))
    ]
   "@
    BIT%x0%b0\t%1, %0 { JEQ\t%l2
    BITX%b0\t%1, %0 { JEQ\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
              (eq (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
                           (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
                  (const_int 0))
-              (pc)
+             (pc)
              (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   "@
   BIT%x0%b0\t%1, %0 { JNE\t%l2
   BITX%b0\t%1, %0 { JNE\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
              (ne (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
                           (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
                  (const_int 0))
-              (pc)
+             (pc)
              (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   "@
   BIT%x0%b0\t%1, %0 { JEQ\t%l2
   BITX%b0\t%1, %0 { JEQ\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 ;;------------------------------------------------------------
 ;; zero-extract versions of the above
   [(set (pc) (if_then_else
              (ne (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rYs,rm")
                                    (const_int 1)
-                                   (match_operand 1 "msp430_bitpos" "i,i"))
+                                   (match_operand 1 "const_0_to_15_operand" "i,i"))
                  (const_int 0))
-              (label_ref (match_operand 2 "" ""))
+             (label_ref (match_operand 2 "" ""))
              (pc)))
    (clobber (reg:BI CARRY))
    ]
   "@
    BIT%x0%b0\t%p1, %0 { JNE\t%l2
    BIT%X0%b0\t%p1, %0 { JNE\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4_z"
   [(set (pc) (if_then_else
              (eq (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
                                   (const_int 1)
-                                  (match_operand 1 "msp430_bitpos" "i"))
+                                  (match_operand 1 "const_0_to_15_operand" "i"))
                  (const_int 0))
-              (label_ref (match_operand 2 "" ""))
+             (label_ref (match_operand 2 "" ""))
              (pc)))
    (clobber (reg:BI CARRY))
    ]
   ""
   "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4_z"
   [(set (pc) (if_then_else
              (eq (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
                                   (const_int 1)
-                                  (match_operand 1 "msp430_bitpos" "i"))
+                                  (match_operand 1 "const_0_to_15_operand" "i"))
                  (const_int 0))
-              (pc)
+             (pc)
              (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   ""
   "BIT%X0%b0\t%p1, %0 { JNE\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 (define_insn "*bitbranch<mode>4_z"
   [(set (pc) (if_then_else
              (ne (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
                                   (const_int 1)
-                                  (match_operand 1 "msp430_bitpos" "i"))
+                                  (match_operand 1 "const_0_to_15_operand" "i"))
                  (const_int 0))
-              (pc)
+             (pc)
              (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   ""
   "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
-  )
+  [(set_attr "extra_length" "2")
+   (set_attr "type" "double")]
+)
 
 ;;------------------------------------------------------------
 ;; Misc
   [(const_int 0)]
   "1"
   "NOP"
+  [(set_attr "length" "2")]
 )
 
 (define_insn "disable_interrupts"
   [(unspec_volatile [(const_int 0)] UNS_DINT)]
   ""
   "DINT \; NOP"
-  )
+  [(set_attr "length" "2")]
+)
 
 (define_insn "enable_interrupts"
   [(unspec_volatile [(const_int 0)] UNS_EINT)]
   ""
   "EINT"
-  )
+  [(set_attr "length" "2")]
+)
 
 (define_insn "push_intr_state"
   [(unspec_volatile [(const_int 0)] UNS_PUSH_INTR)]
   ""
   "PUSH\tSR"
-  )
+  [(set_attr "length" "2")]
+)
 
 (define_insn "pop_intr_state"
   [(unspec_volatile [(const_int 0)] UNS_POP_INTR)]
   ""
   "POP\tSR"
-  )
+  [(set_attr "length" "2")]
+)
 
 ;; Clear bits in the copy of the status register that is currently
 ;; saved on the stack at the top of the interrupt handler.
   [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIC_SR)]
   ""
   "BIC.W\t%0, %O0(SP)"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extra_length" "2")]
+)
 
 ;; Set bits in the copy of the status register that is currently
 ;; saved on the stack at the top of the interrupt handler.
   [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIS_SR)]
   ""
   "BIS.W\t%0, %O0(SP)"
-  )
+  [(set_attr "type" "single")
+   (set_attr "extra_length" "2")]
+)
 
 ;; For some reason GCC is generating (set (reg) (and (neg (reg)) (int)))
 ;; very late on in the compilation and not splitting it into separate
 ;; instructions, so we provide a pattern to support it here.
 (define_insn "andneghi3"
-  [(set (match_operand:HI                 0 "register_operand" "=r")
-       (and:HI (neg:HI (match_operand:HI 1 "register_operand"  "r"))
-               (match_operand            2 "immediate_operand" "n")))]
+  [(set (match_operand:HI                0 "register_operand" "=r,r")
+       (and:HI (neg:HI (match_operand:HI 1 "general_operand"  "0,rm"))
+               (match_operand            2 "immediate_operand" "n,n")))]
   ""
-  "*
-    if (REGNO (operands[0]) != REGNO (operands[1]))
-      return \"MOV.W\t%1, %0 { INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0\";
-    else
-      return \"INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0\";
-  "
-  )
+  "@
+  INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0
+  MOV%X1.W\t%1, %0 { INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0"
+  [(set_attr "length" "12,14")
+   (set_attr "type" "double")]
+)
+
 
 (define_insn "delay_cycles_start"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")]
                    UNS_DELAY_START)]
   ""
   "; Begin %J0 cycle delay"
-  )
+  [(set_attr "length" "0")]
+)
 
 (define_insn "delay_cycles_end"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")]
        JNE     1b
        POP     r14
        POP     r13"
-  )
+  [(set_attr "length" "32")]
+)
 
 (define_insn "delay_cycles_32x"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
        TST.W   r13
        JNE     1b
        POPM.A  #2,r14"
-  )
+  [(set_attr "length" "28")]
+)
 
 (define_insn "delay_cycles_16"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
 1:     SUB.W   #1, r13
        JNE     1b
        POP     r13"
-  )
+  [(set_attr "length" "14")]
+)
 
 (define_insn "delay_cycles_16x"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
 1:     SUB.W   #1, r13
        JNE     1b
        POPM.A  #1,r13"
-  )
+  [(set_attr "length" "14")]
+)
 
 (define_insn "delay_cycles_2"
   [(unspec_volatile [(const_int 0) ] UNS_DELAY_2)]
   ""
   "JMP .+2"
-  )
+  [(set_attr "length" "2")]
+)
 
 (define_insn "delay_cycles_1"
   [(unspec_volatile [(const_int 0) ] UNS_DELAY_1)]
   ""
   "NOP"
-  )
+  [(set_attr "length" "2")]
+)
+
+(define_expand "mulhi3"
+  [(set (match_operand:HI                         0 "register_operand" "=r")
+       (mult:HI (match_operand:HI 1 "register_operand" "%0")
+                (match_operand:HI 2 "register_operand" "r")))]
+  "msp430_has_hwmult ()"
+  {
+    msp430_expand_helper (operands, "__mspabi_mpyi", false);
+    DONE;
+  }
+)
+
+(define_expand "mulsi3"
+  [(set (match_operand:SI                         0 "register_operand" "=r")
+       (mult:SI (match_operand:SI 1 "register_operand" "%0")
+                (match_operand:SI 2 "register_operand" "r")))]
+  "msp430_has_hwmult ()"
+  {
+    msp430_expand_helper (operands, "__mspabi_mpyl", false);
+    DONE;
+  }
+)
 
 ; libgcc helper functions for widening multiplication aren't currently
 ; generated by gcc, so we can't catch them later and map them to the mspabi
 ; If we don't have hardware multiply support, it will generally be slower and
 ; result in larger code to call the mspabi library function to perform the
 ; widening multiplication than just leaving GCC to widen the arguments itself.
-;
-; We don't use library functions for SImode->DImode widening since its always
-; larger and slower than letting GCC widen the arguments inline.
+
 (define_expand "mulhisi3"
   [(set (match_operand:SI                         0 "register_operand" "=r")
        (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
   }
 )
 
+(define_expand "mulsidi3"
+  [(set (match_operand:DI                         0 "register_operand" "=r")
+       (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+                (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+  "msp430_has_hwmult ()"
+  {
+    /* Leave the other case for the inline insn.  */
+    if (!(optimize > 2 && msp430_has_hwmult ()))
+    {
+      msp430_expand_helper (operands, "__mspabi_mpysll", false);
+      DONE;
+    }
+  }
+)
+
+(define_expand "umulsidi3"
+  [(set (match_operand:DI                         0 "register_operand" "=r")
+       (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+                (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+  "msp430_has_hwmult ()"
+  {
+    /* Leave the other case for the inline insn.  */
+    if (!(optimize > 2 && msp430_has_hwmult ()))
+    {
+      msp430_expand_helper (operands, "__mspabi_mpyull", false);
+      DONE;
+    }
+  }
+)
+
+
 (define_insn "*mulhisi3_inline"
   [(set (match_operand:SI                          0 "register_operand" "=r")
        (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
     else
       return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0132 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\";
   "
+  [(set_attr "length" "24")]
 )
 
 (define_insn "*umulhisi3_inline"
     else
       return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0130 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\";
   "
+  [(set_attr "length" "24")]
 )
 
-(define_insn "mulsidi3"
+(define_insn "*mulsidi3_inline"
   [(set (match_operand:DI                          0 "register_operand" "=r")
        (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
                 (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
     else
       return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0144 { MOV.W %H1, &0x0146 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\";
   "
+  [(set_attr "length" "40")]
 )
 
-(define_insn "umulsidi3"
+(define_insn "*umulsidi3_inline"
   [(set (match_operand:DI                          0 "register_operand" "=r")
        (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
                 (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
     else
       return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0140 { MOV.W %H1, &0x0142 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\";
   "
+  [(set_attr "length" "40")]
 )