MSP430: Add defaulting to the insn length attribute
authorJozef Lawrynowicz <jozef.l@mittosystems.com>
Fri, 13 Nov 2020 15:35:47 +0000 (15:35 +0000)
committerJozef Lawrynowicz <jozef.l@mittosystems.com>
Fri, 13 Nov 2020 15:36:30 +0000 (15:36 +0000)
The length of MSP430 instructions is mostly just a function of the type
and number of operands. Setting the "type" attribute on all insns
describes the number of operands, and the position of the source and
destination operands.

In most cases, defaulting in the "length" and "extension" attribute
definitions can then be used to calculate the total length of the
instruction by using the value of the "type" attribute to examine the
operands.

gcc/ChangeLog:

* config/msp430/msp430-protos.h (msp430x_extendhisi): Return int
instead of char *.
(msp430_output_asm_shift_insns): Likewise.
Add new return_length argument.
(msp430x_insn_required): Add prototype.
* config/msp430/msp430.c (msp430_output_asm_shift_insns): Return the
total length, in bytes, of the emitted instructions.
(msp430x_insn_required): New function.
(msp430x_extendhisi): Return the total length, in bytes, of the
emitted instructions.
* config/msp430/msp430.h (ADJUST_INSN_LENGTH): Define.
* config/msp430/msp430.md: New define_attr "type".
New define_attr "extension".
New define_attr "length_multiplier".
New define_attr "extra_length".
Rewrite define_attr "length".
Set type, extension, length, length_multiplier or extra_length insn
attributes on all insns, as appropriate.
(andneghi3): Rewrite using constraints instead of C code to decide
output insns.
* config/msp430/predicates.md (msp430_cheap_operand): New predicate.
(msp430_high_memory_operand): New predicate.

gcc/config/msp430/msp430-protos.h
gcc/config/msp430/msp430.c
gcc/config/msp430/msp430.h
gcc/config/msp430/msp430.md
gcc/config/msp430/predicates.md

index 0b4d9a42b4130929525b62a101e6d535493c6f78..33ad1adc61f65711374dac65f1020f9262fb833a 100644 (file)
@@ -26,7 +26,7 @@ void  msp430_expand_eh_return (rtx);
 void   msp430_expand_epilogue (int);
 void   msp430_expand_helper (rtx *operands, const char *, bool);
 void   msp430_expand_prologue (void);
-const char * msp430x_extendhisi (rtx *);
+int msp430x_extendhisi (rtx *, bool);
 void   msp430_fixup_compare_operands (machine_mode, rtx *);
 int    msp430_hard_regno_nregs_has_padding (int, machine_mode);
 int    msp430_hard_regno_nregs_with_padding (int, machine_mode);
@@ -49,10 +49,11 @@ rtx msp430_subreg (machine_mode, rtx, machine_mode, int);
 bool    msp430_use_f5_series_hwmult (void);
 bool   msp430_has_hwmult (void);
 bool msp430_op_not_in_high_mem (rtx op);
+bool msp430x_insn_required (rtx op);
 
 #ifdef RTX_CODE
 int msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands);
-const char * msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode, rtx *operands);
+int msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode, rtx *operands, bool);
 #endif
 
 #endif /* GCC_MSP430_PROTOS_H */
index 9f7635118b1d948c22dbf84e589ab9f47d855604..cc3472e853dc7509c1beb31bd22ec2413155db3a 100644 (file)
@@ -3520,18 +3520,22 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
    For 430X it is inneficient to do so for any modes except SI and DI, since we
    can make use of R*M insns or RPT with 430X insns, so this function is only
    used for SImode in that case.  */
-const char *
+int
 msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
