public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
@ 2020-07-23 15:43 Jozef Lawrynowicz
  2020-07-23 15:47 ` [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST Jozef Lawrynowicz
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:43 UTC (permalink / raw)
  To: gcc-patches

The following series of patches for MSP430 implement some of the target
macros used to determine the relative costs of operations.

To give an indication of the overall effect of these changes on
codesize, below are some size statistics collected from all the
executable files from execute.exp that are built at -Os.
There are around 1470 such tests (depending on the configuration).

The percentage change (((new - old)/old) * 100) in text size is calculated
for each test and the given metric is applied to that overall set of data.

Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
-----------------------------------------------------------------------------
-mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
-mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
-mlarge       |  -1.7    |   -1.9     |      1412         |      37

Successfully regtested on trunk for msp430-elf, ok to apply?

Jozef Lawrynowicz (5):
  MSP430: Implement TARGET_MEMORY_MOVE_COST
  MSP430: Implement TARGET_RTX_COSTS
  MSP430: Add defaulting to the insn length attribute
  MSP430: Implement TARGET_INSN_COST
  MSP430: Skip index-1.c test

 gcc/config/msp430/msp430-protos.h             |   5 +-
 gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
 gcc/config/msp430/msp430.h                    |  13 +
 gcc/config/msp430/msp430.md                   | 439 +++++++--
 gcc/config/msp430/msp430.opt                  |   4 +
 gcc/config/msp430/predicates.md               |  13 +
 gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
 7 files changed, 1206 insertions(+), 137 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
@ 2020-07-23 15:47 ` Jozef Lawrynowicz
  2020-07-23 15:49 ` [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS Jozef Lawrynowicz
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:47 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 167 bytes --]

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

[-- Attachment #2: 0001-MSP430-Implement-TARGET_MEMORY_MOVE_COST.patch --]
[-- Type: text/plain, Size: 5197 bytes --]

From c801a2851d47601218578c411854de9540486335 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:11 +0100
Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

gcc/ChangeLog:

	* config/msp430/msp430.c (struct single_op_cost): New struct.
	(struct double_op_cost): Likewise.
	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
	(TARGET_MEMORY_MOVE_COST): Define to...
	(msp430_memory_move_cost): New function.
	(BRANCH_COST): Don't define but add comment.
---
 gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index c2b24974364..9e739233fa0 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
 }
 
 \f
+/* Describing Relative Costs of Operations
+   To model the cost of an instruction, use the number of cycles when
+   optimizing for speed, and the number of words when optimizing for size.
+   The cheapest instruction will execute in one cycle and cost one word.
+   The cycle and size costs correspond to 430 ISA instructions, not 430X
+   instructions or 430X "address" instructions.  The relative costs of 430X
+   instructions is accurately modeled with the 430 costs.  The relative costs
+   of some "address" instructions can differ, but these are not yet handled.
+   Adding support for this could improve performance/code size.  */
+
+const int debug_rtx_costs = 0;
+
+struct single_op_cost
+{
+  const int reg;
+  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
+  const int ind;
+  const int mem;
+};
+
+static const struct single_op_cost cycle_cost_single_op =
+{
+  1, 3, 4
+};
+
+static const struct single_op_cost size_cost_single_op =
+{
+  1, 1, 2
+};
+
+/* When the destination of an insn is memory, the cost is always the same
+   regardless of whether that memory is accessed using indirect register,
+   indexed or absolute addressing.
+   When the source operand is memory, indirect register and post-increment have
+   the same cost, which is lower than indexed and absolute, which also have
+   the same cost.  */
+struct double_op_cost
+{
+  /* Source operand is a register.  */
+  const int r2r;
+  const int r2pc;
+  const int r2m;
+
+  /* Source operand is memory, using indirect register (@Rn) or indirect
+     autoincrement (@Rn+) addressing modes.  */
+  const int ind2r;
+  const int ind2pc;
+  const int ind2m;
+
+  /* Source operand is an immediate.  */
+  const int imm2r;
+  const int imm2pc;
+  const int imm2m;
+
+  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
+     addressing modes.  */
+  const int mem2r;
+  const int mem2pc;
+  const int mem2m;
+};
+
+/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
+   of clock cycles or words.  */
+static const struct double_op_cost cycle_cost_double_op_mov =
+{
+  1, 3, 3,
+  2, 4, 4,
+  2, 3, 4,
+  3, 5, 5
+};
+
+/* Cycle count when memory is the destination operand is one larger than above
+   for instructions that aren't MOV, BIT or CMP.  */
+static const struct double_op_cost cycle_cost_double_op =
+{
+  1, 3, 4,
+  2, 4, 5,
+  2, 3, 5,
+  3, 5, 6
+};
+
+static const struct double_op_cost size_cost_double_op =
+{
+  1, 1, 2,
+  1, 1, 2,
+  2, 2, 3,
+  2, 2, 3
+};
+
+/* TARGET_REGISTER_MOVE_COST
+   There is only one class of general-purpose, non-fixed registers, and the
+   relative cost of moving data between them is always the same.
+   Therefore, the default of 2 is optimal.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
+
+/* Return the cost of moving data between registers and memory.
+   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
+   of 2.
+   IN is false if the value is to be written to memory.  */
+static int
+msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+			 reg_class_t rclass ATTRIBUTE_UNUSED,
+			 bool in)
+{
+  int cost;
+  const struct double_op_cost *cost_p;
+  /* Optimize with a code size focus by default, unless -O2 or above is
+     specified.  */
+  bool speed = (!optimize_size && optimize >= 2);
+
+  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
+
+  if (in)
+    /* Reading from memory using indirect addressing is assumed to be the more
+       common case.  */
+    cost = cost_p->ind2r;
+  else
+    cost = cost_p->r2m;
+
+  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
+     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
+  return 2 * cost;
+}
+
+/* BRANCH_COST
+   Changing from the default of 1 doesn't affect code generation, presumably
+   because there are no conditional move insns - when a condition is involved,
+   the only option is to use a cbranch.  */
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
  2020-07-23 15:47 ` [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST Jozef Lawrynowicz
@ 2020-07-23 15:49 ` Jozef Lawrynowicz
  2020-07-23 15:54 ` [PATCH 3/5] MSP430: Add defaulting to the insn length attribute Jozef Lawrynowicz
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:49 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 402 bytes --]

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.


[-- Attachment #2: 0002-MSP430-Implement-TARGET_RTX_COSTS.patch --]
[-- Type: text/plain, Size: 19795 bytes --]

From f5cbd8967d9c64a4ea6eb9fb8846b4361e16e396 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:59 +0100
Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.

gcc/ChangeLog:

	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
	declaration.
	Remove unused argument.
	(struct msp430_multlib_costs): New struct.
	(msp430_is_mem_indirect): New function.
	(msp430_costs): Likewise.
	(msp430_shift_costs): Likewise.
	(msp430_muldiv_costs): Likewise.
	(msp430_get_inner_dest_code): Likewise.
	(msp430_single_op_cost): Likewise.
	(msp430_rtx_costs): Rewrite from scratch.
	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
---
 gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 530 insertions(+), 15 deletions(-)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 9e739233fa0..81ee5075a57 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -49,6 +49,9 @@
 #include "msp430-devices.h"
 #include "incpath.h"
 #include "prefix.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -56,6 +59,7 @@
 
 static void msp430_compute_frame_info (void);
 static bool use_32bit_hwmult (void);
+static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
 
 \f
 
@@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
   2, 2, 3
 };
 
+struct msp430_multlib_costs
+{
+  const int mulhi;
+  const int mulsi;
+  const int muldi;
+};
+
+/* There is no precise size cost when using libcalls, instead it is disparaged
+   relative to other instructions.
+   The cycle costs are from the CALL to the RET, inclusive.
+   FIXME muldi cost is not accurate.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
+{
+  27, 33, 66
+};
+
+/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
+{
+  27, 42, 66
+};
+
 /* TARGET_REGISTER_MOVE_COST
    There is only one class of general-purpose, non-fixed registers, and the
    relative cost of moving data between them is always the same.
@@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
    because there are no conditional move insns - when a condition is involved,
    the only option is to use a cbranch.  */
 
+/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
+   reference, @Rn or @Rn+.  */
+static bool
+msp430_is_mem_indirect (rtx x)
+{
+  gcc_assert (GET_CODE (x) == MEM);
+  rtx op0 = XEXP (x, 0);
+  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
+}
+
+/* Costs of MSP430 instructions are generally based on the addressing mode
+   combination of the source and destination operands.
+   Given source operand SRC (which may be NULL to indicate a single-operand
+   instruction) and destination operand DST return the cost of this
+   expression.  */
+static int
+msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  machine_mode outer_mode = GET_MODE (outer_rtx);
+  const struct double_op_cost *cost_p;
+  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  if (outer_code == TRUNCATE
+      && (outer_mode == QImode
+	  || outer_mode == HImode
+	  || outer_mode == PSImode))
+    /* Truncation to these modes is normally free as a side effect of the
+       instructions themselves.  */
+    return 0;
+
+  if (dst_code == SYMBOL_REF
+      || dst_code == LABEL_REF
+      || dst_code == CONST_INT)
+    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
+    return 0;
+
+  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
+    {
+      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+      fprintf (stderr, "from:\n");
+      debug_rtx (outer_rtx);
+    }
+
+  switch (src_code)
+    {
+    case REG:
+      return (dst_code == REG ? cost_p->r2r
+	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
+
+    case CONST_INT:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      return (dst_code == REG ? cost_p->imm2r
+	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
+
+
+    case MEM:
+      if (msp430_is_mem_indirect (src))
+	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
+						   ? cost_p->ind2pc
+						   : cost_p->ind2m));
+      else
+	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
+						   ? cost_p->mem2pc
+						   : cost_p->mem2m));
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
+	  debug_rtx (src);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem2m;
+    }
+}
+
+/* Given source operand SRC and destination operand DST from the shift or
+   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  int amt;
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+
+  if (debug_rtx_costs
+      && dst_code != REG
+      && dst_code != MEM
+      && dst_code != CONST
+      && dst_code != SYMBOL_REF
+      && dst_code != CONST_INT)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+    }
+
+  if (debug_rtx_costs
+      && src_code != CONST_INT
+      && src_code != REG)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
+      debug_rtx (src);
+    }
+
+  if (src_code != CONST_INT)
+    /* The size or speed cost when the shift amount is unknown cannot be
+       accurately calculated, so just disparage it slightly.  */
+    return 2 * msp430_costs (src, dst, speed, outer_rtx);
+
+  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
+    {
+      /* GCC sometimes tries to perform shifts in some very inventive ways,
+	 resulting in much larger code size usage than necessary, if
+	 they are disparaged too much here.  So in general, if
+	 use_helper_for_const_shift thinks a helper should be used, obey
+	 that and don't disparage the shift any more than a regular
+	 instruction, even though the shift may actually cost more.
+	 This ensures that the RTL generated at the initial expand pass has the
+	 expected shift instructions, which can be mapped to the helper
+	 functions.  */
+      return msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  if (!msp430x)
+    {
+      /* Each shift by one place will be emitted individually.  */
+      switch (dst_code)
+	{
+	case REG:
+	case CONST_INT:
+	  return amt * cost_p->reg;
+	case MEM:
+	  if (msp430_is_mem_indirect (dst))
+	    return amt * cost_p->ind;
+	  else
+	    return amt * cost_p->mem;
+	default:
+	  return amt * cost_p->mem;
+	}
+    }
+
+  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
+     versions are used.
+     Instructions which shift a MEM operand will never actually be output.  It
+     will always be copied into a register to allow for efficient shifting.  So
+     the cost just takes into account the cost of an additional copy in that
+     case.  */
+  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
+	  + (dst_code == REG ? 0
+	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
+}
+
+/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
+   RTX OUTER_RTX, return the cost of performing that operation, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
+		     machine_mode outer_mode)
+{
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  const struct msp430_multlib_costs *cost_p;
+  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
+						 || use_32bit_hwmult ()));
+  cost_p = (hwmult_16bit
+	    ? &cycle_cost_multlib_32bit
+	    : &cycle_cost_multlib_16bit);
+
+  int factor = 1;
+  /* Only used in some calculations.  */
+  int mode_factor = 1;
+  if (outer_mode == SImode)
+    mode_factor = 2;
+  else if (outer_mode == PSImode)
+    /* PSImode multiplication is performed using SImode operands, so has extra
+       cost to factor in the conversions necessary before/after the
+       operation.  */
+    mode_factor = 3;
+  else if (outer_mode == DImode)
+    mode_factor = 4;
+
+  if (!speed)
+    {
+      /* The codesize cost of using a helper function to perform the
+	 multiplication or division cannot be accurately calculated, since the
+	 cost depends on how many times the operation is performed in the
+	 entire program.  */
+      if (outer_code != MULT)
+	/* Division is always expensive.  */
+	factor = 7;
+      else if (((hwmult_16bit && outer_mode != DImode)
+		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
+	/* When the hardware multiplier is available, only disparage
+	   slightly.  */
+	factor = 2;
+      else
+	factor = 5;
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  /* When there is hardware multiply support, there is a relatively low, fixed
+     cycle cost to performing any multiplication, but when there is no hardware
+     multiply support it is very costly.  That precise cycle cost has not been
+     calculated here.
+     Division is extra slow since it always uses a software library.
+     The 16-bit hardware multiply library cannot be used to produce 64-bit
+     results.  */
+  if (outer_code != MULT || !msp430_has_hwmult ()
+      || (outer_mode == DImode && hwmult_16bit))
+    {
+      factor = (outer_code == MULT ? 50 : 70);
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  switch (outer_mode)
+    {
+    case E_QImode:
+    case E_HImode:
+      /* Include the cost of copying the operands into and out of the hardware
+	 multiply routine.  */
+      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
+
+    case E_PSImode:
+      /* Extra factor for the conversions necessary to do PSI->SI before the
+	 operation.  */
+      factor = 2;
+      /* fallthru.  */
+    case E_SImode:
+      return factor * (cost_p->mulsi
+		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
+
+    case E_DImode:
+    default:
+      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
+    }
+}
+
+/* Recurse within X to find the actual destination operand of the expression.
+   For example:
+   (plus (ashift (minus (ashift (reg)
+   (const_int) ......
+   should return the reg RTX.  */
+static rtx
+msp430_get_inner_dest_code (rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx op0 = XEXP (x, 0);
+  switch (code)
+    {
+    case REG:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case LABEL_REF:
+      return x;
+
+    case MEM:
+      /* Return the MEM expr not the inner REG for these cases.  */
+      switch (GET_CODE (op0))
+	{
+	case REG:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	case CONST:
+	case POST_INC:
+	  return x;
+
+	case PLUS:
+	  /* return MEM (PLUS (REG) (CONST)) */
+	  if (GET_CODE (XEXP (op0, 0)) == REG)
+	    {
+	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+		  || GET_CODE (XEXP (op0, 1)) == CONST
+		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
+		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
+		return x;
+	      else
+		return msp430_get_inner_dest_code (op0);
+	    }
+	  return msp430_get_inner_dest_code (op0);
+
+	default:
+	  if (GET_RTX_FORMAT (code)[0] != 'e')
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+      break;
+
+    default:
+      if (op0 == NULL_RTX)
+	gcc_unreachable ();
+      else
+	{
+	  if (GET_RTX_FORMAT (code)[0] != 'e'
+	      && code != ENTRY_VALUE)
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+    }
+}
+
+/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
+   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
+static int
+msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+  const struct double_op_cost *double_op_cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  switch (dst_code)
+    {
+    case REG:
+      return cost_p->reg;
+    case MEM:
+      if (msp430_is_mem_indirect (dst))
+	return cost_p->ind;
+      else
+	return cost_p->mem;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+      /* A constant value would need to be copied into a register first.  */
+      return double_op_cost_p->imm2r + cost_p->reg;
+
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_single_op_cost unhandled "
+		   "dst operand:\n");
+	  debug_rtx (dst);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem;
+    }
+}
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
-			      machine_mode mode,
-			      int	   outer_code ATTRIBUTE_UNUSED,
-			      int	   opno ATTRIBUTE_UNUSED,
-			      int *	   total,
-			      bool	   speed ATTRIBUTE_UNUSED)
+/* This target hook describes the relative costs of RTL expressions.
+   The function recurses to just before the lowest level of the expression,
+   when both of the operands of the expression can be examined at the same time.
+   This is because the cost of the expression depends on the specific
+   addressing mode combination of the operands.
+   The hook returns true when all subexpressions of X have been processed, and
+   false when rtx_cost should recurse.  */
+static bool
+msp430_rtx_costs (rtx x,
+		  machine_mode mode,
+		  int	   outer_code ATTRIBUTE_UNUSED,
+		  int	   opno ATTRIBUTE_UNUSED,
+		  int *	   total,
+		  bool	   speed)
 {
-  int code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+  rtx dst, src;
+  rtx dst_inner, src_inner;
+
+  *total = 0;
+  dst = XEXP (x, 0);
+  if (GET_RTX_LENGTH (code) == 1)
+    /* Some RTX that are single-op in GCC are double-op when translated to
+       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
+    src = dst;
+  else
+    src = XEXP (x, 1);
+
 
   switch (code)
     {
+    case SET:
+      /* Ignoring SET improves codesize.  */
+      if (!speed)
+	return true;
+      /* fallthru.  */
+    case PLUS:
+      if (outer_code == MEM)
+	/* Do not add any cost for the plus itself, but recurse in case there
+	   are more complicated RTX inside.  */
+	return false;
+      /* fallthru.  */
+    case MINUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case NOT:
+    case ZERO_EXTEND:
+    case TRUNCATE:
+    case NEG:
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case IF_THEN_ELSE:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case ROTATE:
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
+						  speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case MULT:
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
+						   x, mode));
+      return false;
+
+    case CALL:
     case SIGN_EXTEND:
-      if (mode == SImode && outer_code == SET)
+      dst_inner = msp430_get_inner_dest_code (dst);
+      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
+    case REG:
+    case PC:
+    case POST_INC:
+      if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      return true;
+
+    case MEM:
+      /* PSImode operands are expensive when in memory.  */
+      if (mode == PSImode)
+	*total = COSTS_N_INSNS (1);
+      else if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      /* Recurse into the MEM.  */
+      return false;
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+      /* Conditions are mostly equivalent, changing their relative
+	 costs has no effect.  */
+      return false;
+
+    case ASM_OPERANDS:
+    case ASM_INPUT:
+    case CLOBBER:
+    case COMPARE:
+    case CONCAT:
+    case ENTRY_VALUE:
+      /* Other unhandled expressions.  */
+      return false;
+
+    default:
+      if (debug_rtx_costs)
 	{
-	  *total = COSTS_N_INSNS (4);
-	  return true;
+	  fprintf (stderr, "\nUnhandled RTX\n");
+	  debug_rtx (x);
 	}
-      break;
+      return false;
     }
-  return false;
 }
 \f
 /* Function Entry and Exit */
@@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
 /* Return TRUE if the helper function should be used and FALSE if the shifts
    insns should be emitted inline.  */
 static bool
