[3/5] MSP430: Add defaulting to the insn length attribute

Message ID 20200723155424.45qrdjwyltdr2oyw@jozef-acer-manjaro
State New
Headers show
Series
  • MSP430: Implement macros to describe relative costs of operations
Related show

Commit Message

Jozef Lawrynowicz July 23, 2020, 3:54 p.m.
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.
From 0e39cc3f13c604df1225d3c1eef6b05e629c184b Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

Date: Thu, 16 Jul 2020 11:32:01 +0100
Subject: [PATCH 3/5] MSP430: Add defaulting to the insn length attribute

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 |   5 +-
 gcc/config/msp430/msp430.c        | 162 ++++++++---
 gcc/config/msp430/msp430.h        |  10 +
 gcc/config/msp430/msp430.md       | 439 ++++++++++++++++++++++++------
 gcc/config/msp430/predicates.md   |  13 +
 5 files changed, 507 insertions(+), 122 deletions(-)

-- 
2.27.0

Patch

diff --git a/gcc/config/msp430/msp430-protos.h b/gcc/config/msp430/msp430-protos.h
index 0b4d9a42b41..33ad1adc61f 100644
--- a/gcc/config/msp430/msp430-protos.h
+++ b/gcc/config/msp430/msp430-protos.h
@@ -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 */
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 81ee5075a57..b7daafcc11a 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -3535,18 +3535,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:
@@ -3564,17 +3568,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 ();
@@ -3582,33 +3597,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.  */
@@ -4130,6 +4173,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
 