-                              rtx *operands)
+                              rtx *operands, bool return_length)
 {
   int i;
   int amt;
   int max_shift = GET_MODE_BITSIZE (mode) - 1;
+  int length = 0;
+
   gcc_assert (CONST_INT_P (operands[2]));
   amt = INTVAL (operands[2]);
 
   if (amt == 0 || amt > max_shift)
     {
+      if (return_length)
+       return 0;
       switch (code)
        {
        case ASHIFT:
@@ -3549,17 +3553,28 @@ msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
        default:
          gcc_unreachable ();
        }
-      return "";
+      return 0;
     }
 
   if (code == ASHIFT)
     {
       if (!msp430x && mode == HImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("RLA.W\t%0", operands);
+       {
+         if (return_length)
+           length = 2 + (MEM_P (operands[0]) ? 2 : 0);
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("RLA.W\t%0", operands);
+       }
       else if (mode == SImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("RLA%X0.W\t%L0 { RLC%X0.W\t%H0", operands);
+       {
+         if (return_length)
+           length = 4 + (MEM_P (operands[0]) ? 4 : 0)
+             + (4 * msp430x_insn_required (operands[0]));
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("RLA%X0.W\t%L0 { RLC%X0.W\t%H0", operands);
+       }
       else
        /* Catch unhandled cases.  */
        gcc_unreachable ();
@@ -3567,33 +3582,61 @@ msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
   else if (code == ASHIFTRT)
     {
       if (!msp430x && mode == HImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("RRA.W\t%0", operands);
+       {
+         if (return_length)
+           length = 2 + (MEM_P (operands[0]) ? 2 : 0);
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("RRA.W\t%0", operands);
+       }
       else if (mode == SImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("RRA%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+       {
+         if (return_length)
+           length = 4 + (MEM_P (operands[0]) ? 4 : 0)
+             + (4 * msp430x_insn_required (operands[0]));
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("RRA%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+       }
       else
        gcc_unreachable ();
     }
   else if (code == LSHIFTRT)
     {
       if (!msp430x && mode == HImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("CLRC { RRC.W\t%0", operands);
+       {
+         if (return_length)
+           length = 4 + (MEM_P (operands[0]) ? 2 : 0);
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("CLRC { RRC.W\t%0", operands);
+       }
       else if (mode == SImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+       {
+         if (return_length)
+           length = 6 + (MEM_P (operands[0]) ? 4 : 0)
+             + (4 * msp430x_insn_required (operands[0]));
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0",
+                              operands);
+       }
       /* FIXME: Why doesn't "RRUX.W\t%H0 { RRC%X0.W\t%L0" work for msp430x?
         It causes execution timeouts e.g. pr41963.c.  */
 #if 0
       else if (msp430x && mode == SImode)
-       for (i = 0; i < amt; i++)
-         output_asm_insn ("RRUX.W\t%H0 { RRC%X0.W\t%L0", operands);
+       {
+         if (return_length)
+           length = 2;
+         else
+           for (i = 0; i < amt; i++)
+             output_asm_insn ("RRUX.W\t%H0 { RRC%X0.W\t%L0", operands);
+       }
 #endif
       else
        gcc_unreachable ();
     }
-  return "";
+  return length * amt;
 }
 
 /* Called by cbranch<mode>4 to coerce operands into usable forms.  */
@@ -4115,6 +4158,20 @@ msp430_op_not_in_high_mem (rtx op)
   return false;
 }
 
+/* Based on the operand OP, is a 430X insn required to handle it?
+   There are only 3 conditions for which a 430X insn is required:
+   - PSImode operand
+   - memory reference to a symbol which could be in upper memory
+     (so its address is > 0xFFFF)
+   - absolute address which has VOIDmode, i.e. (mem:HI (const_int))
+   Use a 430 insn if none of these conditions are true.  */
+bool
+msp430x_insn_required (rtx op)
+{
+  return (GET_MODE (op) == PSImode
+         || !msp430_op_not_in_high_mem (op));
+}
+
 #undef  TARGET_PRINT_OPERAND
 #define TARGET_PRINT_OPERAND           msp430_print_operand
 
@@ -4455,35 +4512,52 @@ msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
 
 /* Generate a sequence of instructions to sign-extend an HI
    value into an SI value.  Handles the tricky case where
-   we are overwriting the destination.  */
-
-const char *
-msp430x_extendhisi (rtx * operands)
+   we are overwriting the destination.
+   Return the number of bytes used by the emitted instructions.
+   If RETURN_LENGTH is true then do not emit the assembly instruction
+   sequence.  */
+int
+msp430x_extendhisi (rtx * operands, bool return_length)
 {
   if (REGNO (operands[0]) == REGNO (operands[1]))
-    /* Low word of dest == source word.  8-byte sequence.  */
-    return "BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0";
-
-  if (! msp430x)
-    /* Note: This sequence is approximately the same length as invoking a helper
-       function to perform the sign-extension, as in:
-
-       MOV.W  %1, %L0
-       MOV.W  %1, r12
-       CALL   __mspabi_srai_15
-       MOV.W  r12, %H0
-
-       but this version does not involve any function calls or using argument
-       registers, so it reduces register pressure.  10-byte sequence.  */
-    return "MOV.W\t%1, %L0 { BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 "
-      "{ INV.W\t%H0, %H0";
-
-  if (REGNO (operands[0]) + 1 == REGNO (operands[1]))
-    /* High word of dest == source word.  6-byte sequence.  */
-    return "MOV.W\t%1, %L0 { RPT\t#15 { RRAX.W\t%H0";
+    {
+      /* Low word of dest == source word.  */
+      if (!return_length)
+       output_asm_insn ("BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0",
+                        operands);
+      return 8;
+    }
+  else if (! msp430x)
+    {
+      /* Note: This sequence is approximately the same length as invoking a
+        helper function to perform the sign-extension, as in:
+
+        MOV.W  %1, %L0
+        MOV.W  %1, r12
+        CALL   __mspabi_srai_15
+        MOV.W  r12, %H0
+
+        but this version does not involve any function calls or using argument
+        registers, so it reduces register pressure.  */
+      if (!return_length)
+       output_asm_insn ("MOV.W\t%1, %L0 { BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0",
+                        operands);
+      return 10;
+    }
+  else if (REGNO (operands[0]) + 1 == REGNO (operands[1]))
+    {
+      /* High word of dest == source word.  */
+      if (!return_length)
+       output_asm_insn ("MOV.W\t%1, %L0 { RPT\t#15 { RRAX.W\t%H0",
+                        operands);
+      return 6;
+    }
 
-  /* No overlap between dest and source.  8-byte sequence.  */
-  return "MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0";
+  /* No overlap between dest and source.  */
+  if (!return_length)
+    output_asm_insn ("MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0",
+                    operands);
+  return 8;
 }
 
 /* Stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)).  */
index 25007716d240271478e1ed01d680df8c51ca7cf9..c2fcaef6c5045810bd0beacc7f1e98217103a817 100644 (file)
@@ -530,3 +530,13 @@ void msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
 
 
 #define SYMBOL_FLAG_LOW_MEM (SYMBOL_FLAG_MACH_DEP << 0)
+
+#define ADJUST_INSN_LENGTH(insn, length) \
+  do   \
+    {  \
+      if (recog_memoized (insn) >= 0)                  \
+       {                                               \
+         length += get_attr_extra_length (insn);       \
+         length *= get_attr_length_multiplier (insn);  \
+       }                                               \
+    } while (0)
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")]
 )
index 4bfa0c0f2d5dd91879f1aa147049810d67f804fc..eb1f61df780bb742685b709b78a82ef6857fda54 100644 (file)
 (define_predicate "msp430_symbol_operand"
   (match_code "symbol_ref")
 )
+
+; Used in length attribute tests - if a source operand is a reg,
+; (mem (post_inc)), or (mem (reg)) then it is cheap compared to other operand
+; types.
+(define_predicate "msp430_cheap_operand"
+  (ior (match_code "reg")
+       (and (match_code "mem")
+           (ior (match_code "reg" "0")
+           (match_code "post_inc" "0")))))
+
+; Used for insn attributes only.  For insn patterns themselves, use constraints.
+(define_predicate "msp430_high_memory_operand"
+  (match_test "msp430x_insn_required (op)"))