-use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
-			    HOST_WIDE_INT amt)
+use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
 {
   const int default_inline_shift = 4;
   /* We initialize the option to 65 so we know if the user set it or not.  */
@@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
      the heuristic accordingly.  */
   int max_inline_32 = max_inline / 2;
 
+  if (mode == E_DImode)
+    return true;
+
   /* Don't use helpers for these modes on 430X, when optimizing for speed, or
      when emitting a small number of insns.  */
   if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
@@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
      constant.  */
   if (!CONST_INT_P (operands[2])
       || mode == E_DImode
-      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
+      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
     {
       const char *helper_name = NULL;
       /* The const variants of mspabi shifts have significantly larger code
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 3/5] MSP430: Add defaulting to the insn length attribute
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
  2020-07-23 15:47 ` [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST Jozef Lawrynowicz
  2020-07-23 15:49 ` [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS Jozef Lawrynowicz
@ 2020-07-23 15:54 ` Jozef Lawrynowicz
  2020-07-23 15:56 ` [PATCH 4/5] MSP430: Implement TARGET_INSN_COST Jozef Lawrynowicz
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:54 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 445 bytes --]

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.


[-- Attachment #2: 0003-MSP430-Add-defaulting-to-the-insn-length-attribute.patch --]
[-- Type: text/plain, Size: 47996 bytes --]

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(-)

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)"))
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 4/5] MSP430: Implement TARGET_INSN_COST
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
                   ` (2 preceding siblings ...)
  2020-07-23 15:54 ` [PATCH 3/5] MSP430: Add defaulting to the insn length attribute Jozef Lawrynowicz
@ 2020-07-23 15:56 ` Jozef Lawrynowicz
  2020-07-23 18:34   ` Segher Boessenkool
  2020-07-23 15:57 ` [PATCH 5/5] MSP430: Skip index-1.c test Jozef Lawrynowicz
  2020-08-07 11:02 ` ping [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
  5 siblings, 1 reply; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:56 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 209 bytes --]

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.


[-- Attachment #2: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7265 bytes --]

From e4c5f9c3f567489f89b41a0d96e321acb5d18152 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.opt: Add -mdebug-insn-costs option.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 29 +++++++++++++
 gcc/config/msp430/msp430.opt                  |  4 ++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 194 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..7ef651fb324 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1711,6 +1711,35 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  int cost;
+
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  cost = get_attr_length (insn);
+  if (TARGET_DEBUG_INSN_COSTS)
+    {
+      fprintf (stderr, "cost %d for insn:\n", cost);
+      debug_rtx (insn);
+    }
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (cost) / (2 * COSTS_N_INSNS (1));
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.opt b/gcc/config/msp430/msp430.opt
index 8134ca7ac95..448c41aa81c 100644
--- a/gcc/config/msp430/msp430.opt
+++ b/gcc/config/msp430/msp430.opt
@@ -115,3 +115,7 @@ Target RejectNegative Joined UInteger IntegerRange(0,65) Var(msp430_max_inline_s
 For shift operations by a constant amount, which require an individual instruction to shift by one
 position, set the maximum number of inline shift instructions (maximum value 64) to emit instead of using the corresponding __mspabi helper function.
 The default value is 4.
+
+mdebug-insn-costs
+Target Report Mask(DEBUG_INSN_COSTS)
+Print insns and their costs as calculated by TARGET_INSN_COSTS.
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 5/5] MSP430: Skip index-1.c test
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
                   ` (3 preceding siblings ...)
  2020-07-23 15:56 ` [PATCH 4/5] MSP430: Implement TARGET_INSN_COST Jozef Lawrynowicz
@ 2020-07-23 15:57 ` Jozef Lawrynowicz
  2020-08-07 11:02 ` ping [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
  5 siblings, 0 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-23 15:57 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 521 bytes --]

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.


[-- Attachment #2: 0005-MSP430-Skip-index-1.c-test.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

From bfafc633013952c1a5cac2dbb10b774f66be920e Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:35:25 +0100
Subject: [PATCH 5/5] MSP430: Skip index-1.c test

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
---
 gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
index b00090d834a..d96be4c77b8 100644
--- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
@@ -1,3 +1,5 @@
+/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
+
 int a[] =
 {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST
  2020-07-23 15:56 ` [PATCH 4/5] MSP430: Implement TARGET_INSN_COST Jozef Lawrynowicz
@ 2020-07-23 18:34   ` Segher Boessenkool
  2020-07-24 11:50     ` Jozef Lawrynowicz
  0 siblings, 1 reply; 16+ messages in thread
From: Segher Boessenkool @ 2020-07-23 18:34 UTC (permalink / raw)
  To: gcc-patches

Hi!

On Thu, Jul 23, 2020 at 04:56:14PM +0100, Jozef Lawrynowicz wrote:
> +static int
> +msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
> +{
> +  int cost;
> +
> +  if (recog_memoized (insn) < 0)
> +    return 0;
> +
> +  cost = get_attr_length (insn);
> +  if (TARGET_DEBUG_INSN_COSTS)
> +    {
> +      fprintf (stderr, "cost %d for insn:\n", cost);
> +      debug_rtx (insn);
> +    }
> +
> +  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
> +     length of 2 bytes is the smallest possible size and so must be equivalent
> +     to COSTS_N_INSNS (1).  */
> +  return COSTS_N_INSNS (cost) / (2 * COSTS_N_INSNS (1));

This is the same as "cost / 2", so "length / 2" here, which doesn't look
right.  The returned value should have the same "unit" as COSTS_N_INSNS
does, so maybe you want  COSTS_N_INSNS (length / 2)  ?

> +  /* FIXME Add more detailed costs when optimizing for speed.
> +     For now the length of the instruction is a good approximiation and roughly
> +     correlates with cycle cost.  *

COSTS_N_INSNS (1) is 4, so that you can make things cost 5, 6, 7 to be a
cost intermediate to COSTS_N_INSNS (1) and COSTS_N_INSNS (2).  This is
very useful, scaling down the costs destroys that.

> +mdebug-insn-costs
> +Target Report Mask(DEBUG_INSN_COSTS)
> +Print insns and their costs as calculated by TARGET_INSN_COSTS.

It is already printed in the generated asm with -dp?  Not sure if you
want more detail than that.

     '-dp'
          Annotate the assembler output with a comment indicating which
          pattern and alternative is used.  The length and cost of each
          instruction are also printed.


Segher

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST
  2020-07-23 18:34   ` Segher Boessenkool
@ 2020-07-24 11:50     ` Jozef Lawrynowicz
  2020-07-24 12:25       ` Segher Boessenkool
  0 siblings, 1 reply; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-07-24 11:50 UTC (permalink / raw)
  To: Segher Boessenkool; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2948 bytes --]

Hi Segher,

Thanks for having a look at the patch.

On Thu, Jul 23, 2020 at 01:34:22PM -0500, Segher Boessenkool wrote:
> Hi!
> 
> On Thu, Jul 23, 2020 at 04:56:14PM +0100, Jozef Lawrynowicz wrote:
> > +static int
> > +msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
> > +{
> > +  int cost;
> > +
> > +  if (recog_memoized (insn) < 0)
> > +    return 0;
> > +
> > +  cost = get_attr_length (insn);
> > +  if (TARGET_DEBUG_INSN_COSTS)
> > +    {
> > +      fprintf (stderr, "cost %d for insn:\n", cost);
> > +      debug_rtx (insn);
> > +    }
> > +
> > +  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
> > +     length of 2 bytes is the smallest possible size and so must be equivalent
> > +     to COSTS_N_INSNS (1).  */
> > +  return COSTS_N_INSNS (cost) / (2 * COSTS_N_INSNS (1));
> 
> This is the same as "cost / 2", so "length / 2" here, which doesn't look
> right.  The returned value should have the same "unit" as COSTS_N_INSNS
> does, so maybe you want  COSTS_N_INSNS (length / 2)  ?

Indeed it looks like I made a thinko in that calculation in TARGET_INSN_COSTS;
trying to make it verbose to show the thought behind the calculation backfired
:)

Fixing it to return "COSTS_N_INSNS (length / 2)" actually made codesize
noticeably worse for most of my benchmarks.
I had to define BRANCH_COST to indicate branches are not cheap.

In the original patch a cheap instruction would have a cost of 1.
When using the default BRANCH_COST of 1 to calculate the cost of a branch
compared to an insn (e.g. in ifcvt.c), BRANCH_COST would be wrapped in
COSTS_N_INSNS, scaling the cost to 4, which suitably disparaged
it vs the cheap insn cost of 1.

With the fixed insn_cost calculation, a cheap instruction would cost 4
with the COSTS_N_INSNS scaling, and a branch would cost the same, which is not
right.

Fixed in the attached patch.

> 
> > +  /* FIXME Add more detailed costs when optimizing for speed.
> > +     For now the length of the instruction is a good approximiation and roughly
> > +     correlates with cycle cost.  *
> 
> COSTS_N_INSNS (1) is 4, so that you can make things cost 5, 6, 7 to be a
> cost intermediate to COSTS_N_INSNS (1) and COSTS_N_INSNS (2).  This is
> very useful, scaling down the costs destroys that.
> 
> > +mdebug-insn-costs
> > +Target Report Mask(DEBUG_INSN_COSTS)
> > +Print insns and their costs as calculated by TARGET_INSN_COSTS.
> 
> It is already printed in the generated asm with -dp?  Not sure if you
> want more detail than that.
> 
>      '-dp'
>           Annotate the assembler output with a comment indicating which
>           pattern and alternative is used.  The length and cost of each
>           instruction are also printed.
> 

During development I found it useful to see the insns in RTL format and their
costs alongside that.  In hindsight, it doesn't really have much use in the
finalized patch, so I've removed it.

Thanks!
Jozef

> 
> Segher

[-- Attachment #2: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7738 bytes --]

From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.opt: Add -mdebug-insn-costs option.
	* config/msp430/msp430.h (BRANCH_COST): Define.
	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 25 ++++++++---
 gcc/config/msp430/msp430.h                    |  8 ++++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 189 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..35ccd2817ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
   return 2 * cost;
 }
 
-/* BRANCH_COST
-   Changing from the default of 1 doesn't affect code generation, presumably
-   because there are no conditional move insns - when a condition is involved,
-   the only option is to use a cbranch.  */
-
 /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
    reference, @Rn or @Rn+.  */
 static bool
@@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (get_attr_length (insn) / 2);
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b813e825311..830101408a5 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
 #define HAS_LONG_COND_BRANCH		0
 #define HAS_LONG_UNCOND_BRANCH		0
 
+/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
+#define BRANCH_COST(speed_p, predictable_p) 3
+
+/* Override the default BRANCH_COST heuristic to indicate that it is preferable
+   to retain short-circuit operations, this results in significantly better
+   codesize and performance.  */
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
 #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
 #define WORD_REGISTER_OPERATIONS	1
 
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST
  2020-07-24 11:50     ` Jozef Lawrynowicz
@ 2020-07-24 12:25       ` Segher Boessenkool
  0 siblings, 0 replies; 16+ messages in thread
From: Segher Boessenkool @ 2020-07-24 12:25 UTC (permalink / raw)
  To: gcc-patches

Hi Jozef,

On Fri, Jul 24, 2020 at 12:50:48PM +0100, Jozef Lawrynowicz wrote:
> On Thu, Jul 23, 2020 at 01:34:22PM -0500, Segher Boessenkool wrote:
> > On Thu, Jul 23, 2020 at 04:56:14PM +0100, Jozef Lawrynowicz wrote:
> > > +  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
> > > +     length of 2 bytes is the smallest possible size and so must be equivalent
> > > +     to COSTS_N_INSNS (1).  */
> > > +  return COSTS_N_INSNS (cost) / (2 * COSTS_N_INSNS (1));
> > 
> > This is the same as "cost / 2", so "length / 2" here, which doesn't look
> > right.  The returned value should have the same "unit" as COSTS_N_INSNS
> > does, so maybe you want  COSTS_N_INSNS (length / 2)  ?
> 
> Indeed it looks like I made a thinko in that calculation in TARGET_INSN_COSTS;
> trying to make it verbose to show the thought behind the calculation backfired
> :)
> 
> Fixing it to return "COSTS_N_INSNS (length / 2)" actually made codesize
> noticeably worse for most of my benchmarks.
> I had to define BRANCH_COST to indicate branches are not cheap.
> 
> In the original patch a cheap instruction would have a cost of 1.
> When using the default BRANCH_COST of 1 to calculate the cost of a branch
> compared to an insn (e.g. in ifcvt.c), BRANCH_COST would be wrapped in
> COSTS_N_INSNS, scaling the cost to 4, which suitably disparaged
> it vs the cheap insn cost of 1.
> 
> With the fixed insn_cost calculation, a cheap instruction would cost 4
> with the COSTS_N_INSNS scaling, and a branch would cost the same, which is not
> right.

There isn't much you can do to battle the "default" cost of 4 -- this is
pervasive throughout the compiler -- so it is much easier to go with the
flow.

> > It is already printed in the generated asm with -dp?  Not sure if you
> > want more detail than that.
> > 
> >      '-dp'
> >           Annotate the assembler output with a comment indicating which
> >           pattern and alternative is used.  The length and cost of each
> >           instruction are also printed.
> > 
> 
> During development I found it useful to see the insns in RTL format and their
> costs alongside that.  In hindsight, it doesn't really have much use in the
> finalized patch, so I've removed it.

There is -dP for that (capital P) :-)  It isn't very pretty, not sure
how that could be improved?

> +/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
> +#define BRANCH_COST(speed_p, predictable_p) 3
> +
> +/* Override the default BRANCH_COST heuristic to indicate that it is preferable
> +   to retain short-circuit operations, this results in significantly better
> +   codesize and performance.  */
> +#define LOGICAL_OP_NON_SHORT_CIRCUIT 0

That looks just fine :-)


Segher

^ permalink raw reply	[flat|nested] 16+ messages in thread

* ping [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
                   ` (4 preceding siblings ...)
  2020-07-23 15:57 ` [PATCH 5/5] MSP430: Skip index-1.c test Jozef Lawrynowicz
@ 2020-08-07 11:02 ` Jozef Lawrynowicz
  2020-09-15 20:30   ` ping x2 " Jozef Lawrynowicz
  5 siblings, 1 reply; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-08-07 11:02 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2170 bytes --]

Pinging for this series of patches.
Attached all patches to this mail with the ammended patch 4 thanks to
Segher's review.

I forgot to mention originally that these patches require the previously
submitted "simplify and extend shift instruction patterns" patch to be
first applied.
https://gcc.gnu.org/pipermail/gcc-patches/2020-July/550396.html

Thanks,
Jozef

On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
> The following series of patches for MSP430 implement some of the target
> macros used to determine the relative costs of operations.
> 
> To give an indication of the overall effect of these changes on
> codesize, below are some size statistics collected from all the
> executable files from execute.exp that are built at -Os.
> There are around 1470 such tests (depending on the configuration).
> 
> The percentage change (((new - old)/old) * 100) in text size is calculated
> for each test and the given metric is applied to that overall set of data.
> 
> Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
> -----------------------------------------------------------------------------
> -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
> -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
> -mlarge       |  -1.7    |   -1.9     |      1412         |      37
> 
> Successfully regtested on trunk for msp430-elf, ok to apply?
> 
> Jozef Lawrynowicz (5):
>   MSP430: Implement TARGET_MEMORY_MOVE_COST
>   MSP430: Implement TARGET_RTX_COSTS
>   MSP430: Add defaulting to the insn length attribute
>   MSP430: Implement TARGET_INSN_COST
>   MSP430: Skip index-1.c test
> 
>  gcc/config/msp430/msp430-protos.h             |   5 +-
>  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
>  gcc/config/msp430/msp430.h                    |  13 +
>  gcc/config/msp430/msp430.md                   | 439 +++++++--
>  gcc/config/msp430/msp430.opt                  |   4 +
>  gcc/config/msp430/predicates.md               |  13 +
>  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
>  7 files changed, 1206 insertions(+), 137 deletions(-)
> 
> -- 
> 2.27.0
> 

[-- Attachment #2: 0001-MSP430-Implement-TARGET_MEMORY_MOVE_COST.patch --]
[-- Type: text/plain, Size: 5197 bytes --]

From e260de5a31e661afdfaaf2c8053b574a292d6826 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:11 +0100
Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

gcc/ChangeLog:

	* config/msp430/msp430.c (struct single_op_cost): New struct.
	(struct double_op_cost): Likewise.
	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
	(TARGET_MEMORY_MOVE_COST): Define to...
	(msp430_memory_move_cost): New function.
	(BRANCH_COST): Don't define but add comment.
---
 gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index c2b24974364..9e739233fa0 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
 }
 
 \f
+/* Describing Relative Costs of Operations
+   To model the cost of an instruction, use the number of cycles when
+   optimizing for speed, and the number of words when optimizing for size.
+   The cheapest instruction will execute in one cycle and cost one word.
+   The cycle and size costs correspond to 430 ISA instructions, not 430X
+   instructions or 430X "address" instructions.  The relative costs of 430X
+   instructions is accurately modeled with the 430 costs.  The relative costs
+   of some "address" instructions can differ, but these are not yet handled.
+   Adding support for this could improve performance/code size.  */
+
+const int debug_rtx_costs = 0;
+
+struct single_op_cost
+{
+  const int reg;
+  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
+  const int ind;
+  const int mem;
+};
+
+static const struct single_op_cost cycle_cost_single_op =
+{
+  1, 3, 4
+};
+
+static const struct single_op_cost size_cost_single_op =
+{
+  1, 1, 2
+};
+
+/* When the destination of an insn is memory, the cost is always the same
+   regardless of whether that memory is accessed using indirect register,
+   indexed or absolute addressing.
+   When the source operand is memory, indirect register and post-increment have
+   the same cost, which is lower than indexed and absolute, which also have
+   the same cost.  */
+struct double_op_cost
+{
+  /* Source operand is a register.  */
+  const int r2r;
+  const int r2pc;
+  const int r2m;
+
+  /* Source operand is memory, using indirect register (@Rn) or indirect
+     autoincrement (@Rn+) addressing modes.  */
+  const int ind2r;
+  const int ind2pc;
+  const int ind2m;
+
+  /* Source operand is an immediate.  */
+  const int imm2r;
+  const int imm2pc;
+  const int imm2m;
+
+  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
+     addressing modes.  */
+  const int mem2r;
+  const int mem2pc;
+  const int mem2m;
+};
+
+/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
+   of clock cycles or words.  */
+static const struct double_op_cost cycle_cost_double_op_mov =
+{
+  1, 3, 3,
+  2, 4, 4,
+  2, 3, 4,
+  3, 5, 5
+};
+
+/* Cycle count when memory is the destination operand is one larger than above
+   for instructions that aren't MOV, BIT or CMP.  */
+static const struct double_op_cost cycle_cost_double_op =
+{
+  1, 3, 4,
+  2, 4, 5,
+  2, 3, 5,
+  3, 5, 6
+};
+
+static const struct double_op_cost size_cost_double_op =
+{
+  1, 1, 2,
+  1, 1, 2,
+  2, 2, 3,
+  2, 2, 3
+};
+
+/* TARGET_REGISTER_MOVE_COST
+   There is only one class of general-purpose, non-fixed registers, and the
+   relative cost of moving data between them is always the same.
+   Therefore, the default of 2 is optimal.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
+
+/* Return the cost of moving data between registers and memory.
+   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
+   of 2.
+   IN is false if the value is to be written to memory.  */
+static int
+msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+			 reg_class_t rclass ATTRIBUTE_UNUSED,
+			 bool in)
+{
+  int cost;
+  const struct double_op_cost *cost_p;
+  /* Optimize with a code size focus by default, unless -O2 or above is
+     specified.  */
+  bool speed = (!optimize_size && optimize >= 2);
+
+  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
+
+  if (in)
+    /* Reading from memory using indirect addressing is assumed to be the more
+       common case.  */
+    cost = cost_p->ind2r;
+  else
+    cost = cost_p->r2m;
+
+  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
+     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
+  return 2 * cost;
+}
+
+/* BRANCH_COST
+   Changing from the default of 1 doesn't affect code generation, presumably
+   because there are no conditional move insns - when a condition is involved,
+   the only option is to use a cbranch.  */
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-- 
2.27.0


[-- Attachment #3: 0002-MSP430-Implement-TARGET_RTX_COSTS.patch --]
[-- Type: text/plain, Size: 19795 bytes --]

From 4b4bc7c51bbea79abe3095d4b0cf562556419f8c Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:59 +0100
Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.

gcc/ChangeLog:

	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
	declaration.
	Remove unused argument.
	(struct msp430_multlib_costs): New struct.
	(msp430_is_mem_indirect): New function.
	(msp430_costs): Likewise.
	(msp430_shift_costs): Likewise.
	(msp430_muldiv_costs): Likewise.
	(msp430_get_inner_dest_code): Likewise.
	(msp430_single_op_cost): Likewise.
	(msp430_rtx_costs): Rewrite from scratch.
	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
---
 gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 530 insertions(+), 15 deletions(-)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 9e739233fa0..81ee5075a57 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -49,6 +49,9 @@
 #include "msp430-devices.h"
 #include "incpath.h"
 #include "prefix.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -56,6 +59,7 @@
 
 static void msp430_compute_frame_info (void);
 static bool use_32bit_hwmult (void);
+static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
 
 \f
 
@@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
   2, 2, 3
 };
 
+struct msp430_multlib_costs
+{
+  const int mulhi;
+  const int mulsi;
+  const int muldi;
+};
+
+/* There is no precise size cost when using libcalls, instead it is disparaged
+   relative to other instructions.
+   The cycle costs are from the CALL to the RET, inclusive.
+   FIXME muldi cost is not accurate.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
+{
+  27, 33, 66
+};
+
+/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
+{
+  27, 42, 66
+};
+
 /* TARGET_REGISTER_MOVE_COST
    There is only one class of general-purpose, non-fixed registers, and the
    relative cost of moving data between them is always the same.
@@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
    because there are no conditional move insns - when a condition is involved,
    the only option is to use a cbranch.  */
 
