]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/config/msp430/msp430.md
MSP430: Add defaulting to the insn length attribute
[thirdparty/gcc.git] / gcc / config / msp430 / msp430.md
index ad244bb0f3396724f3efbbd345c4e9f5f92ad3d9..65e951774b16a395762bb1ad44d4751ae2f80b24 100644 (file)
    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")
        (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"
   "@
   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.
   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 ""
   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
   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 ""
   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")]
 )
 
 ;;------------------------------------------------------------
        (any_shift:HI (match_operand:HI 1 "general_operand"       "0")
                      (match_operand:HI 2 "const_int_operand"     "n")))]
   "!msp430x"
-  "* return msp430_output_asm_shift_insns (<CODE>, HImode, operands);"
+  "* 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")]
 )
 
 ;; All 430 and 430X SImode constant shifts
        (any_shift:SI (match_operand:SI 1 "general_operand"       "0")
                      (match_operand:SI 2 "const_int_operand"     "n")))]
   ""
-  "* return msp430_output_asm_shift_insns (<CODE>, SImode, operands);"
+  "* 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 "ashl<mode>3_430x"
   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"
   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"
   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
   "@
    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"
   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
                                    (const_int 1)
                                    (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
                                   (const_int 1)
                                   (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
                                   (const_int 1)
                                   (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
                                   (const_int 1)
                                   (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 "general_operand"  "rm"))
-               (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%X1.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")]
+)
 
 ; 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
     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"
     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"
     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")]
 )