@@ -4462,35 +4519,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)).  */
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index fd48549f5c2..b813e825311 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -532,3 +532,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)
diff --git a/gcc/config/msp430/msp430.md b/gcc/config/msp430/msp430.md
index f70e61b97dd..68e1a66dbb1 100644
--- a/gcc/config/msp430/msp430.md
+++ b/gcc/config/msp430/msp430.md
@@ -58,8 +58,100 @@  (define_c_enum "unspec"
    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")
@@ -97,35 +189,43 @@  (define_insn "push"
 	(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
@@ -144,7 +244,9 @@  (define_insn "popm"
 		     (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
@@ -170,6 +272,10 @@  (define_insn "grow_and_swap"
       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"
@@ -178,7 +284,12 @@  (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.
@@ -189,6 +300,7 @@  (define_insn "movqihi"
   "@
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "movqi_topbyte"
@@ -196,6 +308,8 @@  (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"
@@ -205,6 +319,7 @@  (define_insn "movqi"
   "@
   MOV.B\t%1, %0
   MOVX.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "movhi"
@@ -215,6 +330,7 @@  (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"
@@ -222,7 +338,7 @@  (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")
@@ -235,7 +351,8 @@  (define_insn_and_split "movsi_s"
    (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")
@@ -248,6 +365,7 @@  (define_insn_and_split "movsi_x"
    (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.
@@ -260,7 +378,10 @@  (define_insn "movpsi"
   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
@@ -274,6 +395,8 @@  (define_insn "movsipsi2"
 	(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.
@@ -282,6 +405,8 @@  (define_insn "*movpsihi2_lo"
 	(subreg:HI (match_operand:PSI 1 "msp430_symbol_operand" "i") 0))]
   "msp430x"
   "MOVA\t%1, %0"
+  [(set_attr "extension" "a")
+   (set_attr "type" "double")]
 )
 
 ;;------------------------------------------------------------
@@ -295,6 +420,8 @@  (define_insn "addpsi3"
   "@
   ADDA\t%2, %0
   ADDX.A\t%2, %0"
+  [(set_attr "extension" "a,x")
+   (set_attr "type" "triple")]
 )
 
 (define_insn "addqi3"
@@ -305,6 +432,7 @@  (define_insn "addqi3"
   "@
    ADD.B\t%2, %0
    ADDX.B\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 (define_insn "addhi3"
@@ -315,6 +443,7 @@  (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.
@@ -327,6 +456,13 @@  (define_insn "addsipsi3"
 		 (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"
@@ -337,6 +473,8 @@  (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.
@@ -382,7 +520,8 @@  (define_insn "addhi3_cy"
   "@
    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")
@@ -397,7 +536,8 @@  (define_insn "addhi3_cy_i"
   "@
    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"
@@ -410,7 +550,8 @@  (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
@@ -440,7 +581,7 @@  (define_split
   if (msp430_split_addsi (operands))
     FAIL;
   "
-  )
+)
 
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
@@ -454,6 +595,9 @@  (define_insn "subpsi3"
   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.
@@ -467,6 +611,8 @@  (define_insn "subqi3"
   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.
@@ -480,6 +626,8 @@  (define_insn "subhi3"
   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"
@@ -490,6 +638,8 @@  (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"
@@ -500,6 +650,8 @@  (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"
@@ -510,6 +662,7 @@  (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"
@@ -521,6 +674,7 @@  (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"
@@ -531,6 +685,7 @@  (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"
@@ -541,6 +696,7 @@  (define_insn "xor<mode>3"
   "@
    XOR%x0%b0\t%2, %0
    XORX%b0\t%2, %0"
+  [(set_attr "type" "triple")]
 )
 
 ;; Macro : XOR #~0, %0
@@ -551,6 +707,7 @@  (define_insn "one_cmpl<mode>2"
   "@
    INV%x0%b0\t%0
    INV%X0%b0\t%0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "extendqihi2"
@@ -560,6 +717,7 @@  (define_insn "extendqihi2"
   "@
    SXT%X0\t%0
    SXT%X0\t%0"
+  [(set_attr "type" "single")]
 )
 
 (define_insn "extendqipsi2"
@@ -569,6 +727,8 @@  (define_insn "extendqipsi2"
   "@
   SXT\t%0
   SXTX.A\t%0"
+  [(set_attr "type" "single")
+   (set_attr "extension" "none,x")]
 )
 
 ;; ------------------------
@@ -590,6 +750,7 @@  (define_insn "zero_extendqihi2"
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0
    AND%X0\t#0xff, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "zero_extendqipsi2"
@@ -599,6 +760,7 @@  (define_insn "zero_extendqipsi2"
   "@
    MOV.B\t%1, %0
    MOV%X1.B\t%1, %0"
+  [(set_attr "type" "double")]
 )
 
 (define_insn "zero_extendqisi2"
@@ -608,6 +770,9 @@  (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"
@@ -618,6 +783,7 @@  (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"
@@ -627,6 +793,8 @@  (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"
@@ -636,6 +804,8 @@  (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
@@ -671,6 +841,13 @@  (define_insn "zero_extendpsisi2"
     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
@@ -687,6 +864,7 @@  (define_insn ""
 	(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 ""
@@ -696,6 +874,7 @@  (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.
@@ -711,6 +890,9 @@  (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")]
 )
 
 (define_insn ""
@@ -722,6 +904,9 @@  (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
@@ -734,6 +919,9 @@  (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")]
 )
 
 (define_insn ""
@@ -741,6 +929,8 @@  (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 ""
@@ -752,6 +942,9 @@  (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
 
@@ -771,13 +964,18 @@  (define_insn "truncpsihi2"
 	(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"
@@ -785,6 +983,9 @@  (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
@@ -798,6 +999,9 @@  (define_insn "extend_and_shift1_hipsi2"
 		   (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"
@@ -806,6 +1010,9 @@  (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).
@@ -827,6 +1034,8 @@  (define_insn "extendpsisi2"
     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
@@ -836,6 +1045,8 @@  (define_insn "truncsipsi2"
 	(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")]
 )
 
 ;;------------------------------------------------------------
@@ -886,7 +1097,10 @@  (define_insn "<shift_insn>hi3_430"
 	(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
@@ -895,7 +1109,10 @@  (define_insn "<shift_insn>si3_const"
 	(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"
@@ -908,6 +1125,8 @@  (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"
@@ -920,6 +1139,8 @@  (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"
@@ -932,6 +1153,8 @@  (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")]
 )
 
 ;;------------------------------------------------------------
@@ -941,39 +1164,43 @@  (define_expand "prologue"
   [(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
@@ -981,7 +1208,8 @@  (define_insn "msp430_refsym_need_exit"
   [(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)]
   ""
   ".refsym\t__crt0_call_exit"
-  )
+  [(set_attr "length" "0")]
+)
 
 ;;------------------------------------------------------------
 ;; Jumps
@@ -998,6 +1226,8 @@  (define_insn "call_internal"
 	 (match_operand 1 ""))]
   ""
   "CALL%Q0\t%0"
+  [(set_attr "extension" "none")
+   (set_attr "type" "single")]
 )
 
 (define_expand "call_value"
@@ -1014,12 +1244,15 @@  (define_insn "call_value_internal"
 	      (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
@@ -1045,13 +1278,15 @@  (define_insn_and_split "msp430_eh_epilogue"
   "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
@@ -1061,6 +1296,10 @@  (define_insn "indirect_jump"
 	(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)))]
 )
 
 ;;------------------------------------------------------------
@@ -1077,14 +1316,14 @@  (define_expand "cbranch<mode>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))
    ]
@@ -1093,7 +1332,9 @@  (define_insn "cbranchpsi4_real"
   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
@@ -1108,7 +1349,9 @@  (define_insn "cbranchqi4_real"
   "@
    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
@@ -1123,6 +1366,8 @@  (define_insn "cbranchhi4_real"
   "@
    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"
@@ -1139,7 +1384,9 @@  (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
@@ -1154,7 +1401,9 @@  (define_insn "cbranchqi4_reversed"
   "@
    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
@@ -1169,14 +1418,16 @@  (define_insn "cbranchhi4_reversed"
   "@
    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))
    ]
@@ -1184,14 +1435,16 @@  (define_insn "*bitbranch<mode>4"
   "@
    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))
    ]
@@ -1199,14 +1452,16 @@  (define_insn "*bitbranch<mode>4"
   "@
    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))
    ]
@@ -1214,14 +1469,16 @@  (define_insn "*bitbranch<mode>4"
   "@
   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))
    ]
@@ -1229,7 +1486,9 @@  (define_insn "*bitbranch<mode>4"
   "@
   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
@@ -1240,7 +1499,7 @@  (define_insn "*bitbranch<mode>4_z"
 				    (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))
    ]
@@ -1248,7 +1507,9 @@  (define_insn "*bitbranch<mode>4_z"
   "@
    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
@@ -1256,13 +1517,15 @@  (define_insn "*bitbranch<mode>4_z"
 				   (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
@@ -1270,13 +1533,15 @@  (define_insn "*bitbranch<mode>4_z"
 				   (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
@@ -1284,13 +1549,15 @@  (define_insn "*bitbranch<mode>4_z"
 				   (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
@@ -1299,31 +1566,36 @@  (define_insn "nop"
   [(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.
@@ -1331,7 +1603,9 @@  (define_insn "bic_SR"
   [(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.
@@ -1339,37 +1613,40 @@  (define_insn "bis_SR"
   [(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 "register_operand"  "0,r"))
+		(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.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")]
 		    UNS_DELAY_END)]
   ""
   "; End %J0 cycle delay"
-  )
+  [(set_attr "length" "0")]
+)
 
 (define_insn "delay_cycles_32"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
@@ -1387,7 +1664,8 @@  (define_insn "delay_cycles_32"
 	JNE	1b
 	POP	r14
 	POP	r13"
-  )
+  [(set_attr "length" "32")]
+)
 
 (define_insn "delay_cycles_32x"
   [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
@@ -1403,7 +1681,8 @@  (define_insn "delay_cycles_32x"
 	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")
@@ -1415,7 +1694,8 @@  (define_insn "delay_cycles_16"
 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")
@@ -1427,19 +1707,22 @@  (define_insn "delay_cycles_16x"
 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
@@ -1494,6 +1777,7 @@  (define_insn "*mulhisi3_inline"
     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"
@@ -1507,6 +1791,7 @@  (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"
@@ -1520,6 +1805,7 @@  (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"
@@ -1533,4 +1819,5 @@  (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")]
 )
diff --git a/gcc/config/msp430/predicates.md b/gcc/config/msp430/predicates.md
index 4bfa0c0f2d5..eb1f61df780 100644
--- a/gcc/config/msp430/predicates.md
+++ b/gcc/config/msp430/predicates.md
@@ -131,3 +131,16 @@  (define_predicate "const_1_to_19_operand"
 (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)"))