+/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
+   reference, @Rn or @Rn+.  */
+static bool
+msp430_is_mem_indirect (rtx x)
+{
+  gcc_assert (GET_CODE (x) == MEM);
+  rtx op0 = XEXP (x, 0);
+  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
+}
+
+/* Costs of MSP430 instructions are generally based on the addressing mode
+   combination of the source and destination operands.
+   Given source operand SRC (which may be NULL to indicate a single-operand
+   instruction) and destination operand DST return the cost of this
+   expression.  */
+static int
+msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  machine_mode outer_mode = GET_MODE (outer_rtx);
+  const struct double_op_cost *cost_p;
+  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  if (outer_code == TRUNCATE
+      && (outer_mode == QImode
+	  || outer_mode == HImode
+	  || outer_mode == PSImode))
+    /* Truncation to these modes is normally free as a side effect of the
+       instructions themselves.  */
+    return 0;
+
+  if (dst_code == SYMBOL_REF
+      || dst_code == LABEL_REF
+      || dst_code == CONST_INT)
+    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
+    return 0;
+
+  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
+    {
+      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+      fprintf (stderr, "from:\n");
+      debug_rtx (outer_rtx);
+    }
+
+  switch (src_code)
+    {
+    case REG:
+      return (dst_code == REG ? cost_p->r2r
+	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
+
+    case CONST_INT:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      return (dst_code == REG ? cost_p->imm2r
+	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
+
+
+    case MEM:
+      if (msp430_is_mem_indirect (src))
+	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
+						   ? cost_p->ind2pc
+						   : cost_p->ind2m));
+      else
+	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
+						   ? cost_p->mem2pc
+						   : cost_p->mem2m));
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
+	  debug_rtx (src);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem2m;
+    }
+}
+
+/* Given source operand SRC and destination operand DST from the shift or
+   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  int amt;
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+
+  if (debug_rtx_costs
+      && dst_code != REG
+      && dst_code != MEM
+      && dst_code != CONST
+      && dst_code != SYMBOL_REF
+      && dst_code != CONST_INT)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+    }
+
+  if (debug_rtx_costs
+      && src_code != CONST_INT
+      && src_code != REG)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
+      debug_rtx (src);
+    }
+
+  if (src_code != CONST_INT)
+    /* The size or speed cost when the shift amount is unknown cannot be
+       accurately calculated, so just disparage it slightly.  */
+    return 2 * msp430_costs (src, dst, speed, outer_rtx);
+
+  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
+    {
+      /* GCC sometimes tries to perform shifts in some very inventive ways,
+	 resulting in much larger code size usage than necessary, if
+	 they are disparaged too much here.  So in general, if
+	 use_helper_for_const_shift thinks a helper should be used, obey
+	 that and don't disparage the shift any more than a regular
+	 instruction, even though the shift may actually cost more.
+	 This ensures that the RTL generated at the initial expand pass has the
+	 expected shift instructions, which can be mapped to the helper
+	 functions.  */
+      return msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  if (!msp430x)
+    {
+      /* Each shift by one place will be emitted individually.  */
+      switch (dst_code)
+	{
+	case REG:
+	case CONST_INT:
+	  return amt * cost_p->reg;
+	case MEM:
+	  if (msp430_is_mem_indirect (dst))
+	    return amt * cost_p->ind;
+	  else
+	    return amt * cost_p->mem;
+	default:
+	  return amt * cost_p->mem;
+	}
+    }
+
+  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
+     versions are used.
+     Instructions which shift a MEM operand will never actually be output.  It
+     will always be copied into a register to allow for efficient shifting.  So
+     the cost just takes into account the cost of an additional copy in that
+     case.  */
+  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
+	  + (dst_code == REG ? 0
+	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
+}
+
+/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
+   RTX OUTER_RTX, return the cost of performing that operation, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
+		     machine_mode outer_mode)
+{
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  const struct msp430_multlib_costs *cost_p;
+  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
+						 || use_32bit_hwmult ()));
+  cost_p = (hwmult_16bit
+	    ? &cycle_cost_multlib_32bit
+	    : &cycle_cost_multlib_16bit);
+
+  int factor = 1;
+  /* Only used in some calculations.  */
+  int mode_factor = 1;
+  if (outer_mode == SImode)
+    mode_factor = 2;
+  else if (outer_mode == PSImode)
+    /* PSImode multiplication is performed using SImode operands, so has extra
+       cost to factor in the conversions necessary before/after the
+       operation.  */
+    mode_factor = 3;
+  else if (outer_mode == DImode)
+    mode_factor = 4;
+
+  if (!speed)
+    {
+      /* The codesize cost of using a helper function to perform the
+	 multiplication or division cannot be accurately calculated, since the
+	 cost depends on how many times the operation is performed in the
+	 entire program.  */
+      if (outer_code != MULT)
+	/* Division is always expensive.  */
+	factor = 7;
+      else if (((hwmult_16bit && outer_mode != DImode)
+		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
+	/* When the hardware multiplier is available, only disparage
+	   slightly.  */
+	factor = 2;
+      else
+	factor = 5;
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  /* When there is hardware multiply support, there is a relatively low, fixed
+     cycle cost to performing any multiplication, but when there is no hardware
+     multiply support it is very costly.  That precise cycle cost has not been
+     calculated here.
+     Division is extra slow since it always uses a software library.
+     The 16-bit hardware multiply library cannot be used to produce 64-bit
+     results.  */
+  if (outer_code != MULT || !msp430_has_hwmult ()
+      || (outer_mode == DImode && hwmult_16bit))
+    {
+      factor = (outer_code == MULT ? 50 : 70);
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  switch (outer_mode)
+    {
+    case E_QImode:
+    case E_HImode:
+      /* Include the cost of copying the operands into and out of the hardware
+	 multiply routine.  */
+      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
+
+    case E_PSImode:
+      /* Extra factor for the conversions necessary to do PSI->SI before the
+	 operation.  */
+      factor = 2;
+      /* fallthru.  */
+    case E_SImode:
+      return factor * (cost_p->mulsi
+		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
+
+    case E_DImode:
+    default:
+      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
+    }
+}
+
+/* Recurse within X to find the actual destination operand of the expression.
+   For example:
+   (plus (ashift (minus (ashift (reg)
+   (const_int) ......
+   should return the reg RTX.  */
+static rtx
+msp430_get_inner_dest_code (rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx op0 = XEXP (x, 0);
+  switch (code)
+    {
+    case REG:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case LABEL_REF:
+      return x;
+
+    case MEM:
+      /* Return the MEM expr not the inner REG for these cases.  */
+      switch (GET_CODE (op0))
+	{
+	case REG:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	case CONST:
+	case POST_INC:
+	  return x;
+
+	case PLUS:
+	  /* return MEM (PLUS (REG) (CONST)) */
+	  if (GET_CODE (XEXP (op0, 0)) == REG)
+	    {
+	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+		  || GET_CODE (XEXP (op0, 1)) == CONST
+		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
+		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
+		return x;
+	      else
+		return msp430_get_inner_dest_code (op0);
+	    }
+	  return msp430_get_inner_dest_code (op0);
+
+	default:
+	  if (GET_RTX_FORMAT (code)[0] != 'e')
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+      break;
+
+    default:
+      if (op0 == NULL_RTX)
+	gcc_unreachable ();
+      else
+	{
+	  if (GET_RTX_FORMAT (code)[0] != 'e'
+	      && code != ENTRY_VALUE)
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+    }
+}
+
+/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
+   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
+static int
+msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+  const struct double_op_cost *double_op_cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  switch (dst_code)
+    {
+    case REG:
+      return cost_p->reg;
+    case MEM:
+      if (msp430_is_mem_indirect (dst))
+	return cost_p->ind;
+      else
+	return cost_p->mem;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+      /* A constant value would need to be copied into a register first.  */
+      return double_op_cost_p->imm2r + cost_p->reg;
+
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_single_op_cost unhandled "
+		   "dst operand:\n");
+	  debug_rtx (dst);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem;
+    }
+}
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
-			      machine_mode mode,
-			      int	   outer_code ATTRIBUTE_UNUSED,
-			      int	   opno ATTRIBUTE_UNUSED,
-			      int *	   total,
-			      bool	   speed ATTRIBUTE_UNUSED)
+/* This target hook describes the relative costs of RTL expressions.
+   The function recurses to just before the lowest level of the expression,
+   when both of the operands of the expression can be examined at the same time.
+   This is because the cost of the expression depends on the specific
+   addressing mode combination of the operands.
+   The hook returns true when all subexpressions of X have been processed, and
+   false when rtx_cost should recurse.  */
+static bool
+msp430_rtx_costs (rtx x,
+		  machine_mode mode,
+		  int	   outer_code ATTRIBUTE_UNUSED,
+		  int	   opno ATTRIBUTE_UNUSED,
+		  int *	   total,
+		  bool	   speed)
 {
-  int code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+  rtx dst, src;
+  rtx dst_inner, src_inner;
+
+  *total = 0;
+  dst = XEXP (x, 0);
+  if (GET_RTX_LENGTH (code) == 1)
+    /* Some RTX that are single-op in GCC are double-op when translated to
+       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
+    src = dst;
+  else
+    src = XEXP (x, 1);
+
 
   switch (code)
     {
+    case SET:
+      /* Ignoring SET improves codesize.  */
+      if (!speed)
+	return true;
+      /* fallthru.  */
+    case PLUS:
+      if (outer_code == MEM)
+	/* Do not add any cost for the plus itself, but recurse in case there
+	   are more complicated RTX inside.  */
+	return false;
+      /* fallthru.  */
+    case MINUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case NOT:
+    case ZERO_EXTEND:
+    case TRUNCATE:
+    case NEG:
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case IF_THEN_ELSE:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case ROTATE:
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
+						  speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case MULT:
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
+						   x, mode));
+      return false;
+
+    case CALL:
     case SIGN_EXTEND:
-      if (mode == SImode && outer_code == SET)
+      dst_inner = msp430_get_inner_dest_code (dst);
+      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
+    case REG:
+    case PC:
+    case POST_INC:
+      if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      return true;
+
+    case MEM:
+      /* PSImode operands are expensive when in memory.  */
+      if (mode == PSImode)
+	*total = COSTS_N_INSNS (1);
+      else if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      /* Recurse into the MEM.  */
+      return false;
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+      /* Conditions are mostly equivalent, changing their relative
+	 costs has no effect.  */
+      return false;
+
+    case ASM_OPERANDS:
+    case ASM_INPUT:
+    case CLOBBER:
+    case COMPARE:
+    case CONCAT:
+    case ENTRY_VALUE:
+      /* Other unhandled expressions.  */
+      return false;
+
+    default:
+      if (debug_rtx_costs)
 	{
-	  *total = COSTS_N_INSNS (4);
-	  return true;
+	  fprintf (stderr, "\nUnhandled RTX\n");
+	  debug_rtx (x);
 	}
-      break;
+      return false;
     }
-  return false;
 }
 \f
 /* Function Entry and Exit */
@@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
 /* Return TRUE if the helper function should be used and FALSE if the shifts
    insns should be emitted inline.  */
 static bool
-use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
-			    HOST_WIDE_INT amt)
+use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
 {
   const int default_inline_shift = 4;
   /* We initialize the option to 65 so we know if the user set it or not.  */
@@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
      the heuristic accordingly.  */
   int max_inline_32 = max_inline / 2;
 
+  if (mode == E_DImode)
+    return true;
+
   /* Don't use helpers for these modes on 430X, when optimizing for speed, or
      when emitting a small number of insns.  */
   if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
@@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
      constant.  */
   if (!CONST_INT_P (operands[2])
       || mode == E_DImode
-      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
+      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
     {
       const char *helper_name = NULL;
       /* The const variants of mspabi shifts have significantly larger code
-- 
2.27.0


[-- Attachment #4: 0003-MSP430-Add-defaulting-to-the-insn-length-attribute.patch --]
[-- Type: text/plain, Size: 47996 bytes --]

From e8f2aacef2b740e8123b6450f2addc16d197c464 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(-)

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)"))
-- 
2.27.0


[-- Attachment #5: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7678 bytes --]

From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.h (BRANCH_COST): Define.
	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 25 ++++++++---
 gcc/config/msp430/msp430.h                    |  8 ++++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 189 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..35ccd2817ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
   return 2 * cost;
 }
 
-/* BRANCH_COST
-   Changing from the default of 1 doesn't affect code generation, presumably
-   because there are no conditional move insns - when a condition is involved,
-   the only option is to use a cbranch.  */
-
 /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
    reference, @Rn or @Rn+.  */
 static bool
@@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (get_attr_length (insn) / 2);
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b813e825311..830101408a5 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
 #define HAS_LONG_COND_BRANCH		0
 #define HAS_LONG_UNCOND_BRANCH		0
 
+/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
+#define BRANCH_COST(speed_p, predictable_p) 3
+
+/* Override the default BRANCH_COST heuristic to indicate that it is preferable
+   to retain short-circuit operations, this results in significantly better
+   codesize and performance.  */
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
 #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
 #define WORD_REGISTER_OPERATIONS	1
 
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


[-- Attachment #6: 0005-MSP430-Skip-index-1.c-test.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

From 59fa213daac1cedd6d1564dfeacb1f6ce9397e98 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:35:25 +0100
Subject: [PATCH 5/5] MSP430: Skip index-1.c test

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
---
 gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
index b00090d834a..d96be4c77b8 100644
--- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
@@ -1,3 +1,5 @@
+/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
+
 int a[] =
 {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* ping x2 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-08-07 11:02 ` ping [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
@ 2020-09-15 20:30   ` Jozef Lawrynowicz
  2020-10-14 15:31     ` ping x3 " Jozef Lawrynowicz
  2020-11-06 20:53     ` ping x2 " Jeff Law
  0 siblings, 2 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-09-15 20:30 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 89647 bytes --]

Ping x2 for below.

On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
> Pinging for this series of patches.
> Attached all patches to this mail with the ammended patch 4 thanks to
> Segher's review.
> 
> Thanks,
> Jozef
> 
> On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
> > The following series of patches for MSP430 implement some of the target
> > macros used to determine the relative costs of operations.
> > 
> > To give an indication of the overall effect of these changes on
> > codesize, below are some size statistics collected from all the
> > executable files from execute.exp that are built at -Os.
> > There are around 1470 such tests (depending on the configuration).
> > 
> > The percentage change (((new - old)/old) * 100) in text size is calculated
> > for each test and the given metric is applied to that overall set of data.
> > 
> > Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
> > -----------------------------------------------------------------------------
> > -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
> > -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
> > -mlarge       |  -1.7    |   -1.9     |      1412         |      37
> > 
> > Successfully regtested on trunk for msp430-elf, ok to apply?
> > 
> > Jozef Lawrynowicz (5):
> >   MSP430: Implement TARGET_MEMORY_MOVE_COST
> >   MSP430: Implement TARGET_RTX_COSTS
> >   MSP430: Add defaulting to the insn length attribute
> >   MSP430: Implement TARGET_INSN_COST
> >   MSP430: Skip index-1.c test
> > 
> >  gcc/config/msp430/msp430-protos.h             |   5 +-
> >  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
> >  gcc/config/msp430/msp430.h                    |  13 +
> >  gcc/config/msp430/msp430.md                   | 439 +++++++--
> >  gcc/config/msp430/msp430.opt                  |   4 +
> >  gcc/config/msp430/predicates.md               |  13 +
> >  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
> >  7 files changed, 1206 insertions(+), 137 deletions(-)
> > 
> > -- 
> > 2.27.0
> > 

> From e260de5a31e661afdfaaf2c8053b574a292d6826 Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Thu, 16 Jul 2020 11:28:11 +0100
> Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST
> 
> The cycle and size cost of a MOV instruction in different addressing
> modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
> TARGET_REGISTER_MOVE_COST.
> 
> gcc/ChangeLog:
> 
> 	* config/msp430/msp430.c (struct single_op_cost): New struct.
> 	(struct double_op_cost): Likewise.
> 	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
> 	(TARGET_MEMORY_MOVE_COST): Define to...
> 	(msp430_memory_move_cost): New function.
> 	(BRANCH_COST): Don't define but add comment.
> ---
>  gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 131 insertions(+)
> 
> diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
> index c2b24974364..9e739233fa0 100644
> --- a/gcc/config/msp430/msp430.c
> +++ b/gcc/config/msp430/msp430.c
> @@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
>  }
>  
>  \f
> +/* Describing Relative Costs of Operations
> +   To model the cost of an instruction, use the number of cycles when
> +   optimizing for speed, and the number of words when optimizing for size.
> +   The cheapest instruction will execute in one cycle and cost one word.
> +   The cycle and size costs correspond to 430 ISA instructions, not 430X
> +   instructions or 430X "address" instructions.  The relative costs of 430X
> +   instructions is accurately modeled with the 430 costs.  The relative costs
> +   of some "address" instructions can differ, but these are not yet handled.
> +   Adding support for this could improve performance/code size.  */
> +
> +const int debug_rtx_costs = 0;
> +
> +struct single_op_cost
> +{
> +  const int reg;
> +  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
> +  const int ind;
> +  const int mem;
> +};
> +
> +static const struct single_op_cost cycle_cost_single_op =
> +{
> +  1, 3, 4
> +};
> +
> +static const struct single_op_cost size_cost_single_op =
> +{
> +  1, 1, 2
> +};
> +
> +/* When the destination of an insn is memory, the cost is always the same
> +   regardless of whether that memory is accessed using indirect register,
> +   indexed or absolute addressing.
> +   When the source operand is memory, indirect register and post-increment have
> +   the same cost, which is lower than indexed and absolute, which also have
> +   the same cost.  */
> +struct double_op_cost
> +{
> +  /* Source operand is a register.  */
> +  const int r2r;
> +  const int r2pc;
> +  const int r2m;
> +
> +  /* Source operand is memory, using indirect register (@Rn) or indirect
> +     autoincrement (@Rn+) addressing modes.  */
> +  const int ind2r;
> +  const int ind2pc;
> +  const int ind2m;
> +
> +  /* Source operand is an immediate.  */
> +  const int imm2r;
> +  const int imm2pc;
> +  const int imm2m;
> +
> +  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
> +     addressing modes.  */
> +  const int mem2r;
> +  const int mem2pc;
> +  const int mem2m;
> +};
> +
> +/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
> +   of clock cycles or words.  */
> +static const struct double_op_cost cycle_cost_double_op_mov =
> +{
> +  1, 3, 3,
> +  2, 4, 4,
> +  2, 3, 4,
> +  3, 5, 5
> +};
> +
> +/* Cycle count when memory is the destination operand is one larger than above
> +   for instructions that aren't MOV, BIT or CMP.  */
> +static const struct double_op_cost cycle_cost_double_op =
> +{
> +  1, 3, 4,
> +  2, 4, 5,
> +  2, 3, 5,
> +  3, 5, 6
> +};
> +
> +static const struct double_op_cost size_cost_double_op =
> +{
> +  1, 1, 2,
> +  1, 1, 2,
> +  2, 2, 3,
> +  2, 2, 3
> +};
> +
> +/* TARGET_REGISTER_MOVE_COST
> +   There is only one class of general-purpose, non-fixed registers, and the
> +   relative cost of moving data between them is always the same.
> +   Therefore, the default of 2 is optimal.  */
> +
> +#undef TARGET_MEMORY_MOVE_COST
> +#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
> +
> +/* Return the cost of moving data between registers and memory.
> +   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
> +   of 2.
> +   IN is false if the value is to be written to memory.  */
> +static int
> +msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
> +			 reg_class_t rclass ATTRIBUTE_UNUSED,
> +			 bool in)
> +{
> +  int cost;
> +  const struct double_op_cost *cost_p;
> +  /* Optimize with a code size focus by default, unless -O2 or above is
> +     specified.  */
> +  bool speed = (!optimize_size && optimize >= 2);
> +
> +  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
> +
> +  if (in)
> +    /* Reading from memory using indirect addressing is assumed to be the more
> +       common case.  */
> +    cost = cost_p->ind2r;
> +  else
> +    cost = cost_p->r2m;
> +
> +  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
> +     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
> +  return 2 * cost;
> +}
> +
> +/* BRANCH_COST
> +   Changing from the default of 1 doesn't affect code generation, presumably
> +   because there are no conditional move insns - when a condition is involved,
> +   the only option is to use a cbranch.  */
> +
>  #undef  TARGET_RTX_COSTS
>  #define TARGET_RTX_COSTS msp430_rtx_costs
>  
> -- 
> 2.27.0
> 

> From 4b4bc7c51bbea79abe3095d4b0cf562556419f8c Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Thu, 16 Jul 2020 11:28:59 +0100
> Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS
> 
> Costs of MSP430 instructions are mostly just a function of the type and number
> of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
> operands to calculate the cost of the expression.
> 
> For more complicated operations where library helper functions are required,
> if the cost cannot be accurately calculated, it is estimated and
> disparaged relative to the cost of a single instruction.
> 
> gcc/ChangeLog:
> 
> 	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
> 	declaration.
> 	Remove unused argument.
> 	(struct msp430_multlib_costs): New struct.
> 	(msp430_is_mem_indirect): New function.
> 	(msp430_costs): Likewise.
> 	(msp430_shift_costs): Likewise.
> 	(msp430_muldiv_costs): Likewise.
> 	(msp430_get_inner_dest_code): Likewise.
> 	(msp430_single_op_cost): Likewise.
> 	(msp430_rtx_costs): Rewrite from scratch.
> 	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
> ---
>  gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 530 insertions(+), 15 deletions(-)
> 
> diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
> index 9e739233fa0..81ee5075a57 100644
> --- a/gcc/config/msp430/msp430.c
> +++ b/gcc/config/msp430/msp430.c
> @@ -49,6 +49,9 @@
>  #include "msp430-devices.h"
>  #include "incpath.h"
>  #include "prefix.h"
> +#include "insn-config.h"
> +#include "insn-attr.h"
> +#include "recog.h"
>  
>  /* This file should be included last.  */
>  #include "target-def.h"
> @@ -56,6 +59,7 @@
>  
>  static void msp430_compute_frame_info (void);
>  static bool use_32bit_hwmult (void);
> +static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
>  
>  \f
>  
> @@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
>    2, 2, 3
>  };
>  
> +struct msp430_multlib_costs
> +{
> +  const int mulhi;
> +  const int mulsi;
> +  const int muldi;
> +};
> +
> +/* There is no precise size cost when using libcalls, instead it is disparaged
> +   relative to other instructions.
> +   The cycle costs are from the CALL to the RET, inclusive.
> +   FIXME muldi cost is not accurate.  */
> +static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
> +{
> +  27, 33, 66
> +};
> +
> +/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
> +static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
> +{
> +  27, 42, 66
> +};
> +
>  /* TARGET_REGISTER_MOVE_COST
>     There is only one class of general-purpose, non-fixed registers, and the
>     relative cost of moving data between them is always the same.
> @@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
>     because there are no conditional move insns - when a condition is involved,
>     the only option is to use a cbranch.  */
>  
> +/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
> +   reference, @Rn or @Rn+.  */
> +static bool
> +msp430_is_mem_indirect (rtx x)
> +{
> +  gcc_assert (GET_CODE (x) == MEM);
> +  rtx op0 = XEXP (x, 0);
> +  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
> +}
> +
> +/* Costs of MSP430 instructions are generally based on the addressing mode
> +   combination of the source and destination operands.
> +   Given source operand SRC (which may be NULL to indicate a single-operand
> +   instruction) and destination operand DST return the cost of this
> +   expression.  */
> +static int
> +msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
> +{
> +  enum rtx_code src_code = GET_CODE (src);
> +  enum rtx_code dst_code = GET_CODE (dst);
> +  enum rtx_code outer_code = GET_CODE (outer_rtx);
> +  machine_mode outer_mode = GET_MODE (outer_rtx);
> +  const struct double_op_cost *cost_p;
> +  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
> +
> +  if (outer_code == TRUNCATE
> +      && (outer_mode == QImode
> +	  || outer_mode == HImode
> +	  || outer_mode == PSImode))
> +    /* Truncation to these modes is normally free as a side effect of the
> +       instructions themselves.  */
> +    return 0;
> +
> +  if (dst_code == SYMBOL_REF
> +      || dst_code == LABEL_REF
> +      || dst_code == CONST_INT)
> +    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
> +    return 0;
> +
> +  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
> +    {
> +      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
> +      debug_rtx (dst);
> +      fprintf (stderr, "from:\n");
> +      debug_rtx (outer_rtx);
> +    }
> +
> +  switch (src_code)
> +    {
> +    case REG:
> +      return (dst_code == REG ? cost_p->r2r
> +	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
> +
> +    case CONST_INT:
> +    case SYMBOL_REF:
> +    case LABEL_REF:
> +    case CONST:
> +      return (dst_code == REG ? cost_p->imm2r
> +	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
> +
> +
> +    case MEM:
> +      if (msp430_is_mem_indirect (src))
> +	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
> +						   ? cost_p->ind2pc
> +						   : cost_p->ind2m));
> +      else
> +	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
> +						   ? cost_p->mem2pc
> +						   : cost_p->mem2m));
> +    default:
> +      if (debug_rtx_costs)
> +	{
> +	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
> +	  debug_rtx (src);
> +	  fprintf (stderr, "from:\n");
> +	  debug_rtx (outer_rtx);
> +	}
> +      return cost_p->mem2m;
> +    }
> +}
> +
> +/* Given source operand SRC and destination operand DST from the shift or
> +   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
> +   optimization for speed when SPEED is true.  */
> +static int
> +msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
> +{
> +  int amt;
> +  enum rtx_code src_code = GET_CODE (src);
> +  enum rtx_code dst_code = GET_CODE (dst);
> +  const struct single_op_cost *cost_p;
> +
> +  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
> +
> +  if (debug_rtx_costs
> +      && dst_code != REG
> +      && dst_code != MEM
> +      && dst_code != CONST
> +      && dst_code != SYMBOL_REF
> +      && dst_code != CONST_INT)
> +    {
> +      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
> +      debug_rtx (dst);
> +    }
> +
> +  if (debug_rtx_costs
> +      && src_code != CONST_INT
> +      && src_code != REG)
> +    {
> +      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
> +      debug_rtx (src);
> +    }
> +
> +  if (src_code != CONST_INT)
> +    /* The size or speed cost when the shift amount is unknown cannot be
> +       accurately calculated, so just disparage it slightly.  */
> +    return 2 * msp430_costs (src, dst, speed, outer_rtx);
> +
> +  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
> +    {
> +      /* GCC sometimes tries to perform shifts in some very inventive ways,
> +	 resulting in much larger code size usage than necessary, if
> +	 they are disparaged too much here.  So in general, if
> +	 use_helper_for_const_shift thinks a helper should be used, obey
> +	 that and don't disparage the shift any more than a regular
> +	 instruction, even though the shift may actually cost more.
> +	 This ensures that the RTL generated at the initial expand pass has the
> +	 expected shift instructions, which can be mapped to the helper
> +	 functions.  */
> +      return msp430_costs (src, dst, speed, outer_rtx);
> +    }
> +
> +  if (!msp430x)
> +    {
> +      /* Each shift by one place will be emitted individually.  */
> +      switch (dst_code)
> +	{
> +	case REG:
> +	case CONST_INT:
> +	  return amt * cost_p->reg;
> +	case MEM:
> +	  if (msp430_is_mem_indirect (dst))
> +	    return amt * cost_p->ind;
> +	  else
> +	    return amt * cost_p->mem;
> +	default:
> +	  return amt * cost_p->mem;
> +	}
> +    }
> +
> +  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
> +     versions are used.
> +     Instructions which shift a MEM operand will never actually be output.  It
> +     will always be copied into a register to allow for efficient shifting.  So
> +     the cost just takes into account the cost of an additional copy in that
> +     case.  */
> +  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
> +	  + (dst_code == REG ? 0
> +	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
> +}
> +
> +/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
> +   RTX OUTER_RTX, return the cost of performing that operation, assuming
> +   optimization for speed when SPEED is true.  */
> +static int
> +msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
> +		     machine_mode outer_mode)
> +{
> +  enum rtx_code outer_code = GET_CODE (outer_rtx);
> +  const struct msp430_multlib_costs *cost_p;
> +  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
> +						 || use_32bit_hwmult ()));
> +  cost_p = (hwmult_16bit
> +	    ? &cycle_cost_multlib_32bit
> +	    : &cycle_cost_multlib_16bit);
> +
> +  int factor = 1;
> +  /* Only used in some calculations.  */
> +  int mode_factor = 1;
> +  if (outer_mode == SImode)
> +    mode_factor = 2;
> +  else if (outer_mode == PSImode)
> +    /* PSImode multiplication is performed using SImode operands, so has extra
> +       cost to factor in the conversions necessary before/after the
> +       operation.  */
> +    mode_factor = 3;
> +  else if (outer_mode == DImode)
> +    mode_factor = 4;
> +
> +  if (!speed)
> +    {
> +      /* The codesize cost of using a helper function to perform the
> +	 multiplication or division cannot be accurately calculated, since the
> +	 cost depends on how many times the operation is performed in the
> +	 entire program.  */
> +      if (outer_code != MULT)
> +	/* Division is always expensive.  */
> +	factor = 7;
> +      else if (((hwmult_16bit && outer_mode != DImode)
> +		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
> +	/* When the hardware multiplier is available, only disparage
> +	   slightly.  */
> +	factor = 2;
> +      else
> +	factor = 5;
> +      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
> +    }
> +
> +  /* When there is hardware multiply support, there is a relatively low, fixed
> +     cycle cost to performing any multiplication, but when there is no hardware
> +     multiply support it is very costly.  That precise cycle cost has not been
> +     calculated here.
> +     Division is extra slow since it always uses a software library.
> +     The 16-bit hardware multiply library cannot be used to produce 64-bit
> +     results.  */
> +  if (outer_code != MULT || !msp430_has_hwmult ()
> +      || (outer_mode == DImode && hwmult_16bit))
> +    {
> +      factor = (outer_code == MULT ? 50 : 70);
> +      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
> +    }
> +
> +  switch (outer_mode)
> +    {
> +    case E_QImode:
> +    case E_HImode:
> +      /* Include the cost of copying the operands into and out of the hardware
> +	 multiply routine.  */
> +      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
> +
> +    case E_PSImode:
> +      /* Extra factor for the conversions necessary to do PSI->SI before the
> +	 operation.  */
> +      factor = 2;
> +      /* fallthru.  */
> +    case E_SImode:
> +      return factor * (cost_p->mulsi
> +		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
> +
> +    case E_DImode:
> +    default:
> +      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
> +    }
> +}
> +
> +/* Recurse within X to find the actual destination operand of the expression.
> +   For example:
> +   (plus (ashift (minus (ashift (reg)
> +   (const_int) ......
> +   should return the reg RTX.  */
> +static rtx
> +msp430_get_inner_dest_code (rtx x)
> +{
> +  enum rtx_code code = GET_CODE (x);
> +  rtx op0 = XEXP (x, 0);
> +  switch (code)
> +    {
> +    case REG:
> +    case SYMBOL_REF:
> +    case CONST_INT:
> +    case CONST:
> +    case LABEL_REF:
> +      return x;
> +
> +    case MEM:
> +      /* Return the MEM expr not the inner REG for these cases.  */
> +      switch (GET_CODE (op0))
> +	{
> +	case REG:
> +	case SYMBOL_REF:
> +	case LABEL_REF:
> +	case CONST:
> +	case POST_INC:
> +	  return x;
> +
> +	case PLUS:
> +	  /* return MEM (PLUS (REG) (CONST)) */
> +	  if (GET_CODE (XEXP (op0, 0)) == REG)
> +	    {
> +	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
> +		  || GET_CODE (XEXP (op0, 1)) == CONST
> +		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
> +		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
> +		return x;
> +	      else
> +		return msp430_get_inner_dest_code (op0);
> +	    }
> +	  return msp430_get_inner_dest_code (op0);
> +
> +	default:
> +	  if (GET_RTX_FORMAT (code)[0] != 'e')
> +	    return x;
> +	  return msp430_get_inner_dest_code (op0);
> +	}
> +      break;
> +
> +    default:
> +      if (op0 == NULL_RTX)
> +	gcc_unreachable ();
> +      else
> +	{
> +	  if (GET_RTX_FORMAT (code)[0] != 'e'
> +	      && code != ENTRY_VALUE)
> +	    return x;
> +	  return msp430_get_inner_dest_code (op0);
> +	}
> +    }
> +}
> +
> +/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
> +   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
> +static int
> +msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
> +{
> +  enum rtx_code dst_code = GET_CODE (dst);
> +  const struct single_op_cost *cost_p;
> +  const struct double_op_cost *double_op_cost_p;
> +
> +  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
> +  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
> +
> +  switch (dst_code)
> +    {
> +    case REG:
> +      return cost_p->reg;
> +    case MEM:
> +      if (msp430_is_mem_indirect (dst))
> +	return cost_p->ind;
> +      else
> +	return cost_p->mem;
> +
> +    case CONST_INT:
> +    case CONST_FIXED:
> +    case CONST_DOUBLE:
> +    case SYMBOL_REF:
> +    case CONST:
> +      /* A constant value would need to be copied into a register first.  */
> +      return double_op_cost_p->imm2r + cost_p->reg;
> +
> +    default:
> +      if (debug_rtx_costs)
> +	{
> +	  fprintf (stderr, "msp430_single_op_cost unhandled "
> +		   "dst operand:\n");
> +	  debug_rtx (dst);
> +	  fprintf (stderr, "from:\n");
> +	  debug_rtx (outer_rtx);
> +	}
> +      return cost_p->mem;
> +    }
> +}
> +
>  #undef  TARGET_RTX_COSTS
>  #define TARGET_RTX_COSTS msp430_rtx_costs
>  
> -static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
> -			      machine_mode mode,
> -			      int	   outer_code ATTRIBUTE_UNUSED,
> -			      int	   opno ATTRIBUTE_UNUSED,
> -			      int *	   total,
> -			      bool	   speed ATTRIBUTE_UNUSED)
> +/* This target hook describes the relative costs of RTL expressions.
> +   The function recurses to just before the lowest level of the expression,
> +   when both of the operands of the expression can be examined at the same time.
> +   This is because the cost of the expression depends on the specific
> +   addressing mode combination of the operands.
> +   The hook returns true when all subexpressions of X have been processed, and
> +   false when rtx_cost should recurse.  */
> +static bool
> +msp430_rtx_costs (rtx x,
> +		  machine_mode mode,
> +		  int	   outer_code ATTRIBUTE_UNUSED,
> +		  int	   opno ATTRIBUTE_UNUSED,
> +		  int *	   total,
> +		  bool	   speed)
>  {
> -  int code = GET_CODE (x);
> +  enum rtx_code code = GET_CODE (x);
> +  rtx dst, src;
> +  rtx dst_inner, src_inner;
> +
> +  *total = 0;
> +  dst = XEXP (x, 0);
> +  if (GET_RTX_LENGTH (code) == 1)
> +    /* Some RTX that are single-op in GCC are double-op when translated to
> +       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
> +    src = dst;
> +  else
> +    src = XEXP (x, 1);
> +
>  
>    switch (code)
>      {
> +    case SET:
> +      /* Ignoring SET improves codesize.  */
> +      if (!speed)
> +	return true;
> +      /* fallthru.  */
> +    case PLUS:
> +      if (outer_code == MEM)
> +	/* Do not add any cost for the plus itself, but recurse in case there
> +	   are more complicated RTX inside.  */
> +	return false;
> +      /* fallthru.  */
> +    case MINUS:
> +    case AND:
> +    case IOR:
> +    case XOR:
> +    case NOT:
> +    case ZERO_EXTEND:
> +    case TRUNCATE:
> +    case NEG:
> +    case ZERO_EXTRACT:
> +    case SIGN_EXTRACT:
> +    case IF_THEN_ELSE:
> +      dst_inner = msp430_get_inner_dest_code (dst);
> +      src_inner = msp430_get_inner_dest_code (src);
> +      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
> +      if (mode == SImode)
> +	*total *= 2;
> +      if (mode == DImode)
> +	*total *= 4;
> +      return false;
> +
> +    case ROTATE:
> +    case ASHIFT:
> +    case ASHIFTRT:
> +    case LSHIFTRT:
> +      dst_inner = msp430_get_inner_dest_code (dst);
> +      src_inner = msp430_get_inner_dest_code (src);
> +      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
> +						  speed, x));
> +      if (mode == SImode)
> +	*total *= 2;
> +      if (mode == DImode)
> +	*total *= 4;
> +      return false;
> +
> +    case MULT:
> +    case DIV:
> +    case MOD:
> +    case UDIV:
> +    case UMOD:
> +      dst_inner = msp430_get_inner_dest_code (dst);
> +      src_inner = msp430_get_inner_dest_code (src);
> +      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
> +						   x, mode));
> +      return false;
> +
> +    case CALL:
>      case SIGN_EXTEND:
> -      if (mode == SImode && outer_code == SET)
> +      dst_inner = msp430_get_inner_dest_code (dst);
> +      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
> +      if (mode == SImode)
> +	*total *= 2;
> +      if (mode == DImode)
> +	*total *= 4;
> +      return false;
> +
> +    case CONST_INT:
> +    case CONST_FIXED:
> +    case CONST_DOUBLE:
> +    case SYMBOL_REF:
> +    case CONST:
> +    case LABEL_REF:
> +    case REG:
> +    case PC:
> +    case POST_INC:
> +      if (mode == SImode)
> +	*total = COSTS_N_INSNS (2);
> +      else if (mode == DImode)
> +	*total = COSTS_N_INSNS (4);
> +      return true;
> +
> +    case MEM:
> +      /* PSImode operands are expensive when in memory.  */
> +      if (mode == PSImode)
> +	*total = COSTS_N_INSNS (1);
> +      else if (mode == SImode)
> +	*total = COSTS_N_INSNS (2);
> +      else if (mode == DImode)
> +	*total = COSTS_N_INSNS (4);
> +      /* Recurse into the MEM.  */
> +      return false;
> +
> +    case EQ:
> +    case NE:
> +    case GT:
> +    case GTU:
> +    case GE:
> +    case GEU:
> +    case LT:
> +    case LTU:
> +    case LE:
> +    case LEU:
> +      /* Conditions are mostly equivalent, changing their relative
> +	 costs has no effect.  */
> +      return false;
> +
> +    case ASM_OPERANDS:
> +    case ASM_INPUT:
> +    case CLOBBER:
> +    case COMPARE:
> +    case CONCAT:
> +    case ENTRY_VALUE:
> +      /* Other unhandled expressions.  */
> +      return false;
> +
> +    default:
> +      if (debug_rtx_costs)
>  	{
> -	  *total = COSTS_N_INSNS (4);
> -	  return true;
> +	  fprintf (stderr, "\nUnhandled RTX\n");
> +	  debug_rtx (x);
>  	}
> -      break;
> +      return false;
>      }
> -  return false;
>  }
>  \f
>  /* Function Entry and Exit */
> @@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
>  /* Return TRUE if the helper function should be used and FALSE if the shifts
>     insns should be emitted inline.  */
>  static bool
> -use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
> -			    HOST_WIDE_INT amt)
> +use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
>  {
>    const int default_inline_shift = 4;
>    /* We initialize the option to 65 so we know if the user set it or not.  */
> @@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
>       the heuristic accordingly.  */
>    int max_inline_32 = max_inline / 2;
>  
> +  if (mode == E_DImode)
> +    return true;
> +
>    /* Don't use helpers for these modes on 430X, when optimizing for speed, or
>       when emitting a small number of insns.  */
>    if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
> @@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
>       constant.  */
>    if (!CONST_INT_P (operands[2])
>        || mode == E_DImode
> -      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
> +      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
>      {
>        const char *helper_name = NULL;
>        /* The const variants of mspabi shifts have significantly larger code
> -- 
> 2.27.0
> 

> From e8f2aacef2b740e8123b6450f2addc16d197c464 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(-)
> 
> 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)"))
> -- 
> 2.27.0
> 

> From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Thu, 16 Jul 2020 11:34:50 +0100
> Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST
> 
> The length of an insn can be used to calculate its cost, when optimizing for
> size. When optimizing for speed, this is a good estimate, since the cycle cost
> of an MSP430 instruction increases with its length.
> 
> gcc/ChangeLog:
> 
> 	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
> 	(msp430_insn_cost): New function.
> 	* config/msp430/msp430.h (BRANCH_COST): Define.
> 	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
> 	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
> 	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
> 	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
> ---
>  gcc/config/msp430/msp430.c                    | 25 ++++++++---
>  gcc/config/msp430/msp430.h                    |  8 ++++
>  .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
>  .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
>  .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
>  .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
>  6 files changed, 189 insertions(+), 5 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
>  create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
>  create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
>  create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
> 
> diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
> index b7daafcc11a..35ccd2817ca 100644
> --- a/gcc/config/msp430/msp430.c
> +++ b/gcc/config/msp430/msp430.c
> @@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
>    return 2 * cost;
>  }
>  
> -/* BRANCH_COST
> -   Changing from the default of 1 doesn't affect code generation, presumably
> -   because there are no conditional move insns - when a condition is involved,
> -   the only option is to use a cbranch.  */
> -
>  /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
>     reference, @Rn or @Rn+.  */
>  static bool
> @@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
>        return false;
>      }
>  }
> +
> +#undef TARGET_INSN_COST
> +#define TARGET_INSN_COST msp430_insn_cost
> +
> +static int
> +msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
> +{
> +  if (recog_memoized (insn) < 0)
> +    return 0;
> +
> +  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
> +     length of 2 bytes is the smallest possible size and so must be equivalent
> +     to COSTS_N_INSNS (1).  */
> +  return COSTS_N_INSNS (get_attr_length (insn) / 2);
> +
> +  /* FIXME Add more detailed costs when optimizing for speed.
> +     For now the length of the instruction is a good approximiation and roughly
> +     correlates with cycle cost.  */
> +}
> +
>  \f
>  /* Function Entry and Exit */
>  
> diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
> index b813e825311..830101408a5 100644
> --- a/gcc/config/msp430/msp430.h
> +++ b/gcc/config/msp430/msp430.h
> @@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
>  #define HAS_LONG_COND_BRANCH		0
>  #define HAS_LONG_UNCOND_BRANCH		0
>  
> +/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
> +#define BRANCH_COST(speed_p, predictable_p) 3
> +
> +/* Override the default BRANCH_COST heuristic to indicate that it is preferable
> +   to retain short-circuit operations, this results in significantly better
> +   codesize and performance.  */
> +#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
> +
>  #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
>  #define WORD_REGISTER_OPERATIONS	1
>  
> diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
> new file mode 100644
> index 00000000000..1bd6a142002
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Verify the MSP430 cost model is working as expected for the default ISA
> +   (msp430x) and hwmult (none), when compiling at -O3.  */
> +
> +char arr[2];
> +char a;
> +char *ptr;
> +
> +/*
> +** foo:
> +** ...
> +**	MOV.B	\&a, \&arr\+1
> +**	MOV.*	#arr\+2, \&ptr
> +** ...
> +*/
> +
> +void
> +foo (void)
> +{
> +  arr[1] = a;
> +  ptr = arr + 2;
> +}
> +
> +extern void ext (void);
> +
> +/*
> +** bar:
> +** ...
> +**	CALL.*	#ext
> +**	CALL.*	#ext
> +** ...
> +*/
> +
> +void
> +bar (void)
> +{
> +  ext ();
> +  ext ();
> +}
> diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
> new file mode 100644
> index 00000000000..1e48625f2e5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
> @@ -0,0 +1,38 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -mhwmult=f5series" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Verify the MSP430 cost model is working as expected for the default ISA
> +   (msp430x) and f5series hwmult, when compiling at -O3.  */
> +
> +volatile unsigned long a;
> +volatile unsigned int b;
> +volatile unsigned long c;
> +unsigned long res1;
> +unsigned long res2;
> +unsigned long res3;
> +
> +/*
> +** foo:
> +** ...
> +**	MOV.B	#16, R14
> +**	CALL.*	#__mspabi_slll
> +** ...
> +**	MOV.*	\&res2.*
> +** ...
> +**	RLA.*RLC.*
> +** ...
> +**	MOV.*	\&res3.*
> +** ...
> +**	RLA.*RLC.*
> +** ...
> +*/
> +void foo (void)
> +{
> +  /* Use the shift library function for this.  */
> +  res1 = (a << 16) | b;
> +  /* Emit 7 inline shifts for this.  */
> +  res2 *= 128;
> +  /* Perform this multiplication inline, using addition and shifts.  */
> +  res3 *= 100;
> +}
> diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
> new file mode 100644
> index 00000000000..8f3d1b28049
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Os" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Verify the MSP430 cost model is working as expected for the default ISA
> +   (msp430x) and hwmult (none), when compiling at -Os.  */
> +
> +char arr[2];
> +char a;
> +char *ptr;
> +
> +/*
> +** foo:
> +** ...
> +**	MOV.B	\&a, \&arr\+1
> +**	MOV.*	#arr\+2, \&ptr
> +** ...
> +*/
> +
> +void
> +foo (void)
> +{
> +  arr[1] = a;
> +  ptr = arr + 2;
> +}
> +
> +extern void ext (void);
> +
> +/*
> +** bar:
> +** ...
> +**	MOV.*	#ext, R10
> +**	CALL.*	R10
> +**	CALL.*	R10
> +** ...
> +*/
> +
> +void
> +bar (void)
> +{
> +  ext ();
> +  ext ();
> +}
> diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
> new file mode 100644
> index 00000000000..bb37f9083d9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
> @@ -0,0 +1,38 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Os -mhwmult=f5series" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Verify the MSP430 cost model is working as expected for the default ISA
> +   (msp430x) and f5series hwmult, when compiling at -Os.  */
> +
> +volatile unsigned long a;
> +volatile unsigned int b;
> +volatile unsigned long c;
> +unsigned long res1;
> +unsigned long res2;
> +unsigned long res3;
> +
> +/*
> +** foo:
> +** ...
> +**	MOV.B	#16, R14
> +**	CALL.*	#__mspabi_slll
> +** ...
> +**	MOV.B	#7, R14
> +**	CALL.*	#__mspabi_slll
> +** ...
> +**	MOV.B	#100, R14
> +**	MOV.B	#0, R15
> +** ...
> +**	CALL.*	#__mulsi2_f5
> +** ...
> +*/
> +void foo (void)
> +{
> +  /* Use the shift library function for this.  */
> +  res1 = (a << 16) | b;
> +  /* Likewise.  */
> +  res2 *= 128;
> +  /* Use the hardware multiply library function for this.  */
> +  res3 *= 100;
> +}
> -- 
> 2.27.0
> 

> From 59fa213daac1cedd6d1564dfeacb1f6ce9397e98 Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Thu, 16 Jul 2020 11:35:25 +0100
> Subject: [PATCH 5/5] MSP430: Skip index-1.c test
> 
> To access the "n - 100000"th element of "a" in this test, GCC will
> generate the following code for msp430-elf with -mcpu=msp430x:
> 
>   RLAM.W  #1, R12
>   MOV.W a-3392(R12), R12
> 
> Since there aren't actually 100,000 elements in a, this means that
> "a-3392" offset calculated by the linker can overflow, as the address of
> "a" can validly be less than 3392.
> 
> The relocations used for -mcpu=msp430 and -mlarge are not as strict and
> the calculated value is allowed to wrap around the address space,
> avoiding relocation overflows.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
> ---
>  gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
> index b00090d834a..d96be4c77b8 100644
> --- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
> +++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
> @@ -1,3 +1,5 @@
> +/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
> +
>  int a[] =
>  {
>    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
> -- 
> 2.27.0
> 


[-- Attachment #2: 0001-MSP430-Implement-TARGET_MEMORY_MOVE_COST.patch --]
[-- Type: text/plain, Size: 5197 bytes --]

From e260de5a31e661afdfaaf2c8053b574a292d6826 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:11 +0100
Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

gcc/ChangeLog:

	* config/msp430/msp430.c (struct single_op_cost): New struct.
	(struct double_op_cost): Likewise.
	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
	(TARGET_MEMORY_MOVE_COST): Define to...
	(msp430_memory_move_cost): New function.
	(BRANCH_COST): Don't define but add comment.
---
 gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index c2b24974364..9e739233fa0 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
 }
 
 \f
+/* Describing Relative Costs of Operations
+   To model the cost of an instruction, use the number of cycles when
+   optimizing for speed, and the number of words when optimizing for size.
+   The cheapest instruction will execute in one cycle and cost one word.
+   The cycle and size costs correspond to 430 ISA instructions, not 430X
+   instructions or 430X "address" instructions.  The relative costs of 430X
+   instructions is accurately modeled with the 430 costs.  The relative costs
+   of some "address" instructions can differ, but these are not yet handled.
+   Adding support for this could improve performance/code size.  */
+
+const int debug_rtx_costs = 0;
+
+struct single_op_cost
+{
+  const int reg;
+  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
+  const int ind;
+  const int mem;
+};
+
+static const struct single_op_cost cycle_cost_single_op =
+{
+  1, 3, 4
+};
+
+static const struct single_op_cost size_cost_single_op =
+{
+  1, 1, 2
+};
+
+/* When the destination of an insn is memory, the cost is always the same
+   regardless of whether that memory is accessed using indirect register,
+   indexed or absolute addressing.
+   When the source operand is memory, indirect register and post-increment have
+   the same cost, which is lower than indexed and absolute, which also have
+   the same cost.  */
+struct double_op_cost
+{
+  /* Source operand is a register.  */
+  const int r2r;
+  const int r2pc;
+  const int r2m;
+
+  /* Source operand is memory, using indirect register (@Rn) or indirect
+     autoincrement (@Rn+) addressing modes.  */
+  const int ind2r;
+  const int ind2pc;
+  const int ind2m;
+
+  /* Source operand is an immediate.  */
+  const int imm2r;
+  const int imm2pc;
+  const int imm2m;
+
+  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
+     addressing modes.  */
+  const int mem2r;
+  const int mem2pc;
+  const int mem2m;
+};
+
+/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
+   of clock cycles or words.  */
+static const struct double_op_cost cycle_cost_double_op_mov =
+{
+  1, 3, 3,
+  2, 4, 4,
+  2, 3, 4,
+  3, 5, 5
+};
+
+/* Cycle count when memory is the destination operand is one larger than above
+   for instructions that aren't MOV, BIT or CMP.  */
+static const struct double_op_cost cycle_cost_double_op =
+{
+  1, 3, 4,
+  2, 4, 5,
+  2, 3, 5,
+  3, 5, 6
+};
+
+static const struct double_op_cost size_cost_double_op =
+{
+  1, 1, 2,
+  1, 1, 2,
+  2, 2, 3,
+  2, 2, 3
+};
+
+/* TARGET_REGISTER_MOVE_COST
+   There is only one class of general-purpose, non-fixed registers, and the
+   relative cost of moving data between them is always the same.
+   Therefore, the default of 2 is optimal.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
+
+/* Return the cost of moving data between registers and memory.
+   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
+   of 2.
+   IN is false if the value is to be written to memory.  */
+static int
+msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+			 reg_class_t rclass ATTRIBUTE_UNUSED,
+			 bool in)
+{
+  int cost;
+  const struct double_op_cost *cost_p;
+  /* Optimize with a code size focus by default, unless -O2 or above is
+     specified.  */
+  bool speed = (!optimize_size && optimize >= 2);
+
+  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
+
+  if (in)
+    /* Reading from memory using indirect addressing is assumed to be the more
+       common case.  */
+    cost = cost_p->ind2r;
+  else
+    cost = cost_p->r2m;
+
+  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
+     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
+  return 2 * cost;
+}
+
+/* BRANCH_COST
+   Changing from the default of 1 doesn't affect code generation, presumably
+   because there are no conditional move insns - when a condition is involved,
+   the only option is to use a cbranch.  */
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-- 
2.27.0


[-- Attachment #3: 0002-MSP430-Implement-TARGET_RTX_COSTS.patch --]
[-- Type: text/plain, Size: 19795 bytes --]

From 4b4bc7c51bbea79abe3095d4b0cf562556419f8c Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:59 +0100
Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.

gcc/ChangeLog:

	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
	declaration.
	Remove unused argument.
	(struct msp430_multlib_costs): New struct.
	(msp430_is_mem_indirect): New function.
	(msp430_costs): Likewise.
	(msp430_shift_costs): Likewise.
	(msp430_muldiv_costs): Likewise.
	(msp430_get_inner_dest_code): Likewise.
	(msp430_single_op_cost): Likewise.
	(msp430_rtx_costs): Rewrite from scratch.
	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
---
 gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 530 insertions(+), 15 deletions(-)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 9e739233fa0..81ee5075a57 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -49,6 +49,9 @@
 #include "msp430-devices.h"
 #include "incpath.h"
 #include "prefix.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -56,6 +59,7 @@
 
 static void msp430_compute_frame_info (void);
 static bool use_32bit_hwmult (void);
+static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
 
 \f
 
@@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
   2, 2, 3
 };
 
+struct msp430_multlib_costs
+{
+  const int mulhi;
+  const int mulsi;
+  const int muldi;
+};
+
+/* There is no precise size cost when using libcalls, instead it is disparaged
+   relative to other instructions.
+   The cycle costs are from the CALL to the RET, inclusive.
+   FIXME muldi cost is not accurate.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
+{
+  27, 33, 66
+};
+
+/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
+{
+  27, 42, 66
+};
+
 /* TARGET_REGISTER_MOVE_COST
    There is only one class of general-purpose, non-fixed registers, and the
    relative cost of moving data between them is always the same.
@@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
    because there are no conditional move insns - when a condition is involved,
    the only option is to use a cbranch.  */
 
+/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
+   reference, @Rn or @Rn+.  */
+static bool
+msp430_is_mem_indirect (rtx x)
+{
+  gcc_assert (GET_CODE (x) == MEM);
+  rtx op0 = XEXP (x, 0);
+  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
+}
+
+/* Costs of MSP430 instructions are generally based on the addressing mode
+   combination of the source and destination operands.
+   Given source operand SRC (which may be NULL to indicate a single-operand
+   instruction) and destination operand DST return the cost of this
+   expression.  */
+static int
+msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  machine_mode outer_mode = GET_MODE (outer_rtx);
+  const struct double_op_cost *cost_p;
+  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  if (outer_code == TRUNCATE
+      && (outer_mode == QImode
+	  || outer_mode == HImode
+	  || outer_mode == PSImode))
+    /* Truncation to these modes is normally free as a side effect of the
+       instructions themselves.  */
+    return 0;
+
+  if (dst_code == SYMBOL_REF
+      || dst_code == LABEL_REF
+      || dst_code == CONST_INT)
+    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
+    return 0;
+
+  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
+    {
+      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+      fprintf (stderr, "from:\n");
+      debug_rtx (outer_rtx);
+    }
+
+  switch (src_code)
+    {
+    case REG:
+      return (dst_code == REG ? cost_p->r2r
+	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
+
+    case CONST_INT:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      return (dst_code == REG ? cost_p->imm2r
+	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
+
+
+    case MEM:
+      if (msp430_is_mem_indirect (src))
+	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
+						   ? cost_p->ind2pc
+						   : cost_p->ind2m));
+      else
+	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
+						   ? cost_p->mem2pc
+						   : cost_p->mem2m));
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
+	  debug_rtx (src);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem2m;
+    }
+}
+
+/* Given source operand SRC and destination operand DST from the shift or
+   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  int amt;
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+
+  if (debug_rtx_costs
+      && dst_code != REG
+      && dst_code != MEM
+      && dst_code != CONST
+      && dst_code != SYMBOL_REF
+      && dst_code != CONST_INT)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+    }
+
+  if (debug_rtx_costs
+      && src_code != CONST_INT
+      && src_code != REG)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
+      debug_rtx (src);
+    }
+
+  if (src_code != CONST_INT)
+    /* The size or speed cost when the shift amount is unknown cannot be
+       accurately calculated, so just disparage it slightly.  */
+    return 2 * msp430_costs (src, dst, speed, outer_rtx);
+
+  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
+    {
+      /* GCC sometimes tries to perform shifts in some very inventive ways,
+	 resulting in much larger code size usage than necessary, if
+	 they are disparaged too much here.  So in general, if
+	 use_helper_for_const_shift thinks a helper should be used, obey
+	 that and don't disparage the shift any more than a regular
+	 instruction, even though the shift may actually cost more.
+	 This ensures that the RTL generated at the initial expand pass has the
+	 expected shift instructions, which can be mapped to the helper
+	 functions.  */
+      return msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  if (!msp430x)
+    {
+      /* Each shift by one place will be emitted individually.  */
+      switch (dst_code)
+	{
+	case REG:
+	case CONST_INT:
+	  return amt * cost_p->reg;
+	case MEM:
+	  if (msp430_is_mem_indirect (dst))
+	    return amt * cost_p->ind;
+	  else
+	    return amt * cost_p->mem;
+	default:
+	  return amt * cost_p->mem;
+	}
+    }
+
+  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
+     versions are used.
+     Instructions which shift a MEM operand will never actually be output.  It
+     will always be copied into a register to allow for efficient shifting.  So
+     the cost just takes into account the cost of an additional copy in that
+     case.  */
+  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
+	  + (dst_code == REG ? 0
+	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
+}
+
+/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
+   RTX OUTER_RTX, return the cost of performing that operation, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
+		     machine_mode outer_mode)
+{
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  const struct msp430_multlib_costs *cost_p;
+  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
+						 || use_32bit_hwmult ()));
+  cost_p = (hwmult_16bit
+	    ? &cycle_cost_multlib_32bit
+	    : &cycle_cost_multlib_16bit);
+
+  int factor = 1;
+  /* Only used in some calculations.  */
+  int mode_factor = 1;
+  if (outer_mode == SImode)
+    mode_factor = 2;
+  else if (outer_mode == PSImode)
+    /* PSImode multiplication is performed using SImode operands, so has extra
+       cost to factor in the conversions necessary before/after the
+       operation.  */
+    mode_factor = 3;
+  else if (outer_mode == DImode)
+    mode_factor = 4;
+
+  if (!speed)
+    {
+      /* The codesize cost of using a helper function to perform the
+	 multiplication or division cannot be accurately calculated, since the
+	 cost depends on how many times the operation is performed in the
+	 entire program.  */
+      if (outer_code != MULT)
+	/* Division is always expensive.  */
+	factor = 7;
+      else if (((hwmult_16bit && outer_mode != DImode)
+		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
+	/* When the hardware multiplier is available, only disparage
+	   slightly.  */
+	factor = 2;
+      else
+	factor = 5;
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  /* When there is hardware multiply support, there is a relatively low, fixed
+     cycle cost to performing any multiplication, but when there is no hardware
+     multiply support it is very costly.  That precise cycle cost has not been
+     calculated here.
+     Division is extra slow since it always uses a software library.
+     The 16-bit hardware multiply library cannot be used to produce 64-bit
+     results.  */
+  if (outer_code != MULT || !msp430_has_hwmult ()
+      || (outer_mode == DImode && hwmult_16bit))
+    {
+      factor = (outer_code == MULT ? 50 : 70);
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  switch (outer_mode)
+    {
+    case E_QImode:
+    case E_HImode:
+      /* Include the cost of copying the operands into and out of the hardware
+	 multiply routine.  */
+      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
+
+    case E_PSImode:
+      /* Extra factor for the conversions necessary to do PSI->SI before the
+	 operation.  */
+      factor = 2;
+      /* fallthru.  */
+    case E_SImode:
+      return factor * (cost_p->mulsi
+		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
+
+    case E_DImode:
+    default:
+      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
+    }
+}
+
+/* Recurse within X to find the actual destination operand of the expression.
+   For example:
+   (plus (ashift (minus (ashift (reg)
+   (const_int) ......
+   should return the reg RTX.  */
+static rtx
+msp430_get_inner_dest_code (rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx op0 = XEXP (x, 0);
+  switch (code)
+    {
+    case REG:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case LABEL_REF:
+      return x;
+
+    case MEM:
+      /* Return the MEM expr not the inner REG for these cases.  */
+      switch (GET_CODE (op0))
+	{
+	case REG:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	case CONST:
+	case POST_INC:
+	  return x;
+
+	case PLUS:
+	  /* return MEM (PLUS (REG) (CONST)) */
+	  if (GET_CODE (XEXP (op0, 0)) == REG)
+	    {
+	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+		  || GET_CODE (XEXP (op0, 1)) == CONST
+		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
+		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
+		return x;
+	      else
+		return msp430_get_inner_dest_code (op0);
+	    }
+	  return msp430_get_inner_dest_code (op0);
+
+	default:
+	  if (GET_RTX_FORMAT (code)[0] != 'e')
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+      break;
+
+    default:
+      if (op0 == NULL_RTX)
+	gcc_unreachable ();
+      else
+	{
+	  if (GET_RTX_FORMAT (code)[0] != 'e'
+	      && code != ENTRY_VALUE)
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+    }
+}
+
+/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
+   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
+static int
+msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+  const struct double_op_cost *double_op_cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  switch (dst_code)
+    {
+    case REG:
+      return cost_p->reg;
+    case MEM:
+      if (msp430_is_mem_indirect (dst))
+	return cost_p->ind;
+      else
+	return cost_p->mem;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+      /* A constant value would need to be copied into a register first.  */
+      return double_op_cost_p->imm2r + cost_p->reg;
+
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_single_op_cost unhandled "
+		   "dst operand:\n");
+	  debug_rtx (dst);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem;
+    }
+}
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
-			      machine_mode mode,
-			      int	   outer_code ATTRIBUTE_UNUSED,
-			      int	   opno ATTRIBUTE_UNUSED,
-			      int *	   total,
-			      bool	   speed ATTRIBUTE_UNUSED)
+/* This target hook describes the relative costs of RTL expressions.
+   The function recurses to just before the lowest level of the expression,
+   when both of the operands of the expression can be examined at the same time.
+   This is because the cost of the expression depends on the specific
+   addressing mode combination of the operands.
+   The hook returns true when all subexpressions of X have been processed, and
+   false when rtx_cost should recurse.  */
+static bool
+msp430_rtx_costs (rtx x,
+		  machine_mode mode,
+		  int	   outer_code ATTRIBUTE_UNUSED,
+		  int	   opno ATTRIBUTE_UNUSED,
+		  int *	   total,
+		  bool	   speed)
 {
-  int code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+  rtx dst, src;
+  rtx dst_inner, src_inner;
+
+  *total = 0;
+  dst = XEXP (x, 0);
+  if (GET_RTX_LENGTH (code) == 1)
+    /* Some RTX that are single-op in GCC are double-op when translated to
+       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
+    src = dst;
+  else
+    src = XEXP (x, 1);
+
 
   switch (code)
     {
+    case SET:
+      /* Ignoring SET improves codesize.  */
+      if (!speed)
+	return true;
+      /* fallthru.  */
+    case PLUS:
+      if (outer_code == MEM)
+	/* Do not add any cost for the plus itself, but recurse in case there
+	   are more complicated RTX inside.  */
+	return false;
+      /* fallthru.  */
+    case MINUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case NOT:
+    case ZERO_EXTEND:
+    case TRUNCATE:
+    case NEG:
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case IF_THEN_ELSE:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case ROTATE:
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
+						  speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case MULT:
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
+						   x, mode));
+      return false;
+
+    case CALL:
     case SIGN_EXTEND:
-      if (mode == SImode && outer_code == SET)
+      dst_inner = msp430_get_inner_dest_code (dst);
+      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
+    case REG:
+    case PC:
+    case POST_INC:
+      if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      return true;
+
+    case MEM:
+      /* PSImode operands are expensive when in memory.  */
+      if (mode == PSImode)
+	*total = COSTS_N_INSNS (1);
+      else if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      /* Recurse into the MEM.  */
+      return false;
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+      /* Conditions are mostly equivalent, changing their relative
+	 costs has no effect.  */
+      return false;
+
+    case ASM_OPERANDS:
+    case ASM_INPUT:
+    case CLOBBER:
+    case COMPARE:
+    case CONCAT:
+    case ENTRY_VALUE:
+      /* Other unhandled expressions.  */
+      return false;
+
+    default:
+      if (debug_rtx_costs)
 	{
-	  *total = COSTS_N_INSNS (4);
-	  return true;
+	  fprintf (stderr, "\nUnhandled RTX\n");
+	  debug_rtx (x);
 	}
-      break;
+      return false;
     }
-  return false;
 }
 \f
 /* Function Entry and Exit */
@@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
 /* Return TRUE if the helper function should be used and FALSE if the shifts
    insns should be emitted inline.  */
 static bool
-use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
-			    HOST_WIDE_INT amt)
+use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
 {
   const int default_inline_shift = 4;
   /* We initialize the option to 65 so we know if the user set it or not.  */
@@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
      the heuristic accordingly.  */
   int max_inline_32 = max_inline / 2;
 
+  if (mode == E_DImode)
+    return true;
+
   /* Don't use helpers for these modes on 430X, when optimizing for speed, or
      when emitting a small number of insns.  */
   if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
@@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
      constant.  */
   if (!CONST_INT_P (operands[2])
       || mode == E_DImode
-      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
+      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
     {
       const char *helper_name = NULL;
       /* The const variants of mspabi shifts have significantly larger code
-- 
2.27.0


[-- Attachment #4: 0003-MSP430-Add-defaulting-to-the-insn-length-attribute.patch --]
[-- Type: text/plain, Size: 47996 bytes --]

From e8f2aacef2b740e8123b6450f2addc16d197c464 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(-)

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)"))
-- 
2.27.0


[-- Attachment #5: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7678 bytes --]

From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.h (BRANCH_COST): Define.
	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 25 ++++++++---
 gcc/config/msp430/msp430.h                    |  8 ++++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 189 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..35ccd2817ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
   return 2 * cost;
 }
 
-/* BRANCH_COST
-   Changing from the default of 1 doesn't affect code generation, presumably
-   because there are no conditional move insns - when a condition is involved,
-   the only option is to use a cbranch.  */
-
 /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
    reference, @Rn or @Rn+.  */
 static bool
@@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (get_attr_length (insn) / 2);
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b813e825311..830101408a5 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
 #define HAS_LONG_COND_BRANCH		0
 #define HAS_LONG_UNCOND_BRANCH		0
 
+/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
+#define BRANCH_COST(speed_p, predictable_p) 3
+
+/* Override the default BRANCH_COST heuristic to indicate that it is preferable
+   to retain short-circuit operations, this results in significantly better
+   codesize and performance.  */
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
 #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
 #define WORD_REGISTER_OPERATIONS	1
 
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


[-- Attachment #6: 0005-MSP430-Skip-index-1.c-test.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

From 59fa213daac1cedd6d1564dfeacb1f6ce9397e98 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:35:25 +0100
Subject: [PATCH 5/5] MSP430: Skip index-1.c test

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
---
 gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
index b00090d834a..d96be4c77b8 100644
--- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
@@ -1,3 +1,5 @@
+/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
+
 int a[] =
 {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* ping x3 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-09-15 20:30   ` ping x2 " Jozef Lawrynowicz
@ 2020-10-14 15:31     ` Jozef Lawrynowicz
  2020-11-06 20:51       ` ping x4 " Jozef Lawrynowicz
  2020-11-06 20:53     ` ping x2 " Jeff Law
  1 sibling, 1 reply; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-10-14 15:31 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2314 bytes --]

3rd ping for below.

On Tue, Sep 15, 2020 at 09:30:22PM +0100, Jozef Lawrynowicz wrote:
> Ping x2 for below.
> 
> On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
> > Pinging for this series of patches.
> > Attached all patches to this mail with the ammended patch 4 thanks to
> > Segher's review.
> > 
> > Thanks,
> > Jozef
> > 
> > On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
> > > The following series of patches for MSP430 implement some of the target
> > > macros used to determine the relative costs of operations.
> > > 
> > > To give an indication of the overall effect of these changes on
> > > codesize, below are some size statistics collected from all the
> > > executable files from execute.exp that are built at -Os.
> > > There are around 1470 such tests (depending on the configuration).
> > > 
> > > The percentage change (((new - old)/old) * 100) in text size is calculated
> > > for each test and the given metric is applied to that overall set of data.
> > > 
> > > Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
> > > -----------------------------------------------------------------------------
> > > -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
> > > -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
> > > -mlarge       |  -1.7    |   -1.9     |      1412         |      37
> > > 
> > > Successfully regtested on trunk for msp430-elf, ok to apply?
> > > 
> > > Jozef Lawrynowicz (5):
> > >   MSP430: Implement TARGET_MEMORY_MOVE_COST
> > >   MSP430: Implement TARGET_RTX_COSTS
> > >   MSP430: Add defaulting to the insn length attribute
> > >   MSP430: Implement TARGET_INSN_COST
> > >   MSP430: Skip index-1.c test
> > > 
> > >  gcc/config/msp430/msp430-protos.h             |   5 +-
> > >  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
> > >  gcc/config/msp430/msp430.h                    |  13 +
> > >  gcc/config/msp430/msp430.md                   | 439 +++++++--
> > >  gcc/config/msp430/msp430.opt                  |   4 +
> > >  gcc/config/msp430/predicates.md               |  13 +
> > >  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
> > >  7 files changed, 1206 insertions(+), 137 deletions(-)
> > > 
> > > -- 
> > > 2.27.0
> > > 
> 

[-- Attachment #2: 0001-MSP430-Implement-TARGET_MEMORY_MOVE_COST.patch --]
[-- Type: text/plain, Size: 5197 bytes --]

From e260de5a31e661afdfaaf2c8053b574a292d6826 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:11 +0100
Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

gcc/ChangeLog:

	* config/msp430/msp430.c (struct single_op_cost): New struct.
	(struct double_op_cost): Likewise.
	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
	(TARGET_MEMORY_MOVE_COST): Define to...
	(msp430_memory_move_cost): New function.
	(BRANCH_COST): Don't define but add comment.
---
 gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index c2b24974364..9e739233fa0 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
 }
 
 \f
+/* Describing Relative Costs of Operations
+   To model the cost of an instruction, use the number of cycles when
+   optimizing for speed, and the number of words when optimizing for size.
+   The cheapest instruction will execute in one cycle and cost one word.
+   The cycle and size costs correspond to 430 ISA instructions, not 430X
+   instructions or 430X "address" instructions.  The relative costs of 430X
+   instructions is accurately modeled with the 430 costs.  The relative costs
+   of some "address" instructions can differ, but these are not yet handled.
+   Adding support for this could improve performance/code size.  */
+
+const int debug_rtx_costs = 0;
+
+struct single_op_cost
+{
+  const int reg;
+  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
+  const int ind;
+  const int mem;
+};
+
+static const struct single_op_cost cycle_cost_single_op =
+{
+  1, 3, 4
+};
+
+static const struct single_op_cost size_cost_single_op =
+{
+  1, 1, 2
+};
+
+/* When the destination of an insn is memory, the cost is always the same
+   regardless of whether that memory is accessed using indirect register,
+   indexed or absolute addressing.
+   When the source operand is memory, indirect register and post-increment have
+   the same cost, which is lower than indexed and absolute, which also have
+   the same cost.  */
+struct double_op_cost
+{
+  /* Source operand is a register.  */
+  const int r2r;
+  const int r2pc;
+  const int r2m;
+
+  /* Source operand is memory, using indirect register (@Rn) or indirect
+     autoincrement (@Rn+) addressing modes.  */
+  const int ind2r;
+  const int ind2pc;
+  const int ind2m;
+
+  /* Source operand is an immediate.  */
+  const int imm2r;
+  const int imm2pc;
+  const int imm2m;
+
+  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
+     addressing modes.  */
+  const int mem2r;
+  const int mem2pc;
+  const int mem2m;
+};
+
+/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
+   of clock cycles or words.  */
+static const struct double_op_cost cycle_cost_double_op_mov =
+{
+  1, 3, 3,
+  2, 4, 4,
+  2, 3, 4,
+  3, 5, 5
+};
+
+/* Cycle count when memory is the destination operand is one larger than above
+   for instructions that aren't MOV, BIT or CMP.  */
+static const struct double_op_cost cycle_cost_double_op =
+{
+  1, 3, 4,
+  2, 4, 5,
+  2, 3, 5,
+  3, 5, 6
+};
+
+static const struct double_op_cost size_cost_double_op =
+{
+  1, 1, 2,
+  1, 1, 2,
+  2, 2, 3,
+  2, 2, 3
+};
+
+/* TARGET_REGISTER_MOVE_COST
+   There is only one class of general-purpose, non-fixed registers, and the
+   relative cost of moving data between them is always the same.
+   Therefore, the default of 2 is optimal.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
+
+/* Return the cost of moving data between registers and memory.
+   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
+   of 2.
+   IN is false if the value is to be written to memory.  */
+static int
+msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+			 reg_class_t rclass ATTRIBUTE_UNUSED,
+			 bool in)
+{
+  int cost;
+  const struct double_op_cost *cost_p;
+  /* Optimize with a code size focus by default, unless -O2 or above is
+     specified.  */
+  bool speed = (!optimize_size && optimize >= 2);
+
+  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
+
+  if (in)
+    /* Reading from memory using indirect addressing is assumed to be the more
+       common case.  */
+    cost = cost_p->ind2r;
+  else
+    cost = cost_p->r2m;
+
+  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
+     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
+  return 2 * cost;
+}
+
+/* BRANCH_COST
+   Changing from the default of 1 doesn't affect code generation, presumably
+   because there are no conditional move insns - when a condition is involved,
+   the only option is to use a cbranch.  */
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-- 
2.27.0


[-- Attachment #3: 0002-MSP430-Implement-TARGET_RTX_COSTS.patch --]
[-- Type: text/plain, Size: 19795 bytes --]

From 4b4bc7c51bbea79abe3095d4b0cf562556419f8c Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:59 +0100
Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.

gcc/ChangeLog:

	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
	declaration.
	Remove unused argument.
	(struct msp430_multlib_costs): New struct.
	(msp430_is_mem_indirect): New function.
	(msp430_costs): Likewise.
	(msp430_shift_costs): Likewise.
	(msp430_muldiv_costs): Likewise.
	(msp430_get_inner_dest_code): Likewise.
	(msp430_single_op_cost): Likewise.
	(msp430_rtx_costs): Rewrite from scratch.
	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
---
 gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 530 insertions(+), 15 deletions(-)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 9e739233fa0..81ee5075a57 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -49,6 +49,9 @@
 #include "msp430-devices.h"
 #include "incpath.h"
 #include "prefix.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -56,6 +59,7 @@
 
 static void msp430_compute_frame_info (void);
 static bool use_32bit_hwmult (void);
+static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
 
 \f
 
@@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
   2, 2, 3
 };
 
+struct msp430_multlib_costs
+{
+  const int mulhi;
+  const int mulsi;
+  const int muldi;
+};
+
+/* There is no precise size cost when using libcalls, instead it is disparaged
+   relative to other instructions.
+   The cycle costs are from the CALL to the RET, inclusive.
+   FIXME muldi cost is not accurate.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
+{
+  27, 33, 66
+};
+
+/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
+{
+  27, 42, 66
+};
+
 /* TARGET_REGISTER_MOVE_COST
    There is only one class of general-purpose, non-fixed registers, and the
    relative cost of moving data between them is always the same.
@@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
    because there are no conditional move insns - when a condition is involved,
    the only option is to use a cbranch.  */
 
+/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
+   reference, @Rn or @Rn+.  */
+static bool
+msp430_is_mem_indirect (rtx x)
+{
+  gcc_assert (GET_CODE (x) == MEM);
+  rtx op0 = XEXP (x, 0);
+  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
+}
+
+/* Costs of MSP430 instructions are generally based on the addressing mode
+   combination of the source and destination operands.
+   Given source operand SRC (which may be NULL to indicate a single-operand
+   instruction) and destination operand DST return the cost of this
+   expression.  */
+static int
+msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  machine_mode outer_mode = GET_MODE (outer_rtx);
+  const struct double_op_cost *cost_p;
+  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  if (outer_code == TRUNCATE
+      && (outer_mode == QImode
+	  || outer_mode == HImode
+	  || outer_mode == PSImode))
+    /* Truncation to these modes is normally free as a side effect of the
+       instructions themselves.  */
+    return 0;
+
+  if (dst_code == SYMBOL_REF
+      || dst_code == LABEL_REF
+      || dst_code == CONST_INT)
+    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
+    return 0;
+
+  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
+    {
+      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+      fprintf (stderr, "from:\n");
+      debug_rtx (outer_rtx);
+    }
+
+  switch (src_code)
+    {
+    case REG:
+      return (dst_code == REG ? cost_p->r2r
+	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
+
+    case CONST_INT:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      return (dst_code == REG ? cost_p->imm2r
+	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
+
+
+    case MEM:
+      if (msp430_is_mem_indirect (src))
+	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
+						   ? cost_p->ind2pc
+						   : cost_p->ind2m));
+      else
+	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
+						   ? cost_p->mem2pc
+						   : cost_p->mem2m));
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
+	  debug_rtx (src);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem2m;
+    }
+}
+
+/* Given source operand SRC and destination operand DST from the shift or
+   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  int amt;
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+
+  if (debug_rtx_costs
+      && dst_code != REG
+      && dst_code != MEM
+      && dst_code != CONST
+      && dst_code != SYMBOL_REF
+      && dst_code != CONST_INT)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+    }
+
+  if (debug_rtx_costs
+      && src_code != CONST_INT
+      && src_code != REG)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
+      debug_rtx (src);
+    }
+
+  if (src_code != CONST_INT)
+    /* The size or speed cost when the shift amount is unknown cannot be
+       accurately calculated, so just disparage it slightly.  */
+    return 2 * msp430_costs (src, dst, speed, outer_rtx);
+
+  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
+    {
+      /* GCC sometimes tries to perform shifts in some very inventive ways,
+	 resulting in much larger code size usage than necessary, if
+	 they are disparaged too much here.  So in general, if
+	 use_helper_for_const_shift thinks a helper should be used, obey
+	 that and don't disparage the shift any more than a regular
+	 instruction, even though the shift may actually cost more.
+	 This ensures that the RTL generated at the initial expand pass has the
+	 expected shift instructions, which can be mapped to the helper
+	 functions.  */
+      return msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  if (!msp430x)
+    {
+      /* Each shift by one place will be emitted individually.  */
+      switch (dst_code)
+	{
+	case REG:
+	case CONST_INT:
+	  return amt * cost_p->reg;
+	case MEM:
+	  if (msp430_is_mem_indirect (dst))
+	    return amt * cost_p->ind;
+	  else
+	    return amt * cost_p->mem;
+	default:
+	  return amt * cost_p->mem;
+	}
+    }
+
+  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
+     versions are used.
+     Instructions which shift a MEM operand will never actually be output.  It
+     will always be copied into a register to allow for efficient shifting.  So
+     the cost just takes into account the cost of an additional copy in that
+     case.  */
+  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
+	  + (dst_code == REG ? 0
+	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
+}
+
+/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
+   RTX OUTER_RTX, return the cost of performing that operation, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
+		     machine_mode outer_mode)
+{
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  const struct msp430_multlib_costs *cost_p;
+  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
+						 || use_32bit_hwmult ()));
+  cost_p = (hwmult_16bit
+	    ? &cycle_cost_multlib_32bit
+	    : &cycle_cost_multlib_16bit);
+
+  int factor = 1;
+  /* Only used in some calculations.  */
+  int mode_factor = 1;
+  if (outer_mode == SImode)
+    mode_factor = 2;
+  else if (outer_mode == PSImode)
+    /* PSImode multiplication is performed using SImode operands, so has extra
+       cost to factor in the conversions necessary before/after the
+       operation.  */
+    mode_factor = 3;
+  else if (outer_mode == DImode)
+    mode_factor = 4;
+
+  if (!speed)
+    {
+      /* The codesize cost of using a helper function to perform the
+	 multiplication or division cannot be accurately calculated, since the
+	 cost depends on how many times the operation is performed in the
+	 entire program.  */
+      if (outer_code != MULT)
+	/* Division is always expensive.  */
+	factor = 7;
+      else if (((hwmult_16bit && outer_mode != DImode)
+		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
+	/* When the hardware multiplier is available, only disparage
+	   slightly.  */
+	factor = 2;
+      else
+	factor = 5;
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  /* When there is hardware multiply support, there is a relatively low, fixed
+     cycle cost to performing any multiplication, but when there is no hardware
+     multiply support it is very costly.  That precise cycle cost has not been
+     calculated here.
+     Division is extra slow since it always uses a software library.
+     The 16-bit hardware multiply library cannot be used to produce 64-bit
+     results.  */
+  if (outer_code != MULT || !msp430_has_hwmult ()
+      || (outer_mode == DImode && hwmult_16bit))
+    {
+      factor = (outer_code == MULT ? 50 : 70);
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  switch (outer_mode)
+    {
+    case E_QImode:
+    case E_HImode:
+      /* Include the cost of copying the operands into and out of the hardware
+	 multiply routine.  */
+      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
+
+    case E_PSImode:
+      /* Extra factor for the conversions necessary to do PSI->SI before the
+	 operation.  */
+      factor = 2;
+      /* fallthru.  */
+    case E_SImode:
+      return factor * (cost_p->mulsi
+		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
+
+    case E_DImode:
+    default:
+      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
+    }
+}
+
+/* Recurse within X to find the actual destination operand of the expression.
+   For example:
+   (plus (ashift (minus (ashift (reg)
+   (const_int) ......
+   should return the reg RTX.  */
+static rtx
+msp430_get_inner_dest_code (rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx op0 = XEXP (x, 0);
+  switch (code)
+    {
+    case REG:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case LABEL_REF:
+      return x;
+
+    case MEM:
+      /* Return the MEM expr not the inner REG for these cases.  */
+      switch (GET_CODE (op0))
+	{
+	case REG:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	case CONST:
+	case POST_INC:
+	  return x;
+
+	case PLUS:
+	  /* return MEM (PLUS (REG) (CONST)) */
+	  if (GET_CODE (XEXP (op0, 0)) == REG)
+	    {
+	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+		  || GET_CODE (XEXP (op0, 1)) == CONST
+		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
+		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
+		return x;
+	      else
+		return msp430_get_inner_dest_code (op0);
+	    }
+	  return msp430_get_inner_dest_code (op0);
+
+	default:
+	  if (GET_RTX_FORMAT (code)[0] != 'e')
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+      break;
+
+    default:
+      if (op0 == NULL_RTX)
+	gcc_unreachable ();
+      else
+	{
+	  if (GET_RTX_FORMAT (code)[0] != 'e'
+	      && code != ENTRY_VALUE)
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+    }
+}
+
+/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
+   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
+static int
+msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+  const struct double_op_cost *double_op_cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  switch (dst_code)
+    {
+    case REG:
+      return cost_p->reg;
+    case MEM:
+      if (msp430_is_mem_indirect (dst))
+	return cost_p->ind;
+      else
+	return cost_p->mem;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+      /* A constant value would need to be copied into a register first.  */
+      return double_op_cost_p->imm2r + cost_p->reg;
+
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_single_op_cost unhandled "
+		   "dst operand:\n");
+	  debug_rtx (dst);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem;
+    }
+}
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
-			      machine_mode mode,
-			      int	   outer_code ATTRIBUTE_UNUSED,
-			      int	   opno ATTRIBUTE_UNUSED,
-			      int *	   total,
-			      bool	   speed ATTRIBUTE_UNUSED)
+/* This target hook describes the relative costs of RTL expressions.
+   The function recurses to just before the lowest level of the expression,
+   when both of the operands of the expression can be examined at the same time.
+   This is because the cost of the expression depends on the specific
+   addressing mode combination of the operands.
+   The hook returns true when all subexpressions of X have been processed, and
+   false when rtx_cost should recurse.  */
+static bool
+msp430_rtx_costs (rtx x,
+		  machine_mode mode,
+		  int	   outer_code ATTRIBUTE_UNUSED,
+		  int	   opno ATTRIBUTE_UNUSED,
+		  int *	   total,
+		  bool	   speed)
 {
-  int code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+  rtx dst, src;
+  rtx dst_inner, src_inner;
+
+  *total = 0;
+  dst = XEXP (x, 0);
+  if (GET_RTX_LENGTH (code) == 1)
+    /* Some RTX that are single-op in GCC are double-op when translated to
+       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
+    src = dst;
+  else
+    src = XEXP (x, 1);
+
 
   switch (code)
     {
+    case SET:
+      /* Ignoring SET improves codesize.  */
+      if (!speed)
+	return true;
+      /* fallthru.  */
+    case PLUS:
+      if (outer_code == MEM)
+	/* Do not add any cost for the plus itself, but recurse in case there
+	   are more complicated RTX inside.  */
+	return false;
+      /* fallthru.  */
+    case MINUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case NOT:
+    case ZERO_EXTEND:
+    case TRUNCATE:
+    case NEG:
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case IF_THEN_ELSE:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case ROTATE:
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
+						  speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case MULT:
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
+						   x, mode));
+      return false;
+
+    case CALL:
     case SIGN_EXTEND:
-      if (mode == SImode && outer_code == SET)
+      dst_inner = msp430_get_inner_dest_code (dst);
+      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
+    case REG:
+    case PC:
+    case POST_INC:
+      if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      return true;
+
+    case MEM:
+      /* PSImode operands are expensive when in memory.  */
+      if (mode == PSImode)
+	*total = COSTS_N_INSNS (1);
+      else if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      /* Recurse into the MEM.  */
+      return false;
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+      /* Conditions are mostly equivalent, changing their relative
+	 costs has no effect.  */
+      return false;
+
+    case ASM_OPERANDS:
+    case ASM_INPUT:
+    case CLOBBER:
+    case COMPARE:
+    case CONCAT:
+    case ENTRY_VALUE:
+      /* Other unhandled expressions.  */
+      return false;
+
+    default:
+      if (debug_rtx_costs)
 	{
-	  *total = COSTS_N_INSNS (4);
-	  return true;
+	  fprintf (stderr, "\nUnhandled RTX\n");
+	  debug_rtx (x);
 	}
-      break;
+      return false;
     }
-  return false;
 }
 \f
 /* Function Entry and Exit */
@@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
 /* Return TRUE if the helper function should be used and FALSE if the shifts
    insns should be emitted inline.  */
 static bool
-use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
-			    HOST_WIDE_INT amt)
+use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
 {
   const int default_inline_shift = 4;
   /* We initialize the option to 65 so we know if the user set it or not.  */
@@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
      the heuristic accordingly.  */
   int max_inline_32 = max_inline / 2;
 
+  if (mode == E_DImode)
+    return true;
+
   /* Don't use helpers for these modes on 430X, when optimizing for speed, or
      when emitting a small number of insns.  */
   if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
@@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
      constant.  */
   if (!CONST_INT_P (operands[2])
       || mode == E_DImode
-      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
+      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
     {
       const char *helper_name = NULL;
       /* The const variants of mspabi shifts have significantly larger code
-- 
2.27.0


[-- Attachment #4: 0003-MSP430-Add-defaulting-to-the-insn-length-attribute.patch --]
[-- Type: text/plain, Size: 47996 bytes --]

From e8f2aacef2b740e8123b6450f2addc16d197c464 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(-)

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)"))
-- 
2.27.0


[-- Attachment #5: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7678 bytes --]

From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.h (BRANCH_COST): Define.
	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 25 ++++++++---
 gcc/config/msp430/msp430.h                    |  8 ++++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 189 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..35ccd2817ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
   return 2 * cost;
 }
 
-/* BRANCH_COST
-   Changing from the default of 1 doesn't affect code generation, presumably
-   because there are no conditional move insns - when a condition is involved,
-   the only option is to use a cbranch.  */
-
 /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
    reference, @Rn or @Rn+.  */
 static bool
@@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (get_attr_length (insn) / 2);
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b813e825311..830101408a5 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
 #define HAS_LONG_COND_BRANCH		0
 #define HAS_LONG_UNCOND_BRANCH		0
 
+/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
+#define BRANCH_COST(speed_p, predictable_p) 3
+
+/* Override the default BRANCH_COST heuristic to indicate that it is preferable
+   to retain short-circuit operations, this results in significantly better
+   codesize and performance.  */
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
 #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
 #define WORD_REGISTER_OPERATIONS	1
 
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


[-- Attachment #6: 0005-MSP430-Skip-index-1.c-test.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

From 59fa213daac1cedd6d1564dfeacb1f6ce9397e98 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:35:25 +0100
Subject: [PATCH 5/5] MSP430: Skip index-1.c test

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
---
 gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
index b00090d834a..d96be4c77b8 100644
--- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
@@ -1,3 +1,5 @@
+/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
+
 int a[] =
 {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* ping x4 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-10-14 15:31     ` ping x3 " Jozef Lawrynowicz
@ 2020-11-06 20:51       ` Jozef Lawrynowicz
  0 siblings, 0 replies; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-11-06 20:51 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2625 bytes --]

4th ping for below. Patches are attached, OP linked here:
https://gcc.gnu.org/pipermail/gcc-patches/2020-July/550542.html

Thanks,
Jozef

On Wed, Oct 14, 2020 at 04:31:30PM +0100, Jozef Lawrynowicz wrote:
> 3rd ping for below.
> 
> On Tue, Sep 15, 2020 at 09:30:22PM +0100, Jozef Lawrynowicz wrote:
> > Ping x2 for below.
> > 
> > On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
> > > Pinging for this series of patches.
> > > Attached all patches to this mail with the ammended patch 4 thanks to
> > > Segher's review.
> > > 
> > > Thanks,
> > > Jozef
> > > 
> > > On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
> > > > The following series of patches for MSP430 implement some of the target
> > > > macros used to determine the relative costs of operations.
> > > > 
> > > > To give an indication of the overall effect of these changes on
> > > > codesize, below are some size statistics collected from all the
> > > > executable files from execute.exp that are built at -Os.
> > > > There are around 1470 such tests (depending on the configuration).
> > > > 
> > > > The percentage change (((new - old)/old) * 100) in text size is calculated
> > > > for each test and the given metric is applied to that overall set of data.
> > > > 
> > > > Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
> > > > -----------------------------------------------------------------------------
> > > > -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
> > > > -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
> > > > -mlarge       |  -1.7    |   -1.9     |      1412         |      37
> > > > 
> > > > Successfully regtested on trunk for msp430-elf, ok to apply?
> > > > 
> > > > Jozef Lawrynowicz (5):
> > > >   MSP430: Implement TARGET_MEMORY_MOVE_COST
> > > >   MSP430: Implement TARGET_RTX_COSTS
> > > >   MSP430: Add defaulting to the insn length attribute
> > > >   MSP430: Implement TARGET_INSN_COST
> > > >   MSP430: Skip index-1.c test
> > > > 
> > > >  gcc/config/msp430/msp430-protos.h             |   5 +-
> > > >  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
> > > >  gcc/config/msp430/msp430.h                    |  13 +
> > > >  gcc/config/msp430/msp430.md                   | 439 +++++++--
> > > >  gcc/config/msp430/msp430.opt                  |   4 +
> > > >  gcc/config/msp430/predicates.md               |  13 +
> > > >  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
> > > >  7 files changed, 1206 insertions(+), 137 deletions(-)
> > > > 
> > > > -- 
> > > > 2.27.0
> > > > 
> > 

[-- Attachment #2: 0001-MSP430-Implement-TARGET_MEMORY_MOVE_COST.patch --]
[-- Type: text/plain, Size: 5197 bytes --]

From e260de5a31e661afdfaaf2c8053b574a292d6826 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:11 +0100
Subject: [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST

The cycle and size cost of a MOV instruction in different addressing
modes can be used to calculate the TARGET_MEMORY_MOVE_COST relative to
TARGET_REGISTER_MOVE_COST.

gcc/ChangeLog:

	* config/msp430/msp430.c (struct single_op_cost): New struct.
	(struct double_op_cost): Likewise.
	(TARGET_REGISTER_MOVE_COST): Don't define but add comment.
	(TARGET_MEMORY_MOVE_COST): Define to...
	(msp430_memory_move_cost): New function.
	(BRANCH_COST): Don't define but add comment.
---
 gcc/config/msp430/msp430.c | 131 +++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index c2b24974364..9e739233fa0 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1043,6 +1043,137 @@ msp430_legitimate_constant (machine_mode mode, rtx x)
 }
 
 \f
+/* Describing Relative Costs of Operations
+   To model the cost of an instruction, use the number of cycles when
+   optimizing for speed, and the number of words when optimizing for size.
+   The cheapest instruction will execute in one cycle and cost one word.
+   The cycle and size costs correspond to 430 ISA instructions, not 430X
+   instructions or 430X "address" instructions.  The relative costs of 430X
+   instructions is accurately modeled with the 430 costs.  The relative costs
+   of some "address" instructions can differ, but these are not yet handled.
+   Adding support for this could improve performance/code size.  */
+
+const int debug_rtx_costs = 0;
+
+struct single_op_cost
+{
+  const int reg;
+  /* Indirect register (@Rn) or indirect autoincrement (@Rn+).  */
+  const int ind;
+  const int mem;
+};
+
+static const struct single_op_cost cycle_cost_single_op =
+{
+  1, 3, 4
+};
+
+static const struct single_op_cost size_cost_single_op =
+{
+  1, 1, 2
+};
+
+/* When the destination of an insn is memory, the cost is always the same
+   regardless of whether that memory is accessed using indirect register,
+   indexed or absolute addressing.
+   When the source operand is memory, indirect register and post-increment have
+   the same cost, which is lower than indexed and absolute, which also have
+   the same cost.  */
+struct double_op_cost
+{
+  /* Source operand is a register.  */
+  const int r2r;
+  const int r2pc;
+  const int r2m;
+
+  /* Source operand is memory, using indirect register (@Rn) or indirect
+     autoincrement (@Rn+) addressing modes.  */
+  const int ind2r;
+  const int ind2pc;
+  const int ind2m;
+
+  /* Source operand is an immediate.  */
+  const int imm2r;
+  const int imm2pc;
+  const int imm2m;
+
+  /* Source operand is memory, using indexed (x(Rn)) or absolute (&ADDR)
+     addressing modes.  */
+  const int mem2r;
+  const int mem2pc;
+  const int mem2m;
+};
+
+/* These structures describe the cost of MOV, BIT and CMP instructions, in terms
+   of clock cycles or words.  */
+static const struct double_op_cost cycle_cost_double_op_mov =
+{
+  1, 3, 3,
+  2, 4, 4,
+  2, 3, 4,
+  3, 5, 5
+};
+
+/* Cycle count when memory is the destination operand is one larger than above
+   for instructions that aren't MOV, BIT or CMP.  */
+static const struct double_op_cost cycle_cost_double_op =
+{
+  1, 3, 4,
+  2, 4, 5,
+  2, 3, 5,
+  3, 5, 6
+};
+
+static const struct double_op_cost size_cost_double_op =
+{
+  1, 1, 2,
+  1, 1, 2,
+  2, 2, 3,
+  2, 2, 3
+};
+
+/* TARGET_REGISTER_MOVE_COST
+   There is only one class of general-purpose, non-fixed registers, and the
+   relative cost of moving data between them is always the same.
+   Therefore, the default of 2 is optimal.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST msp430_memory_move_cost
+
+/* Return the cost of moving data between registers and memory.
+   The returned cost must be relative to the default TARGET_REGISTER_MOVE_COST
+   of 2.
+   IN is false if the value is to be written to memory.  */
+static int
+msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+			 reg_class_t rclass ATTRIBUTE_UNUSED,
+			 bool in)
+{
+  int cost;
+  const struct double_op_cost *cost_p;
+  /* Optimize with a code size focus by default, unless -O2 or above is
+     specified.  */
+  bool speed = (!optimize_size && optimize >= 2);
+
+  cost_p = (speed ? &cycle_cost_double_op_mov : &size_cost_double_op);
+
+  if (in)
+    /* Reading from memory using indirect addressing is assumed to be the more
+       common case.  */
+    cost = cost_p->ind2r;
+  else
+    cost = cost_p->r2m;
+
+  /* All register to register moves cost 1 cycle or 1 word, so multiply by 2
+     to get the costs relative to TARGET_REGISTER_MOVE_COST of 2.  */
+  return 2 * cost;
+}
+
+/* BRANCH_COST
+   Changing from the default of 1 doesn't affect code generation, presumably
+   because there are no conditional move insns - when a condition is involved,
+   the only option is to use a cbranch.  */
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-- 
2.27.0


[-- Attachment #3: 0002-MSP430-Implement-TARGET_RTX_COSTS.patch --]
[-- Type: text/plain, Size: 19795 bytes --]

From 4b4bc7c51bbea79abe3095d4b0cf562556419f8c Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:28:59 +0100
Subject: [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS

Costs of MSP430 instructions are mostly just a function of the type and number
of operands. In these cases, TARGET_RTX_COSTS just needs to examine the
operands to calculate the cost of the expression.

For more complicated operations where library helper functions are required,
if the cost cannot be accurately calculated, it is estimated and
disparaged relative to the cost of a single instruction.

gcc/ChangeLog:

	* config/msp430/msp430.c (use_helper_for_const_shift): Add forward
	declaration.
	Remove unused argument.
	(struct msp430_multlib_costs): New struct.
	(msp430_is_mem_indirect): New function.
	(msp430_costs): Likewise.
	(msp430_shift_costs): Likewise.
	(msp430_muldiv_costs): Likewise.
	(msp430_get_inner_dest_code): Likewise.
	(msp430_single_op_cost): Likewise.
	(msp430_rtx_costs): Rewrite from scratch.
	(msp430_expand_shift): Adjust use_helper_for_const_shift call.
---
 gcc/config/msp430/msp430.c | 545 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 530 insertions(+), 15 deletions(-)

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 9e739233fa0..81ee5075a57 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -49,6 +49,9 @@
 #include "msp430-devices.h"
 #include "incpath.h"
 #include "prefix.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -56,6 +59,7 @@
 
 static void msp430_compute_frame_info (void);
 static bool use_32bit_hwmult (void);
+static bool use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt);
 
 \f
 
@@ -1132,6 +1136,28 @@ static const struct double_op_cost size_cost_double_op =
   2, 2, 3
 };
 
+struct msp430_multlib_costs
+{
+  const int mulhi;
+  const int mulsi;
+  const int muldi;
+};
+
+/* There is no precise size cost when using libcalls, instead it is disparaged
+   relative to other instructions.
+   The cycle costs are from the CALL to the RET, inclusive.
+   FIXME muldi cost is not accurate.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_32bit =
+{
+  27, 33, 66
+};
+
+/* 32bit multiply takes a few more instructions on 16bit hwmult.  */
+static const struct msp430_multlib_costs cycle_cost_multlib_16bit =
+{
+  27, 42, 66
+};
+
 /* TARGET_REGISTER_MOVE_COST
    There is only one class of general-purpose, non-fixed registers, and the
    relative cost of moving data between them is always the same.
@@ -1174,29 +1200,516 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
    because there are no conditional move insns - when a condition is involved,
    the only option is to use a cbranch.  */
 
+/* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
+   reference, @Rn or @Rn+.  */
+static bool
+msp430_is_mem_indirect (rtx x)
+{
+  gcc_assert (GET_CODE (x) == MEM);
+  rtx op0 = XEXP (x, 0);
+  return (GET_CODE (op0) == REG || GET_CODE (op0) == POST_INC);
+}
+
+/* Costs of MSP430 instructions are generally based on the addressing mode
+   combination of the source and destination operands.
+   Given source operand SRC (which may be NULL to indicate a single-operand
+   instruction) and destination operand DST return the cost of this
+   expression.  */
+static int
+msp430_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  machine_mode outer_mode = GET_MODE (outer_rtx);
+  const struct double_op_cost *cost_p;
+  cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  if (outer_code == TRUNCATE
+      && (outer_mode == QImode
+	  || outer_mode == HImode
+	  || outer_mode == PSImode))
+    /* Truncation to these modes is normally free as a side effect of the
+       instructions themselves.  */
+    return 0;
+
+  if (dst_code == SYMBOL_REF
+      || dst_code == LABEL_REF
+      || dst_code == CONST_INT)
+    /* Catch RTX like (minus (const_int 0) (reg)) but don't add any cost.  */
+    return 0;
+
+  if (debug_rtx_costs && dst_code != REG && dst_code != MEM && dst_code != PC)
+    {
+      fprintf (stderr, "msp430_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+      fprintf (stderr, "from:\n");
+      debug_rtx (outer_rtx);
+    }
+
+  switch (src_code)
+    {
+    case REG:
+      return (dst_code == REG ? cost_p->r2r
+	      : (dst_code == PC ? cost_p->r2pc : cost_p->r2m));
+
+    case CONST_INT:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      return (dst_code == REG ? cost_p->imm2r
+	      : (dst_code == PC ? cost_p->imm2pc : cost_p->imm2m));
+
+
+    case MEM:
+      if (msp430_is_mem_indirect (src))
+	return (dst_code == REG ? cost_p->ind2r : (dst_code == PC
+						   ? cost_p->ind2pc
+						   : cost_p->ind2m));
+      else
+	return (dst_code == REG ? cost_p->mem2r	: (dst_code == PC
+						   ? cost_p->mem2pc
+						   : cost_p->mem2m));
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_costs unhandled src operand:\n");
+	  debug_rtx (src);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem2m;
+    }
+}
+
+/* Given source operand SRC and destination operand DST from the shift or
+   rotate RTX OUTER_RTX, return the cost of performing that shift, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_shift_costs (rtx src, rtx dst, bool speed, rtx outer_rtx)
+{
+  int amt;
+  enum rtx_code src_code = GET_CODE (src);
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+
+  if (debug_rtx_costs
+      && dst_code != REG
+      && dst_code != MEM
+      && dst_code != CONST
+      && dst_code != SYMBOL_REF
+      && dst_code != CONST_INT)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled dst operand:\n");
+      debug_rtx (dst);
+    }
+
+  if (debug_rtx_costs
+      && src_code != CONST_INT
+      && src_code != REG)
+    {
+      fprintf (stderr, "msp430_shift_costs unhandled src operand:\n");
+      debug_rtx (src);
+    }
+
+  if (src_code != CONST_INT)
+    /* The size or speed cost when the shift amount is unknown cannot be
+       accurately calculated, so just disparage it slightly.  */
+    return 2 * msp430_costs (src, dst, speed, outer_rtx);
+
+  if (use_helper_for_const_shift (GET_MODE (outer_rtx), amt = INTVAL (src)))
+    {
+      /* GCC sometimes tries to perform shifts in some very inventive ways,
+	 resulting in much larger code size usage than necessary, if
+	 they are disparaged too much here.  So in general, if
+	 use_helper_for_const_shift thinks a helper should be used, obey
+	 that and don't disparage the shift any more than a regular
+	 instruction, even though the shift may actually cost more.
+	 This ensures that the RTL generated at the initial expand pass has the
+	 expected shift instructions, which can be mapped to the helper
+	 functions.  */
+      return msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  if (!msp430x)
+    {
+      /* Each shift by one place will be emitted individually.  */
+      switch (dst_code)
+	{
+	case REG:
+	case CONST_INT:
+	  return amt * cost_p->reg;
+	case MEM:
+	  if (msp430_is_mem_indirect (dst))
+	    return amt * cost_p->ind;
+	  else
+	    return amt * cost_p->mem;
+	default:
+	  return amt * cost_p->mem;
+	}
+    }
+
+  /* RRAM, RRCM, RRUM, RLAM are used for shift counts <= 4, otherwise, the 'X'
+     versions are used.
+     Instructions which shift a MEM operand will never actually be output.  It
+     will always be copied into a register to allow for efficient shifting.  So
+     the cost just takes into account the cost of an additional copy in that
+     case.  */
+  return (amt <= 4 ? (speed ? amt : 1) : (speed ? amt + 1 : 2)
+	  + (dst_code == REG ? 0
+	     : msp430_costs (dst, gen_rtx_REG (HImode, 10), speed, outer_rtx)));
+}
+
+/* Given source operand SRC and destination operand DST from the MULT/DIV/MOD
+   RTX OUTER_RTX, return the cost of performing that operation, assuming
+   optimization for speed when SPEED is true.  */
+static int
+msp430_muldiv_costs (rtx src, rtx dst, bool speed, rtx outer_rtx,
+		     machine_mode outer_mode)
+{
+  enum rtx_code outer_code = GET_CODE (outer_rtx);
+  const struct msp430_multlib_costs *cost_p;
+  bool hwmult_16bit = (msp430_has_hwmult () && !(msp430_use_f5_series_hwmult ()
+						 || use_32bit_hwmult ()));
+  cost_p = (hwmult_16bit
+	    ? &cycle_cost_multlib_32bit
+	    : &cycle_cost_multlib_16bit);
+
+  int factor = 1;
+  /* Only used in some calculations.  */
+  int mode_factor = 1;
+  if (outer_mode == SImode)
+    mode_factor = 2;
+  else if (outer_mode == PSImode)
+    /* PSImode multiplication is performed using SImode operands, so has extra
+       cost to factor in the conversions necessary before/after the
+       operation.  */
+    mode_factor = 3;
+  else if (outer_mode == DImode)
+    mode_factor = 4;
+
+  if (!speed)
+    {
+      /* The codesize cost of using a helper function to perform the
+	 multiplication or division cannot be accurately calculated, since the
+	 cost depends on how many times the operation is performed in the
+	 entire program.  */
+      if (outer_code != MULT)
+	/* Division is always expensive.  */
+	factor = 7;
+      else if (((hwmult_16bit && outer_mode != DImode)
+		   || use_32bit_hwmult () || msp430_use_f5_series_hwmult ()))
+	/* When the hardware multiplier is available, only disparage
+	   slightly.  */
+	factor = 2;
+      else
+	factor = 5;
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  /* When there is hardware multiply support, there is a relatively low, fixed
+     cycle cost to performing any multiplication, but when there is no hardware
+     multiply support it is very costly.  That precise cycle cost has not been
+     calculated here.
+     Division is extra slow since it always uses a software library.
+     The 16-bit hardware multiply library cannot be used to produce 64-bit
+     results.  */
+  if (outer_code != MULT || !msp430_has_hwmult ()
+      || (outer_mode == DImode && hwmult_16bit))
+    {
+      factor = (outer_code == MULT ? 50 : 70);
+      return factor * mode_factor * msp430_costs (src, dst, speed, outer_rtx);
+    }
+
+  switch (outer_mode)
+    {
+    case E_QImode:
+    case E_HImode:
+      /* Include the cost of copying the operands into and out of the hardware
+	 multiply routine.  */
+      return cost_p->mulhi + (3 * msp430_costs (src, dst, speed, outer_rtx));
+
+    case E_PSImode:
+      /* Extra factor for the conversions necessary to do PSI->SI before the
+	 operation.  */
+      factor = 2;
+      /* fallthru.  */
+    case E_SImode:
+      return factor * (cost_p->mulsi
+		       + (6 * msp430_costs (src, dst, speed, outer_rtx)));
+
+    case E_DImode:
+    default:
+      return cost_p->muldi + (12 * msp430_costs (src, dst, speed, outer_rtx));
+    }
+}
+
+/* Recurse within X to find the actual destination operand of the expression.
+   For example:
+   (plus (ashift (minus (ashift (reg)
+   (const_int) ......
+   should return the reg RTX.  */
+static rtx
+msp430_get_inner_dest_code (rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx op0 = XEXP (x, 0);
+  switch (code)
+    {
+    case REG:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case LABEL_REF:
+      return x;
+
+    case MEM:
+      /* Return the MEM expr not the inner REG for these cases.  */
+      switch (GET_CODE (op0))
+	{
+	case REG:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	case CONST:
+	case POST_INC:
+	  return x;
+
+	case PLUS:
+	  /* return MEM (PLUS (REG) (CONST)) */
+	  if (GET_CODE (XEXP (op0, 0)) == REG)
+	    {
+	      if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+		  || GET_CODE (XEXP (op0, 1)) == CONST
+		  || GET_CODE (XEXP (op0, 1)) == LABEL_REF
+		  || GET_CODE (XEXP (op0, 1)) == SYMBOL_REF)
+		return x;
+	      else
+		return msp430_get_inner_dest_code (op0);
+	    }
+	  return msp430_get_inner_dest_code (op0);
+
+	default:
+	  if (GET_RTX_FORMAT (code)[0] != 'e')
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+      break;
+
+    default:
+      if (op0 == NULL_RTX)
+	gcc_unreachable ();
+      else
+	{
+	  if (GET_RTX_FORMAT (code)[0] != 'e'
+	      && code != ENTRY_VALUE)
+	    return x;
+	  return msp430_get_inner_dest_code (op0);
+	}
+    }
+}
+
+/* Calculate the cost of an MSP430 single-operand instruction, for operand DST
+   within the RTX OUTER_RTX, optimizing for speed if SPEED is true.  */
+static int
+msp430_single_op_cost (rtx dst, bool speed, rtx outer_rtx)
+{
+  enum rtx_code dst_code = GET_CODE (dst);
+  const struct single_op_cost *cost_p;
+  const struct double_op_cost *double_op_cost_p;
+
+  cost_p = (speed ? &cycle_cost_single_op : &size_cost_single_op);
+  double_op_cost_p = (speed ? &cycle_cost_double_op : &size_cost_double_op);
+
+  switch (dst_code)
+    {
+    case REG:
+      return cost_p->reg;
+    case MEM:
+      if (msp430_is_mem_indirect (dst))
+	return cost_p->ind;
+      else
+	return cost_p->mem;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+      /* A constant value would need to be copied into a register first.  */
+      return double_op_cost_p->imm2r + cost_p->reg;
+
+    default:
+      if (debug_rtx_costs)
+	{
+	  fprintf (stderr, "msp430_single_op_cost unhandled "
+		   "dst operand:\n");
+	  debug_rtx (dst);
+	  fprintf (stderr, "from:\n");
+	  debug_rtx (outer_rtx);
+	}
+      return cost_p->mem;
+    }
+}
+
 #undef  TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS msp430_rtx_costs
 
-static bool msp430_rtx_costs (rtx	   x ATTRIBUTE_UNUSED,
-			      machine_mode mode,
-			      int	   outer_code ATTRIBUTE_UNUSED,
-			      int	   opno ATTRIBUTE_UNUSED,
-			      int *	   total,
-			      bool	   speed ATTRIBUTE_UNUSED)
+/* This target hook describes the relative costs of RTL expressions.
+   The function recurses to just before the lowest level of the expression,
+   when both of the operands of the expression can be examined at the same time.
+   This is because the cost of the expression depends on the specific
+   addressing mode combination of the operands.
+   The hook returns true when all subexpressions of X have been processed, and
+   false when rtx_cost should recurse.  */
+static bool
+msp430_rtx_costs (rtx x,
+		  machine_mode mode,
+		  int	   outer_code ATTRIBUTE_UNUSED,
+		  int	   opno ATTRIBUTE_UNUSED,
+		  int *	   total,
+		  bool	   speed)
 {
-  int code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+  rtx dst, src;
+  rtx dst_inner, src_inner;
+
+  *total = 0;
+  dst = XEXP (x, 0);
+  if (GET_RTX_LENGTH (code) == 1)
+    /* Some RTX that are single-op in GCC are double-op when translated to
+       MSP430 instructions e.g NOT, NEG, ZERO_EXTEND.  */
+    src = dst;
+  else
+    src = XEXP (x, 1);
+
 
   switch (code)
     {
+    case SET:
+      /* Ignoring SET improves codesize.  */
+      if (!speed)
+	return true;
+      /* fallthru.  */
+    case PLUS:
+      if (outer_code == MEM)
+	/* Do not add any cost for the plus itself, but recurse in case there
+	   are more complicated RTX inside.  */
+	return false;
+      /* fallthru.  */
+    case MINUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case NOT:
+    case ZERO_EXTEND:
+    case TRUNCATE:
+    case NEG:
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case IF_THEN_ELSE:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_costs (src_inner, dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case ROTATE:
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_shift_costs (src_inner, dst_inner,
+						  speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case MULT:
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      dst_inner = msp430_get_inner_dest_code (dst);
+      src_inner = msp430_get_inner_dest_code (src);
+      *total = COSTS_N_INSNS (msp430_muldiv_costs (src_inner, dst_inner, speed,
+						   x, mode));
+      return false;
+
+    case CALL:
     case SIGN_EXTEND:
-      if (mode == SImode && outer_code == SET)
+      dst_inner = msp430_get_inner_dest_code (dst);
+      *total = COSTS_N_INSNS (msp430_single_op_cost (dst_inner, speed, x));
+      if (mode == SImode)
+	*total *= 2;
+      if (mode == DImode)
+	*total *= 4;
+      return false;
+
+    case CONST_INT:
+    case CONST_FIXED:
+    case CONST_DOUBLE:
+    case SYMBOL_REF:
+    case CONST:
+    case LABEL_REF:
+    case REG:
+    case PC:
+    case POST_INC:
+      if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      return true;
+
+    case MEM:
+      /* PSImode operands are expensive when in memory.  */
+      if (mode == PSImode)
+	*total = COSTS_N_INSNS (1);
+      else if (mode == SImode)
+	*total = COSTS_N_INSNS (2);
+      else if (mode == DImode)
+	*total = COSTS_N_INSNS (4);
+      /* Recurse into the MEM.  */
+      return false;
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+      /* Conditions are mostly equivalent, changing their relative
+	 costs has no effect.  */
+      return false;
+
+    case ASM_OPERANDS:
+    case ASM_INPUT:
+    case CLOBBER:
+    case COMPARE:
+    case CONCAT:
+    case ENTRY_VALUE:
+      /* Other unhandled expressions.  */
+      return false;
+
+    default:
+      if (debug_rtx_costs)
 	{
-	  *total = COSTS_N_INSNS (4);
-	  return true;
+	  fprintf (stderr, "\nUnhandled RTX\n");
+	  debug_rtx (x);
 	}
-      break;
+      return false;
     }
-  return false;
 }
 \f
 /* Function Entry and Exit */
@@ -2915,8 +3428,7 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
 /* Return TRUE if the helper function should be used and FALSE if the shifts
    insns should be emitted inline.  */
 static bool
-use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
-			    HOST_WIDE_INT amt)
+use_helper_for_const_shift (machine_mode mode, HOST_WIDE_INT amt)
 {
   const int default_inline_shift = 4;
   /* We initialize the option to 65 so we know if the user set it or not.  */
@@ -2927,6 +3439,9 @@ use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
      the heuristic accordingly.  */
   int max_inline_32 = max_inline / 2;
 
+  if (mode == E_DImode)
+    return true;
+
   /* Don't use helpers for these modes on 430X, when optimizing for speed, or
      when emitting a small number of insns.  */
   if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
@@ -2964,7 +3479,7 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
      constant.  */
   if (!CONST_INT_P (operands[2])
       || mode == E_DImode
-      || use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
+      || use_helper_for_const_shift (mode, INTVAL (operands[2])))
     {
       const char *helper_name = NULL;
       /* The const variants of mspabi shifts have significantly larger code
-- 
2.27.0


[-- Attachment #4: 0003-MSP430-Add-defaulting-to-the-insn-length-attribute.patch --]
[-- Type: text/plain, Size: 47996 bytes --]

From e8f2aacef2b740e8123b6450f2addc16d197c464 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(-)

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)"))
-- 
2.27.0


[-- Attachment #5: 0004-MSP430-Implement-TARGET_INSN_COST.patch --]
[-- Type: text/plain, Size: 7678 bytes --]

From 8b69c5a38006d30d001561d47f7ecbd9bd3ead78 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:34:50 +0100
Subject: [PATCH 4/5] MSP430: Implement TARGET_INSN_COST

The length of an insn can be used to calculate its cost, when optimizing for
size. When optimizing for speed, this is a good estimate, since the cycle cost
of an MSP430 instruction increases with its length.

gcc/ChangeLog:

	* config/msp430/msp430.c (TARGET_INSN_COST): Define.
	(msp430_insn_cost): New function.
	* config/msp430/msp430.h (BRANCH_COST): Define.
	(LOGICAL_OP_NON_SHORT_CIRCUIT): Define.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/rtx-cost-O3-default.c: New test.
	* gcc.target/msp430/rtx-cost-O3-f5series.c: New test.
	* gcc.target/msp430/rtx-cost-Os-default.c: New test.
	* gcc.target/msp430/rtx-cost-Os-f5series.c: New test.
---
 gcc/config/msp430/msp430.c                    | 25 ++++++++---
 gcc/config/msp430/msp430.h                    |  8 ++++
 .../gcc.target/msp430/rtx-cost-O3-default.c   | 42 ++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-O3-f5series.c  | 38 ++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-default.c   | 43 +++++++++++++++++++
 .../gcc.target/msp430/rtx-cost-Os-f5series.c  | 38 ++++++++++++++++
 6 files changed, 189 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index b7daafcc11a..35ccd2817ca 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -1195,11 +1195,6 @@ msp430_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
   return 2 * cost;
 }
 
-/* BRANCH_COST
-   Changing from the default of 1 doesn't affect code generation, presumably
-   because there are no conditional move insns - when a condition is involved,
-   the only option is to use a cbranch.  */
-
 /* For X, which must be a MEM RTX, return TRUE if it is an indirect memory
    reference, @Rn or @Rn+.  */
 static bool
@@ -1711,6 +1706,26 @@ msp430_rtx_costs (rtx x,
       return false;
     }
 }
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST msp430_insn_cost
+
+static int
+msp430_insn_cost (rtx_insn *insn, bool speed ATTRIBUTE_UNUSED)
+{
+  if (recog_memoized (insn) < 0)
+    return 0;
+
+  /* The returned cost must be relative to COSTS_N_INSNS (1). An insn with a
+     length of 2 bytes is the smallest possible size and so must be equivalent
+     to COSTS_N_INSNS (1).  */
+  return COSTS_N_INSNS (get_attr_length (insn) / 2);
+
+  /* FIXME Add more detailed costs when optimizing for speed.
+     For now the length of the instruction is a good approximiation and roughly
+     correlates with cycle cost.  */
+}
+
 \f
 /* Function Entry and Exit */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b813e825311..830101408a5 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -245,6 +245,14 @@ extern const char *msp430_get_linker_devices_include_path (int, const char **);
 #define HAS_LONG_COND_BRANCH		0
 #define HAS_LONG_UNCOND_BRANCH		0
 
+/* The cost of a branch sequence is roughly 3 "cheap" instructions.  */
+#define BRANCH_COST(speed_p, predictable_p) 3
+
+/* Override the default BRANCH_COST heuristic to indicate that it is preferable
+   to retain short-circuit operations, this results in significantly better
+   codesize and performance.  */
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
 #define LOAD_EXTEND_OP(M)		ZERO_EXTEND
 #define WORD_REGISTER_OPERATIONS	1
 
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
new file mode 100644
index 00000000000..1bd6a142002
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-default.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -O3.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	CALL.*	#ext
+**	CALL.*	#ext
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
new file mode 100644
index 00000000000..1e48625f2e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-O3-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -O3.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.*	\&res2.*
+** ...
+**	RLA.*RLC.*
+** ...
+**	MOV.*	\&res3.*
+** ...
+**	RLA.*RLC.*
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Emit 7 inline shifts for this.  */
+  res2 *= 128;
+  /* Perform this multiplication inline, using addition and shifts.  */
+  res3 *= 100;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
new file mode 100644
index 00000000000..8f3d1b28049
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-default.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-Os" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and hwmult (none), when compiling at -Os.  */
+
+char arr[2];
+char a;
+char *ptr;
+
+/*
+** foo:
+** ...
+**	MOV.B	\&a, \&arr\+1
+**	MOV.*	#arr\+2, \&ptr
+** ...
+*/
+
+void
+foo (void)
+{
+  arr[1] = a;
+  ptr = arr + 2;
+}
+
+extern void ext (void);
+
+/*
+** bar:
+** ...
+**	MOV.*	#ext, R10
+**	CALL.*	R10
+**	CALL.*	R10
+** ...
+*/
+
+void
+bar (void)
+{
+  ext ();
+  ext ();
+}
diff --git a/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
new file mode 100644
index 00000000000..bb37f9083d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/rtx-cost-Os-f5series.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mhwmult=f5series" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Verify the MSP430 cost model is working as expected for the default ISA
+   (msp430x) and f5series hwmult, when compiling at -Os.  */
+
+volatile unsigned long a;
+volatile unsigned int b;
+volatile unsigned long c;
+unsigned long res1;
+unsigned long res2;
+unsigned long res3;
+
+/*
+** foo:
+** ...
+**	MOV.B	#16, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#7, R14
+**	CALL.*	#__mspabi_slll
+** ...
+**	MOV.B	#100, R14
+**	MOV.B	#0, R15
+** ...
+**	CALL.*	#__mulsi2_f5
+** ...
+*/
+void foo (void)
+{
+  /* Use the shift library function for this.  */
+  res1 = (a << 16) | b;
+  /* Likewise.  */
+  res2 *= 128;
+  /* Use the hardware multiply library function for this.  */
+  res3 *= 100;
+}
-- 
2.27.0


[-- Attachment #6: 0005-MSP430-Skip-index-1.c-test.patch --]
[-- Type: text/plain, Size: 1364 bytes --]

From 59fa213daac1cedd6d1564dfeacb1f6ce9397e98 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 16 Jul 2020 11:35:25 +0100
Subject: [PATCH 5/5] MSP430: Skip index-1.c test

To access the "n - 100000"th element of "a" in this test, GCC will
generate the following code for msp430-elf with -mcpu=msp430x:

  RLAM.W  #1, R12
  MOV.W a-3392(R12), R12

Since there aren't actually 100,000 elements in a, this means that
"a-3392" offset calculated by the linker can overflow, as the address of
"a" can validly be less than 3392.

The relocations used for -mcpu=msp430 and -mlarge are not as strict and
the calculated value is allowed to wrap around the address space,
avoiding relocation overflows.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/index-1.c: Skip for the default MSP430 430X ISA.
---
 gcc/testsuite/gcc.c-torture/execute/index-1.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/index-1.c b/gcc/testsuite/gcc.c-torture/execute/index-1.c
index b00090d834a..d96be4c77b8 100644
--- a/gcc/testsuite/gcc.c-torture/execute/index-1.c
+++ b/gcc/testsuite/gcc.c-torture/execute/index-1.c
@@ -1,3 +1,5 @@
+/* { dg-skip-if "strict reloc overflow checking" { msp430-*-* } { "*" } { "-mcpu=msp430" "-mlarge"} } */
+
 int a[] =
 {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-- 
2.27.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: ping x2 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-09-15 20:30   ` ping x2 " Jozef Lawrynowicz
  2020-10-14 15:31     ` ping x3 " Jozef Lawrynowicz
@ 2020-11-06 20:53     ` Jeff Law
  2020-11-06 21:23       ` Jozef Lawrynowicz
  1 sibling, 1 reply; 16+ messages in thread
From: Jeff Law @ 2020-11-06 20:53 UTC (permalink / raw)
  To: gcc-patches


On 9/15/20 2:30 PM, Jozef Lawrynowicz wrote:
> Ping x2 for below.
>
> On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
>> Pinging for this series of patches.
>> Attached all patches to this mail with the ammended patch 4 thanks to
>> Segher's review.
>>
>> Thanks,
>> Jozef
>>
>> On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
>>> The following series of patches for MSP430 implement some of the target
>>> macros used to determine the relative costs of operations.
>>>
>>> To give an indication of the overall effect of these changes on
>>> codesize, below are some size statistics collected from all the
>>> executable files from execute.exp that are built at -Os.
>>> There are around 1470 such tests (depending on the configuration).
>>>
>>> The percentage change (((new - old)/old) * 100) in text size is calculated
>>> for each test and the given metric is applied to that overall set of data.
>>>
>>> Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
>>> -----------------------------------------------------------------------------
>>> -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
>>> -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
>>> -mlarge       |  -1.7    |   -1.9     |      1412         |      37
>>>
>>> Successfully regtested on trunk for msp430-elf, ok to apply?
>>>
>>> Jozef Lawrynowicz (5):
>>>   MSP430: Implement TARGET_MEMORY_MOVE_COST
>>>   MSP430: Implement TARGET_RTX_COSTS
>>>   MSP430: Add defaulting to the insn length attribute
>>>   MSP430: Implement TARGET_INSN_COST
>>>   MSP430: Skip index-1.c test
>>>
>>>  gcc/config/msp430/msp430-protos.h             |   5 +-
>>>  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
>>>  gcc/config/msp430/msp430.h                    |  13 +
>>>  gcc/config/msp430/msp430.md                   | 439 +++++++--
>>>  gcc/config/msp430/msp430.opt                  |   4 +
>>>  gcc/config/msp430/predicates.md               |  13 +
>>>  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
>>>  7 files changed, 1206 insertions(+), 137 deletions(-)

[ ... ]

So it's a series of 5 patches.  They LGTM.    And if  there's minor
updates needed to address issues found once they're on the trunk, the
consider those updates pre-approved.

Note that defining LOGICAL_OP_NON_SHORT_CIRCUIT and BRANCH_COST impact
gimple code generation.  I'm a bit surprised there wasn't more fallout
in the existing tests.

jeff



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: ping x2 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-11-06 20:53     ` ping x2 " Jeff Law
@ 2020-11-06 21:23       ` Jozef Lawrynowicz
  2020-11-06 21:29         ` Jeff Law
  0 siblings, 1 reply; 16+ messages in thread
From: Jozef Lawrynowicz @ 2020-11-06 21:23 UTC (permalink / raw)
  To: Jeff Law; +Cc: gcc-patches

On Fri, Nov 06, 2020 at 01:53:19PM -0700, Jeff Law via Gcc-patches wrote:
> 
> On 9/15/20 2:30 PM, Jozef Lawrynowicz wrote:
> > Ping x2 for below.
> >
> > On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
> >> Pinging for this series of patches.
> >> Attached all patches to this mail with the ammended patch 4 thanks to
> >> Segher's review.
> >>
> >> Thanks,
> >> Jozef
> >>
> >> On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
> >>> The following series of patches for MSP430 implement some of the target
> >>> macros used to determine the relative costs of operations.
> >>>
> >>> To give an indication of the overall effect of these changes on
> >>> codesize, below are some size statistics collected from all the
> >>> executable files from execute.exp that are built at -Os.
> >>> There are around 1470 such tests (depending on the configuration).
> >>>
> >>> The percentage change (((new - old)/old) * 100) in text size is calculated
> >>> for each test and the given metric is applied to that overall set of data.
> >>>
> >>> Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
> >>> -----------------------------------------------------------------------------
> >>> -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
> >>> -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
> >>> -mlarge       |  -1.7    |   -1.9     |      1412         |      37
> >>>
> >>> Successfully regtested on trunk for msp430-elf, ok to apply?
> >>>
> >>> Jozef Lawrynowicz (5):
> >>>   MSP430: Implement TARGET_MEMORY_MOVE_COST
> >>>   MSP430: Implement TARGET_RTX_COSTS
> >>>   MSP430: Add defaulting to the insn length attribute
> >>>   MSP430: Implement TARGET_INSN_COST
> >>>   MSP430: Skip index-1.c test
> >>>
> >>>  gcc/config/msp430/msp430-protos.h             |   5 +-
> >>>  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
> >>>  gcc/config/msp430/msp430.h                    |  13 +
> >>>  gcc/config/msp430/msp430.md                   | 439 +++++++--
> >>>  gcc/config/msp430/msp430.opt                  |   4 +
> >>>  gcc/config/msp430/predicates.md               |  13 +
> >>>  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
> >>>  7 files changed, 1206 insertions(+), 137 deletions(-)
> 
> [ ... ]
> 
> So it's a series of 5 patches.  They LGTM.    And if  there's minor
> updates needed to address issues found once they're on the trunk, the
> consider those updates pre-approved.

Spooky, I pinged these patches the minute before you replied to this one.

> 
> Note that defining LOGICAL_OP_NON_SHORT_CIRCUIT and BRANCH_COST impact
> gimple code generation.  I'm a bit surprised there wasn't more fallout
> in the existing tests.

IIRC, when I tried messing with LOGICAL_OP_NON_SHORT_CIRCUIT and
BRANCH_COST before the full costs were implemented, they never had any
effect. I'm going to hand wave here and say that whatever behavior these
macros were affecting before the costs were implemented, was actually
behaving as it should. Then with the full costs implemented, I had to
tweak them to get back to that optimal state.

Also, the only conditional instructions available for MSP430 are
cbranch, i.e. there is never a choice between a cbranch and a cstore.
I was surprised BRANCH_COST had any effect, but after looking at output
assembly, it appeared that it did affect the number of cbranch insns
emitted, vs just other insns to move data about.

The changes did fix these optimization tests:
gcc.dg/tree-ssa/reassoc-33.c scan-tree-dump-times reassoc1 "Optimizing range tests" 3
gcc.dg/tree-ssa/reassoc-34.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1
gcc.dg/tree-ssa/reassoc-35.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1
gcc.dg/tree-ssa/reassoc-36.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1

Thanks for the review, I'll watch for fallout after installing on trunk.
Jozef
> 
> jeff
> 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: ping x2 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations
  2020-11-06 21:23       ` Jozef Lawrynowicz
@ 2020-11-06 21:29         ` Jeff Law
  0 siblings, 0 replies; 16+ messages in thread
From: Jeff Law @ 2020-11-06 21:29 UTC (permalink / raw)
  To: gcc-patches


On 11/6/20 2:23 PM, Jozef Lawrynowicz wrote:
> On Fri, Nov 06, 2020 at 01:53:19PM -0700, Jeff Law via Gcc-patches wrote:
>> On 9/15/20 2:30 PM, Jozef Lawrynowicz wrote:
>>> Ping x2 for below.
>>>
>>> On Fri, Aug 07, 2020 at 12:02:59PM +0100, Jozef Lawrynowicz wrote:
>>>> Pinging for this series of patches.
>>>> Attached all patches to this mail with the ammended patch 4 thanks to
>>>> Segher's review.
>>>>
>>>> Thanks,
>>>> Jozef
>>>>
>>>> On Thu, Jul 23, 2020 at 04:43:56PM +0100, Jozef Lawrynowicz wrote:
>>>>> The following series of patches for MSP430 implement some of the target
>>>>> macros used to determine the relative costs of operations.
>>>>>
>>>>> To give an indication of the overall effect of these changes on
>>>>> codesize, below are some size statistics collected from all the
>>>>> executable files from execute.exp that are built at -Os.
>>>>> There are around 1470 such tests (depending on the configuration).
>>>>>
>>>>> The percentage change (((new - old)/old) * 100) in text size is calculated
>>>>> for each test and the given metric is applied to that overall set of data.
>>>>>
>>>>> Configuration | Mean (%) | Median (%) | Delta < 0 (count) | Delta > 0 (count)
>>>>> -----------------------------------------------------------------------------
>>>>> -mcpu=msp430  |  -2.4    |   -2.7     |      1454         |      17
>>>>> -mcpu=msp430x |  -2.3    |   -2.4     |      1460         |      10
>>>>> -mlarge       |  -1.7    |   -1.9     |      1412         |      37
>>>>>
>>>>> Successfully regtested on trunk for msp430-elf, ok to apply?
>>>>>
>>>>> Jozef Lawrynowicz (5):
>>>>>   MSP430: Implement TARGET_MEMORY_MOVE_COST
>>>>>   MSP430: Implement TARGET_RTX_COSTS
>>>>>   MSP430: Add defaulting to the insn length attribute
>>>>>   MSP430: Implement TARGET_INSN_COST
>>>>>   MSP430: Skip index-1.c test
>>>>>
>>>>>  gcc/config/msp430/msp430-protos.h             |   5 +-
>>>>>  gcc/config/msp430/msp430.c                    | 867 ++++++++++++++++--
>>>>>  gcc/config/msp430/msp430.h                    |  13 +
>>>>>  gcc/config/msp430/msp430.md                   | 439 +++++++--
>>>>>  gcc/config/msp430/msp430.opt                  |   4 +
>>>>>  gcc/config/msp430/predicates.md               |  13 +
>>>>>  gcc/testsuite/gcc.c-torture/execute/index-1.c |   2 +
>>>>>  7 files changed, 1206 insertions(+), 137 deletions(-)
>> [ ... ]
>>
>> So it's a series of 5 patches.  They LGTM.    And if  there's minor
>> updates needed to address issues found once they're on the trunk, the
>> consider those updates pre-approved.
> Spooky, I pinged these patches the minute before you replied to this one.

Yea, I saw your ping when I went to my inbox after reviewing the change ;-)


>
>> Note that defining LOGICAL_OP_NON_SHORT_CIRCUIT and BRANCH_COST impact
>> gimple code generation.  I'm a bit surprised there wasn't more fallout
>> in the existing tests.
> IIRC, when I tried messing with LOGICAL_OP_NON_SHORT_CIRCUIT and
> BRANCH_COST before the full costs were implemented, they never had any
> effect. I'm going to hand wave here and say that whatever behavior these
> macros were affecting before the costs were implemented, was actually
> behaving as it should. Then with the full costs implemented, I had to
> tweak them to get back to that optimal state.
>
> Also, the only conditional instructions available for MSP430 are
> cbranch, i.e. there is never a choice between a cbranch and a cstore.
> I was surprised BRANCH_COST had any effect, but after looking at output
> assembly, it appeared that it did affect the number of cbranch insns
> emitted, vs just other insns to move data about.

It tends to twiddle some of the jump threading tests which are sensitive
to the jumping structure (surprise).  We try to handle both cases, but
it's proven pretty fragile thorugh the years.


>
> The changes did fix these optimization tests:
> gcc.dg/tree-ssa/reassoc-33.c scan-tree-dump-times reassoc1 "Optimizing range tests" 3
> gcc.dg/tree-ssa/reassoc-34.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1
> gcc.dg/tree-ssa/reassoc-35.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1
Yea, those would be well within the realm of expected for changing those
macros since they'd change the decisons  for expanding a range test
either as a series of conditionals or a series of logical ops and a
single conditional.  THat in turn heavily influences optimize_range_test
in tree-ssa-reassoc
> gcc.dg/tree-ssa/reassoc-36.c scan-tree-dump-times reassoc1 "Optimizing range tests" 1
>
> Thanks for the review, I'll watch for fallout after installing on trunk.

Likewise.  I think msp430 was running in my tester while I was reviewing
the changes, so tomorrow's run should be interesting to review.


jeff


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2020-11-06 21:29 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-23 15:43 [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
2020-07-23 15:47 ` [PATCH 1/5] MSP430: Implement TARGET_MEMORY_MOVE_COST Jozef Lawrynowicz
2020-07-23 15:49 ` [PATCH 2/5] MSP430: Implement TARGET_RTX_COSTS Jozef Lawrynowicz
2020-07-23 15:54 ` [PATCH 3/5] MSP430: Add defaulting to the insn length attribute Jozef Lawrynowicz
2020-07-23 15:56 ` [PATCH 4/5] MSP430: Implement TARGET_INSN_COST Jozef Lawrynowicz
2020-07-23 18:34   ` Segher Boessenkool
2020-07-24 11:50     ` Jozef Lawrynowicz
2020-07-24 12:25       ` Segher Boessenkool
2020-07-23 15:57 ` [PATCH 5/5] MSP430: Skip index-1.c test Jozef Lawrynowicz
2020-08-07 11:02 ` ping [PATCH 0/5] MSP430: Implement macros to describe relative costs of operations Jozef Lawrynowicz
2020-09-15 20:30   ` ping x2 " Jozef Lawrynowicz
2020-10-14 15:31     ` ping x3 " Jozef Lawrynowicz
2020-11-06 20:51       ` ping x4 " Jozef Lawrynowicz
2020-11-06 20:53     ` ping x2 " Jeff Law
2020-11-06 21:23       ` Jozef Lawrynowicz
2020-11-06 21:29         ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).