This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: msp430 port



Ok, I think I got it all covered...  ended up copying most of the
pushm/popm from rx, added "return" but not "simple_return", and merged
added a peephole for and->bic.

[gcc]

+
+(define_predicate "msp_volatile_memory_operand"
+  (and (match_code "mem")
+ (match_test ("memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0), MEM_ADDR_SPACE (op))")))
+)
+
+; TRUE for any valid general operand.  We do this because
+; general_operand refuses to match volatile memory refs.
+
+(define_predicate "msp_general_operand"
+  (ior (match_operand 0 "general_operand")
+       (match_operand 0 "msp_volatile_memory_operand"))
+)
See my previous message.  This i
+
+; Likewise for nonimmediate_operand.
+
+(define_predicate "msp_nonimmediate_operand"
+  (ior (match_operand 0 "nonimmediate_operand")/sub
+       (match_operand 0 "msp_volatile_memory_operand"))
+)
+
+(define_predicate "ubyte_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 255)")))
+
+; TRUE for comparisons we support.
+(define_predicate "msp430_cmp_operator"
+  (match_code "eq,ne,lt,ltu,ge,geu"))
+
+; TRUE for comparisons we need to reverse.
+(define_predicate "msp430_reversible_cmp_operator"
+  (match_code "gt,gtu,le,leu"))
+
+; TRUE for constants the constant generator can produce
+(define_predicate "msp430_constgen_operand"
+  (and (match_code "const_int")
+       (match_test ("   INTVAL (op) == 0
+		     || INTVAL (op) == 1
+		     || INTVAL (op) == 2
+		     || INTVAL (op) == 4
+		     || INTVAL (op) == 8
+		     || INTVAL (op) == -1 "))))
+
+; TRUE for constants the constant generator can produce
+(define_predicate "msp430_inv_constgen_operand"
+  (and (match_code "const_int")
+       (match_test ("   INTVAL (op) == ~0
+		     || INTVAL (op) == ~1
+		     || INTVAL (op) == ~2
+		     || INTVAL (op) == ~4
+		     || INTVAL (op) == ~8
+		     || INTVAL (op) == ~(-1) "))))
+
+(define_predicate "msp430_nonsubreg_operand"
+  (match_code "reg,mem"))
+
+; TRUE for constants which are bit positions for zero_extract
+(define_predicate "msp430_bitpos"
+  (and (match_code "const_int")
+       (match_test ("IN_RANGE (INTVAL (op), 0, 15)"))))
+
+;; Return true if OP is a store multiple operation.  This looks like:
+;;
+;;   [(set (SP) (MINUS (SP) (INT)))
+;;    (set (MEM (SP)) (REG))
+;;    (set (MEM (MINUS (SP) (INT))) (REG)) {optionally repeated}
+;;   ]
+
+(define_special_predicate "msp430_store_multiple_vector_p"
+  (match_code "parallel")
+{
+  int count = XVECLEN (op, 0);
+  unsigned int src_regno;
+  rtx element;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  if (count < 2)
+    return false;
+
+  /* Check that the first element of the vector is the stack adjust.  */
+  element = XVECEXP (op, 0, 0);
+  if (     GET_CODE (element) != SET
+      || ! REG_P (SET_DEST (element))
+      ||   REGNO (SET_DEST (element)) != SP_REGNO
+      ||   GET_CODE (SET_SRC (element)) != MINUS
+      || ! REG_P (XEXP (SET_SRC (element), 0))
+      ||   REGNO (XEXP (SET_SRC (element), 0)) != SP_REGNO
+      || ! CONST_INT_P (XEXP (SET_SRC (element), 1)))
+    return false;
+	 +  /* Check that the next element is the first push.  */
+  element = XVECEXP (op, 0, 1);
+  if (     GET_CODE (element) != SET
+      || ! REG_P (SET_SRC (element))
+      || GET_MODE (SET_SRC (element)) != PSImode
+      || ! MEM_P (SET_DEST (element))
+      || GET_MODE (SET_DEST (element)) != PSImode
+      || GET_CODE (XEXP (SET_DEST (element), 0)) != MINUS
+      || ! REG_P (XEXP (XEXP (SET_DEST (element), 0), 0))
+      ||   REGNO (XEXP (XEXP (SET_DEST (element), 0), 0)) != SP_REGNO
+      || ! CONST_INT_P (XEXP (XEXP (SET_DEST (element), 0), 1))
+      || INTVAL (XEXP (XEXP (SET_DEST (element), 0), 1)) != 4)
+    return false;
+
+  src_regno = REGNO (SET_SRC (element));
+
+  /* Check that the remaining elements use SP-<disp>
+     addressing and decreasing register numbers.  */
+  for (i = 2; i < count; i++)
+    {
+      element = XVECEXP (op, 0, i);
+
+      if (     GET_CODE (element) != SET
+	  || ! REG_P (SET_SRC (element))
+	  || GET_MODE (SET_SRC (element)) != PSImode
+	  || REGNO (SET_SRC (element)) != src_regno - (i - 1)
+	  || ! MEM_P (SET_DEST (element))
+	  || GET_MODE (SET_DEST (element)) != PSImode
+	  || GET_CODE (XEXP (SET_DEST (element), 0)) != MINUS
+          || ! REG_P (XEXP (XEXP (SET_DEST (element), 0), 0))
+          ||   REGNO (XEXP (XEXP (SET_DEST (element), 0), 0)) != SP_REGNO
+	  || ! CONST_INT_P (XEXP (XEXP (SET_DEST (element), 0), 1))
+	  || INTVAL (XEXP (XEXP (SET_DEST (element), 0), 1))
+	     != i * 4)
+	return false;
+    }
+  return true;
+})
+
+(define_special_predicate "msp430_load_multiple_vector_p"
+  (match_code "parallel")
+{
+  int count = XVECLEN (op, 0);
+  unsigned int src_regno;
+  rtx element;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  if (count < 2)
+    return false;
+
+ /* Check that the first element of the vector is the stack adjust.  */
+  element = XVECEXP (op, 0, 0);
+  if (     GET_CODE (element) != SET
+      || ! REG_P (SET_DEST (element))
+      ||   REGNO (SET_DEST (element)) != SP_REGNO
+      ||   GET_CODE (SET_SRC (element)) != PLUS
+      || ! REG_P (XEXP (SET_SRC (element), 0))
+      ||   REGNO (XEXP (SET_SRC (element), 0)) != SP_REGNO
+      || ! CONST_INT_P (XEXP (SET_SRC (element), 1)))
+    return false;
+	 +  element = XVECEXP (op, 0, 1);
+	 +  /* Check that the next element is the first push.  */
+  element = XVECEXP (op, 0, 1);
+  if (     GET_CODE (element) != SET
+      || ! REG_P (SET_DEST (element))
+      || GET_MODE (SET_DEST (element)) != PSImode
+      || ! MEM_P (SET_SRC (element))
+      || GET_MODE (SET_SRC (element)) != PSImode
+      || GET_CODE (XEXP (SET_SRC (element), 0)) != PLUS
+      || ! REG_P (XEXP (XEXP (SET_SRC (element), 0), 0))
+      ||   REGNO (XEXP (XEXP (SET_SRC (element), 0), 0)) != SP_REGNO
+      || ! CONST_INT_P (XEXP (XEXP (SET_SRC (element), 0), 1)))
+    return false;
+
+  src_regno = REGNO (SET_DEST (element));
+
+   /* Check that the remaining elements use SP+<disp>
+     addressing and decreasing register numbers.  */
+  for (i = 2; i < count; i++)
+    {
+      element = XVECEXP (op, 0, i);
+
+      if (     GET_CODE (element) != SET
+	  || ! REG_P (SET_DEST (element))
+	  || GET_MODE (SET_DEST (element)) != PSImode
+	  || REGNO (SET_DEST (element)) != src_regno - (i - 1)
+	  || ! MEM_P (SET_SRC (element))
+	  || GET_MODE (SET_SRC (element)) != PSImode
+	  || GET_CODE (XEXP (SET_SRC (element), 0)) != PLUS
+          || ! REG_P (XEXP (XEXP (SET_SRC (element), 0), 0))
+          ||   REGNO (XEXP (XEXP (SET_SRC (element), 0), 0)) != SP_REGNO
+	  || ! CONST_INT_P (XEXP (XEXP (SET_SRC (element), 0), 1))
+	  || INTVAL (XEXP (XEXP (SET_SRC (element), 0), 1))
+	     != count * 4 - (i + 1) * 4)
+	return false;
+    }
+  return true;
+})
Index: gcc/config/msp430/msp430.md
===================================================================
--- gcc/config/msp430/msp430.md	(revision 0)
+++ gcc/config/msp430/msp430.md	(revision 0)
@@ -0,0 +1,1291 @@
+;;  Machine Description for TI MSP43* processors
+;;  Copyright (C) 2013 Free Software Foundation, Inc.
+;;  Contributed by Red Hat.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+
+(define_constants
+  [
+   (PC_REGNO 0)
+   (SP_REGNO 1)
+   (CARRY 2)
+  ])
+
+(define_c_enum "unspec"
+  [
+   UNS_PROLOGUE_START_MARKER
+   UNS_PROLOGUE_END_MARKER
+   UNS_EPILOGUE_START_MARKER
+   UNS_EPILOGUE_HELPER
+
+   UNS_PUSHM
+   UNS_POPM
+
+   UNS_GROW_AND_SWAP
+   UNS_SWAP_AND_SHRINK
+  ])
+  +(include "predicates.md")
+(include "constraints.md")
+
+(define_mode_iterator QHI [QI HI PSI])
+
+;; There are two basic "family" tests we do here:
+;;
+;; msp430x - true if 430X instructions are available.
+;; TARGET_LARGE - true if pointers are 20-bits
+;;
+;; Note that there are three supported cases, since the base 430
+;; doesn't have 20-bit pointers:
+;;
+;; 1. MSP430 cpu, small model
+;; 2. MSP430X cpu, small model.
+;; 3. MSP430X cpu, large model.
+
+;;------------------------------------------------------------
+;; Moves
+
+;; Push/Pop must be before the generic move patterns
+
+(define_insn "push"
+  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNO)))
+	(match_operand:HI 0 "register_operand" "r"))]
+  ""
+  "PUSH\t%0"
+  )
+
+(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"
+  )
+
+(define_insn "pushm"
+  [(match_parallel 1 "msp430_store_multiple_vector_p"
+     [(set (reg:HI SP_REGNO)
+	   (minus:HI (reg:HI SP_REGNO)
+		     (match_operand 0 "const_int_operand" "n")))])]
+  "reload_completed && ! TARGET_LARGE"
+  {
+    return msp430_emit_pushm (operands);
+  }
+  )
+
+(define_insn "pushma"
+  [(match_parallel 1 "msp430_store_multiple_vector_p"
+     [(set (reg:PSI SP_REGNO)
+	   (minus:PSI (reg:PSI SP_REGNO)
+		     (match_operand 0 "const_int_operand" "n")))])]
+  "reload_completed && TARGET_LARGE"
+  {
+    return msp430_emit_pushm (operands);
+  }
+  )
+
+(define_insn "pop"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+	(mem:HI (post_inc:HI (reg:HI SP_REGNO))))]
+  ""
+  "POP\t%0"
+  )
+
+(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"
+  )
+
+(define_insn "popm"
+  [(match_parallel 1 "msp430_load_multiple_vector_p"
+     [(set (reg:HI SP_REGNO)
+	   (plus:HI (reg:HI SP_REGNO)
+		     (match_operand 0 "const_int_operand" "n")))])]
+  "reload_completed && !TARGET_LARGE"
+  {
+    return msp430_emit_popm (operands);
+  }
+)
+
+(define_insn "popma"
+  [(match_parallel 1 "msp430_load_multiple_vector_p"
+     [(set (reg:PSI SP_REGNO)
+	   (plus:PSI (reg:PSI SP_REGNO)
+		     (match_operand 0 "const_int_operand" "n")))])]
+  "reload_completed && TARGET_LARGE"
+  {
+    return msp430_emit_popm (operands);
+  }
+)
+
+;; 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
+;; argument is split between argument registers and the stack, gcc expects the +;; callee to allocate space on the stack that can contain the register-based
+;; part of the argument.  This space *has* to be just before the remaining
+;; arguments (ie the ones that are fully on the stack).
+;;
+;; The problem is that the MSP430 CALL instruction pushes the return address
+;; onto the stack in the exact place where the callee wants to allocate
+;; this extra space. So we need a sequence of instructions that can allocate
+;; the extra space and then move the return address down the stack, so that
+;; the extra space is now adjacent to the remaining arguments.
+;;
+;; This could be constructed through regular insns, but they might be split up
+;; by a misguided optimization, so an unspec volatile is used instead.
+
+(define_insn "grow_and_swap"
+  [(unspec_volatile [(const_int 0)] UNS_GROW_AND_SWAP)
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  { if (TARGET_LARGE)
+      return "SUBA\t#2, r1 \n MOVX.A\t2(r1), 0(r1)";
+    return "SUB\t#2, r1 \n MOV.W\t2(r1), 0(r1)";
+    }
+  )
+
+(define_insn "swap_and_shrink"
+  [(unspec_volatile [(const_int 0)] UNS_SWAP_AND_SHRINK)
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  { return TARGET_LARGE
+	   ? "MOVX.A\t0(r1), 2(r1) \n ADDA\t#2, SP"
+	   : "MOV.W\t0(r1), 2(r1) \n ADD\t#2, SP";
+  })
+
+(define_insn "movqi"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=rYs,rm")
+	(match_operand:QI 1 "general_operand" "riYs,rmi"))]
+  ""
+  "@
+  MOV.B\t%1, %0
+  MOV%X0.B\t%1, %0"
+)
+
+(define_insn "movhi"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=rYs,rm")
+	(match_operand:HI 1 "general_operand" "riYs,rmi"))]
+  ""
+  "@
+  MOV.W\t%1, %0
+  MOV%X0.W\t%1, %0"
+)
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "")
+	(match_operand:SI 1 "general_operand" ""))]
+  ""
+  ""
+  )
+  +(define_insn_and_split "movsi_x"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+	(match_operand:SI 1 "general_operand" "rmi"))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_operand:HI 2 "nonimmediate_operand")
+	(match_operand:HI 4 "general_operand"))
+   (set (match_operand:HI 3 "nonimmediate_operand")
+	(match_operand:HI 5 "general_operand"))]
+  "msp430_split_movsi (operands);"
+)
+
+;; Some MOVX.A cases can be done with MOVA, this is only a few of them.
+(define_insn "movpsi"
+  [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,Ya,rm")
+	(match_operand:PSI 1 "general_operand" "riYa,r,rmi"))]
+  ""
+  "@
+  MOV%A0\t%1, %0
+  MOV%A0\t%1, %0
+  MOV%X0.%A0\t%1, %0")
+
+;;------------------------------------------------------------
+;; Math
+
+(define_insn "addpsi3"
+  [(set (match_operand:PSI           0 "nonimmediate_operand" "=r,rm")
+	(plus:PSI (match_operand:PSI 1 "nonimmediate_operand" "%0,0")
+		  (match_operand:PSI 2 "general_operand"      "rLs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  ADDA\t%2, %0
+  ADDX.A\t%2, %0"
+)
+
+(define_insn "addqi3"
+  [(set (match_operand:QI          0 "nonimmediate_operand" "=rYs,rm")
+	(plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+		 (match_operand:QI 2 "general_operand"      "riYs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   ADD.B\t%2, %0
+   ADD%X0.B\t%2, %0"
+)
+
+(define_insn "addqi3_ze"
+  [(set (match_operand:HI          0 "nonimmediate_operand" "=r,r")
+ (zero_extend:HI (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+				 (match_operand:QI 2 "general_operand"      "riYs,rmi"))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   ADD.B\t%2, %0
+   ADD%X0.B\t%2, %0"
+)
+
+(define_insn "addhi3"
+  [(set (match_operand:HI           0 "nonimmediate_operand" "=rYs,rm")
+	(plus:HI (match_operand:HI  1 "nonimmediate_operand" "%0,0")
+		  (match_operand:HI 2 "general_operand"      "riYs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   ADD.W\t%2, %0
+   ADD%X0.W\t%2, %0"
+)
+
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,rm")
+	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
+		 (match_operand:SI 2 "general_operand" "r,mi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   ADD\t%L2, %L0 { ADDC\t%H2, %H0
+   ADD%X0\t%L2, %L0 { ADDC%X0\t%H2, %H0"
+)
+
+; Version of addhi that exposes the carry operations, for SImode adds.
+;
+; We have these two add patterns and the splitter that follows because
+; our tests have shown that this results in a significant reduction in
+; code size - because GCC is able to discard any unused part of the
+; addition.  We have to annotate the patterns with the set and use of
+; the carry flag because otherwise GCC will discard parts of the
+; addition when they are actually needed.
+
+(define_insn "addhi3_cy"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+	(plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+		 (match_operand:HI 2 "general_operand" "ri,rmi")))
+   (set (reg:CC CARRY)
+   	(compare (plus:SI (match_dup 1) (match_dup 2))
+		 (const_int 0)))
+   ]
+  ""
+  "@
+   ADD %2, %1 ; cy
+   ADD%X0 %2, %1 ; cy"
+  )
+
+; Version of addhi that adds the carry, for SImode adds.
+(define_insn "addchi4_cy"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+	(plus:HI (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+			  (match_operand:HI 2 "general_operand" "ri,rmi"))
+		 (ltu:HI (reg:CC CARRY) (const_int 0))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   ADDC %2, %1
+   ADDC%X0 %2, %1"
+  )
+
+; 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
+; halves.
+(define_split
+  [(set (match_operand:SI 0 "msp430_nonsubreg_operand" "")
+	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "")
+		 (match_operand:SI 2 "general_operand" "")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  [(parallel [(set (match_operand:HI 3 "nonimmediate_operand" "")
+		   (plus:HI (match_dup 4)
+			    (match_dup 5)))
+	      (set (reg:CC CARRY)
+		   (compare (plus:SI (match_dup 4) (match_dup 5))
+			    (const_int 0)))]
+
+	      )
+   (parallel [(set (match_operand:HI 6 "nonimmediate_operand" "")
+		   (plus:HI (plus:HI (match_dup 7)
+				     (match_dup 8))
+			    (ltu:HI (reg:CC CARRY) (const_int 0))))
+	      (clobber (reg:CC CARRY))
+	      ])
+   ]
+  "
+   operands[3] = msp430_subreg (HImode, operands[0], SImode, 0);
+   operands[4] = msp430_subreg (HImode, operands[1], SImode, 0);
+   operands[5] = msp430_subreg (HImode, operands[2], SImode, 0);
+   operands[6] = msp430_subreg (HImode, operands[0], SImode, 2);
+   operands[7] = msp430_subreg (HImode, operands[1], SImode, 2);
+   operands[8] = msp430_subreg (HImode, operands[2], SImode, 2);
+   if (GET_CODE (operands[5]) == CONST_INT)
+     {
+       operands[9] = GEN_INT (INTVAL (operands[5]) & 0xffff);
+     }
+   else
+     {
+       operands[9] = gen_rtx_ZERO_EXTEND (SImode, operands[5]);
+     }
+   "
+  )
+
+
+(define_insn "subpsi3"
+  [(set (match_operand:PSI            0 "nonimmediate_operand" "=r,   rm")
+	(minus:PSI (match_operand:PSI 1 "general_operand"       "0,   0")
+		   (match_operand:PSI 2 "general_operand"       "rLs, rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  SUBA\t%2, %0
+  SUBX.A\t%2, %0"
+)
+
+(define_insn "subqi3"
+  [(set (match_operand:QI           0 "nonimmediate_operand" "=rYs,  rm")
+	(minus:QI (match_operand:QI 1 "general_operand"       "0,    0")
+		  (match_operand:QI 2 "general_operand"      " riYs, rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  SUB.B\t%2, %0
+  SUB%X0.B\t%2, %0"
+)
+
+(define_insn "subhi3"
+  [(set (match_operand:HI           0 "nonimmediate_operand" "=rYs,  rm")
+	(minus:HI (match_operand:HI 1 "general_operand"       "0,    0")
+		  (match_operand:HI 2 "general_operand"      " riYs, rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  SUB.W\t%2, %0
+  SUB%X0.W\t%2, %0"
+)
+
+(define_insn "subsi3"
+  [(set (match_operand:SI           0 "nonimmediate_operand" "=&rm")
+	(minus:SI (match_operand:SI 1 "nonimmediate_operand"   "0")
+		  (match_operand:SI 2 "general_operand"        "rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "SUB%X0\t%L2, %L0 { SUBC%X0\t%H2, %H0"
+)
+
+(define_insn "*bic<mode>_cg"
+  [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,m")
+	(and:QHI (match_operand:QHI 1 "msp_general_operand" "0,0")
+		 (match_operand 2 "msp430_inv_constgen_operand" "n,n")))]
+  ""
+  "@
+   BIC%x0%B0\t#%I2, %0
+   BIC%X0%B0\t#%I2, %0"
+)
+
+(define_insn "bic<mode>3"
+ [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm") + (and:QHI (not:QHI (match_operand:QHI 1 "msp_general_operand" "rYs,rmn"))
+		 (match_operand:QHI          2 "msp_nonimmediate_operand"  "0,0")))]
+  ""
+  "@
+   BIC%x0%B0\t%1, %0
+   BIC%X0%B0\t%1, %0"
+)
+
+(define_insn "and<mode>3"
+  [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm")
+	(and:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0")
+		 (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   AND%x0%B0\t%2, %0
+   AND%X0%B0\t%2, %0"
+)
+
+(define_peephole2
+  [(parallel [(set (match_operand:QHI 0 "msp_nonimmediate_operand")
+	(and:QHI (match_operand:QHI 1 "msp_nonimmediate_operand")
+		 (match_operand 2 "msp430_inv_constgen_operand")))
+	      (clobber (reg:CC CARRY))])
+   ]
+  ""
+  [
+   (set (match_dup 0)
+	(and:QHI (match_dup 1)
+		 (match_dup 2)))
+   ]
+  )
+
+(define_insn "ior<mode>3"
+  [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm")
+	(ior:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0")
+		 (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   BIS%x0%B0\t%2, %0
+   BIS%X0%B0\t%2, %0"
+)
+
+(define_insn "xor<mode>3"
+  [(set (match_operand:QHI 0 "nonimmediate_operand" "=rYs,rm")
+	(xor:QHI (match_operand:QHI 1 "nonimmediate_operand" "%0,0")
+		 (match_operand:QHI 2 "general_operand" "riYs,rmi")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   XOR%x0%B0\t%2, %0
+   XOR%X0%B0\t%2, %0"
+)
+
+;; Macro : XOR #~0, %0
+(define_insn "one_cmpl<mode>2"
+  [(set (match_operand:QHI 0 "nonimmediate_operand" "=rYs,m")
+	(not:QHI (match_operand:QHI 1 "nonimmediate_operand" "0,0")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   INV%x0%B0\t%0
+   INV%X0%B0\t%0"
+)
+
+(define_insn "extendqihi2"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=rYs,m")
+	(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,0")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   SXT%X0\t%0
+   SXT%X0\t%0"
+)
+
+;; Note: QImode moves to registers clear the top byte
+(define_insn "zero_extendqihi2"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=rYs,m,r,r")
+	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,0,rYs,m")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   AND\t#0xff, %0
+   AND%X0\t#0xff, %0
+   MOV.B\t%1,%0
+   MOV%X0.B\t%1,%0"
+)
+
+;; Eliminate extraneous zero-extends mysteriously created by gcc.
+(define_peephole2
+  [(set (match_operand:HI 0 "register_operand")
+	(zero_extend:HI (match_operand:QI 1 "general_operand")))
+   (set (match_operand:HI 2 "register_operand")
+	(zero_extend:HI (match_operand:QI 3 "register_operand")))]
+ "REGNO (operands[0]) == REGNO (operands[2]) && REGNO (operands[2]) == REGNO (operands[3])"
+  [(set (match_dup 0)
+	(zero_extend:HI (match_dup 1)))]
+)
+   +(define_insn "zero_extendhipsi2"
+  [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,m")
+	(zero_extend:PSI (match_operand:HI 1 "nonimmediate_operand" "rm,r")))]
+  ""
+  "MOVX\t%1, %0"
+)
+
+(define_insn "truncpsihi2"
+  [(set (match_operand:HI               0 "nonimmediate_operand" "=rm")
+	(truncate:HI (match_operand:PSI 1 "register_operand"      "r")))]
+  ""
+  "MOVX\t%1, %0"
+)
+
+(define_insn "extendhisi2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r")
+	(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  { return msp430x_extendhisi (operands); }
+)
+
+;; Optimization
+(define_insn "extendhipsi2"
+  [(set (match_operand:PSI 0 "nonimmediate_operand" "=r")
+ (subreg:PSI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")) 0))
+   (clobber (reg:CC CARRY))
+   ]
+  "TARGET_LARGE"
+  "RLAM #4, %0 { RRAM #4, %0"
+)
+
+;; Look for cases where integer/pointer conversions are suboptimal due
+;; to missing patterns, despite us not having opcodes for these
+;; patterns.  Doing these manually allows for alternate optimization
+;; paths.
+(define_insn "zero_extendhisi2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")))]
+  "TARGET_LARGE"
+  "MOV.W\t#0,%H0"
+)
+
+;; Optimization
+(define_insn "zero_extendhisipsi2"
+  [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r")
+ (subreg:PSI (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,r")) 0))
+   (clobber (reg:CC CARRY))
+   ]
+  "TARGET_LARGE"
+  "@
+   AND.W\t#-1,%0
+   MOV.W\t%1,%0"
+)
+
+;; Optimization
+(define_insn "extend_and_shift1_hipsi2"
+  [(set (subreg:SI (match_operand:PSI 0 "nonimmediate_operand" "=r") 0)
+ (ashift:SI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0"))
+		   (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  "TARGET_LARGE"
+  "RLAM #4, %0 { RRAM #3, %0"
+)
+
+;; Optimization
+(define_insn "extend_and_shift2_hipsi2"
+  [(set (subreg:SI (match_operand:PSI 0 "nonimmediate_operand" "=r") 0)
+ (ashift:SI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0"))
+		   (const_int 2)))
+   (clobber (reg:CC CARRY))
+   ]
+  "TARGET_LARGE"
+  "RLAM #4, %0 { RRAM #2, %0"
+)
+
+; Nasty - we are sign-extending a 20-bit PSI value in one register into
+; two adjacent 16-bit registers to make an SI value.  There is no MSP430X
+; instruction that will do this, so we push the 20-bit value onto the stack
+; and then pop it off as two 16-bit values.
+;
+; FIXME: The MSP430X documentation does not specify if zero-extension or
+; sign-extension happens when the 20-bit value is pushed onto the stack.
+; It is probably zero-extension, but if not this pattern will not work
+; when the PSI value is negative..
+;
+; Note: using PUSHM.A #1 is two bytes smaller than using PUSHX.A....
+
+(define_insn "zero_extendpsisi2"
+  [(set (match_operand:SI                  0 "register_operand" "=r")
+	(zero_extend:SI (match_operand:PSI 1 "register_operand" "r")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "*
+    if (REGNO (operands[1]) == SP_REGNO)
+      /* If the source register is the stack pointer, the value
+         stored in the stack slot will be the value *after* the
+	 stack pointer has been decremented.  So allow for that
+	 here.  */
+      return \"PUSHM.A #1, %1 { ADDX.W #4, @r1 { POPX.W %0 { POPX.W %H0\";
+    else
+      return \"PUSHM.A #1, %1 { POPX.W %0 { POPX.W %H0\";
+  "
+)
+
+; See the movsipsi2 pattern above for another way that GCC performs this
+; conversion.
+(define_insn "truncsipsi2"
+  [(set (match_operand:PSI              0 "register_operand" "=r")
+	(truncate:PSI (match_operand:SI 1 "register_operand" "r")))]
+  ""
+  "PUSH.W %H1 { PUSH.W %1 { POPM.A #1, %0"
+)
+
+;;------------------------------------------------------------
+;; Shift Functions
+
+;; Note:  We do not use the RPT ... SHIFT instruction sequence
+;; when the repeat count is in a register, because even though RPT
+;; accepts counts in registers, it does not work if the count is
+;; zero, and the actual count in the register has to be one less
+;; than the required number of iterations.  We could encode a
+;; seqeunce like this:
+;;
+;;   bit #0xf, Rn
+;;   bz  1f
+;;   dec Rn
+;;   rpt Rn
+;;   <shift> Rm
+;;   inc Rn
+;; 1:
+;;
+;; But is longer than calling a helper function, and we are mostly
+;; concerned with code size.  FIXME: Maybe enable a sequence like
+;; this at -O3 and above ?
+;;
+;; Note - we ignore shift counts of less than one or more than 15.
+;; This is permitted by the ISO C99 standard as such shifts result
+;; in "undefined" behaviour.  [6.5.7 (3)]
+
+;; signed A << C
+
+(define_expand "ashlhi3"
+  [(set (match_operand:HI            0 "nonimmediate_operand")
+	(ashift:HI (match_operand:HI 1 "general_operand")
+		   (match_operand:HI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  {
+    if (msp430x
+        && REG_P (operands[0])
+        && REG_P (operands[1])
+        && CONST_INT_P (operands[2]))
+ emit_insn (gen_430x_shift_left (operands[0], operands[1], operands[2]));
+    else		 +      msp430_expand_helper (operands, \"__mspabi_slli\", true);
+    DONE;
+  }
+)
+
+(define_insn "slli_1"
+  [(set (match_operand:HI            0 "nonimmediate_operand" "=rm")
+	(ashift:HI (match_operand:HI 1 "general_operand"       "0")
+		   (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RLA.W\t%0" ;; Note - this is a macro for ADD
+)
+
+(define_insn "430x_shift_left"
+  [(set (match_operand:HI            0 "register_operand" "=r")
+	(ashift:HI (match_operand:HI 1 "register_operand"  "0")
+		   (match_operand    2 "immediate_operand" "n")))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  "*
+  if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 16)
+    return \"rpt\t%2 { rlax.w\t%0\";
+  return \"# nop left shift\";
+  "
+)
+
+(define_insn "slll_1"
+  [(set (match_operand:SI            0 "nonimmediate_operand" "=rm")
+	(ashift:SI (match_operand:SI 1 "general_operand"       "0")
+		   (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RLA.W\t%L0 { RLC.W\t%H0"
+)
+
+(define_insn "slll_2"
+  [(set (match_operand:SI            0 "nonimmediate_operand" "=rm")
+	(ashift:SI (match_operand:SI 1 "general_operand"       "0")
+		   (const_int 2)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RLA.W\t%L0 { RLC.W\t%H0 { RLA.W\t%L0 { RLC.W\t%H0"
+)
+
+(define_expand "ashlsi3"
+  [(set (match_operand:SI            0 "nonimmediate_operand")
+	(ashift:SI (match_operand:SI 1 "general_operand")
+		   (match_operand:SI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "msp430_expand_helper (operands, \"__mspabi_slll\", true);
+   DONE;"
+)
+
+;;----------
+
+;; signed A >> C
+
+(define_expand "ashrhi3"
+  [(set (match_operand:HI              0 "nonimmediate_operand")
+	(ashiftrt:HI (match_operand:HI 1 "general_operand")
+		     (match_operand:HI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  {
+    if (msp430x
+        && REG_P (operands[0])
+        && REG_P (operands[1])
+        && CONST_INT_P (operands[2]))
+ emit_insn (gen_430x_arithmetic_shift_right (operands[0], operands[1], operands[2])); + else + msp430_expand_helper (operands, \"__mspabi_srai\", true);
+   DONE;
+   }
+)
+
+(define_insn "srai_1"
+  [(set (match_operand:HI              0 "nonimmediate_operand" "=rm")
+	(ashiftrt:HI (match_operand:HI 1 "general_operand"      "0")
+		     (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RRA.W\t%0"
+)
+
+(define_insn "430x_arithmetic_shift_right"
+  [(set (match_operand:HI              0 "register_operand" "=r")
+	(ashiftrt:HI (match_operand:HI 1 "register_operand"  "0")
+		     (match_operand    2 "immediate_operand" "n")))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  "*
+  if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 16)
+    return \"rpt\t%2 { rrax.w\t%0\";
+  return \"# nop arith right shift\";
+  "
+)
+
+(define_insn "srap_1"
+  [(set (match_operand:PSI              0 "register_operand" "=r")
+	(ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
+		      (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  "RRAM.A #1,%0"
+)
+
+(define_insn "srap_2"
+  [(set (match_operand:PSI              0 "register_operand" "=r")
+	(ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
+		      (const_int 2)))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  "RRAM.A #2,%0"
+)
+
+(define_insn "sral_1"
+  [(set (match_operand:SI              0 "nonimmediate_operand" "=rm")
+	(ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
+		     (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RRA.W\t%H0 { RRC.W\t%L0"
+)
+
+(define_insn "sral_2"
+  [(set (match_operand:SI              0 "nonimmediate_operand" "=rm")
+	(ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
+		     (const_int 2)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RRA.W\t%H0 { RRC.W\t%L0 { RRA.W\t%H0 { RRC.W\t%L0"
+)
+
+(define_expand "ashrsi3"
+  [(set (match_operand:SI              0 "nonimmediate_operand")
+	(ashiftrt:SI (match_operand:SI 1 "general_operand")
+		     (match_operand:SI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "msp430_expand_helper (operands, \"__mspabi_sral\", true);
+   DONE;"
+)
+
+;;----------
+
+;; unsigned A >> C
+
+(define_expand "lshrhi3"
+  [(set (match_operand:HI              0 "nonimmediate_operand")
+	(lshiftrt:HI (match_operand:HI 1 "general_operand")
+		     (match_operand:HI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  {
+    if (msp430x
+        && REG_P (operands[0])
+        && REG_P (operands[1])
+        && CONST_INT_P (operands[2]))
+ emit_insn (gen_430x_logical_shift_right (operands[0], operands[1], operands[2]));
+    else		 +      msp430_expand_helper (operands, \"__mspabi_srli\", true);
+    DONE;
+  }
+)
+
+(define_insn "srli_1"
+  [(set (match_operand:HI              0 "nonimmediate_operand" "=rm")
+	(lshiftrt:HI (match_operand:HI 1 "general_operand"       "0")
+		     (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "CLRC { RRC.W\t%0"
+)
+
+(define_insn "430x_logical_shift_right"
+  [(set (match_operand:HI              0 "register_operand" "=r")
+	(lshiftrt:HI (match_operand:HI 1 "register_operand"  "0")
+		     (match_operand    2 "immediate_operand" "n")))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  {
+    return msp430x_logical_shift_right (operands[2]);
+  }
+)
+
+(define_insn "srlp_1"
+  [(set (match_operand:PSI              0 "register_operand" "=r")
+	(lshiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
+		      (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "RRUM.A #1,%0"
+)
+
+(define_insn "srll_1"
+  [(set (match_operand:SI              0 "nonimmediate_operand" "=rm")
+	(lshiftrt:SI (match_operand:SI 1 "general_operand"       "0")
+		     (const_int 1)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "CLRC { RRC.W\t%H0 { RRC.W\t%L0"
+)
+
+(define_insn "srll_2x"
+  [(set (match_operand:SI              0 "nonimmediate_operand" "=r")
+	(lshiftrt:SI (match_operand:SI 1 "general_operand"       "0")
+		     (const_int 2)))
+   (clobber (reg:CC CARRY))
+   ]
+  "msp430x"
+  "RRUX.W\t%H0 { RRC.W\t%L0 { RRUX.W\t%H0 { RRC.W\t%L0"
+)
+
+(define_expand "lshrsi3"
+  [(set (match_operand:SI              0 "nonimmediate_operand")
+	(lshiftrt:SI (match_operand:SI 1 "general_operand")
+		     (match_operand:SI 2 "general_operand")))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "msp430_expand_helper (operands, \"__mspabi_srll\", true);
+   DONE;"
+)
+
+;;------------------------------------------------------------
+;; Function Entry/Exit
+
+(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"
+ [(unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER)]
+  ""
+  "BR%A0\t#__mspabi_func_epilog_%D0"
+  )
+
+
+(define_insn "prologue_start_marker"
+  [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_START_MARKER)]
+  ""
+  "; start of prologue"
+  )
+
+(define_insn "prologue_end_marker"
+  [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_END_MARKER)]
+  ""
+  "; end of prologue"
+  )
+
+(define_insn "epilogue_start_marker"
+  [(unspec_volatile [(const_int 0)] UNS_EPILOGUE_START_MARKER)]
+  ""
+  "; start of epilogue"
+  )
+
+;;------------------------------------------------------------
+;; Jumps
+
+(define_expand "call"
+  [(call:HI (match_operand 0 "")
+	 (match_operand 1 ""))]
+  ""
+  ""
+)
+
+(define_insn "call_internal"
+  [(call (mem:HI (match_operand 0 "general_operand" "rmi"))
+	 (match_operand 1 ""))]
+  ""
+  "CALL%A0\t%0"
+)
+
+(define_expand "call_value"
+  [(set (match_operand          0 "register_operand")
+	(call:HI (match_operand 1 "general_operand")
+		 (match_operand 2 "")))]
+  ""
+  ""
+)
+
+(define_insn "call_value_internal"
+  [(set (match_operand               0 "register_operand" "=r")
+	(call (mem:HI (match_operand 1 "general_operand" "rmi"))
+	      (match_operand 2 "")))]
+  ""
+  "CALL%A0\t%1"
+)
+
+(define_expand "return"
+  [(return)]
+  "msp430_can_use_return_insn_p ()"
+)
+
+(define_insn "msp_return"
+  [(return)]
+  ""
+  { return TARGET_LARGE ? "RETA" : "RET"; }
+)
+
+;; This pattern is NOT, as expected, a return pattern.  It's called
+;; before reload and must only store its operands, and emit a
+;; placeholder where the epilog needs to be.  AFTER reload, the
+;; placeholder should get expanded into a regular-type epilogue that
+;; also does the EH return.
+(define_expand "eh_return"
+  [(match_operand:HI 0 "" "")]
+  ""
+  "msp430_expand_eh_return (operands[0]);
+   emit_jump_insn (gen_msp430_eh_epilogue ());
+   emit_barrier ();
+   DONE;"
+)
+
+;; This is the actual EH epilogue.  We emit it in the pattern above,
+;; before reload, and convert it to a real epilogue after reload.
+(define_insn_and_split "msp430_eh_epilogue"
+  [(eh_return)]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+  "msp430_expand_epilogue (1); DONE;"
+  )
+
+(define_insn "jump"
+  [(set (pc)
+	(label_ref (match_operand 0 "" "")))]
+  ""
+  "BR%A0\t#%l0"
+)
+
+;; FIXME: GCC currently (8/feb/2013) cannot handle symbol_refs
+;; in indirect jumps (cf gcc.c-torture/compile/991213-3.c).
+(define_insn "indirect_jump"
+  [(set (pc)
+	(match_operand 0 "nonimmediate_operand" "rYl"))]
+  ""
+  "BR%A0\t%0"
+)
+
+;;------------------------------------------------------------
+;; Various Conditionals
+
+(define_expand "cbranch<mode>4"
+  [(parallel [(set (pc) (if_then_else
+			 (match_operator 0 ""
+					 [(match_operand:QHI 1 "nonimmediate_operand")
+					  (match_operand:QHI 2 "general_operand")])
+			 (label_ref (match_operand 3 "" ""))
+			 (pc)))
+	      (clobber (reg:CC CARRY))]
+  )]
+  ""
+  "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 "nonimmediate_operand" "r,rYs,rm")
+			       (match_operand:PSI 2 "general_operand"      "rLs,rYsi,rmi")])
+              (label_ref (match_operand           3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  CMP%A0\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"
+  )
+
+(define_insn "cbranchqi4_real"
+  [(set (pc) (if_then_else
+	      (match_operator                    0 "msp430_cmp_operator"
+			      [(match_operand:QI 1 "nonimmediate_operand" "rYs,rm")
+			       (match_operand:QI 2 "general_operand"      "rYsi,rmi")])
+              (label_ref (match_operand          3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   CMP.B\t%2, %1 { J%0\t%l3
+   CMP%X0.B\t%2, %1 { J%0\t%l3"
+  )
+
+(define_insn "cbranchhi4_real"
+  [(set (pc) (if_then_else
+	      (match_operator                    0 "msp430_cmp_operator"
+			      [(match_operand:HI 1 "nonimmediate_operand" "rYs,rm")
+			       (match_operand:HI 2 "general_operand"      "rYsi,rmi")])
+              (label_ref (match_operand          3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   CMP.W\t%2, %1 { J%0\t%l3
+   CMP%X0.W\t%2, %1 { J%0\t%l3"
+  )
+
+(define_insn "cbranchpsi4_reversed"
+  [(set (pc) (if_then_else
+ (match_operator 0 "msp430_reversible_cmp_operator"
+			      [(match_operand:PSI 1 "general_operand" "rLs,rYsi,rmi")
+			       (match_operand:PSI 2 "general_operand" "r,rYs,rm")])
+              (label_ref (match_operand           3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+  CMP%A0\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"
+  )
+
+(define_insn "cbranchqi4_reversed"
+  [(set (pc) (if_then_else
+ (match_operator 0 "msp430_reversible_cmp_operator"
+			      [(match_operand:QI 1 "general_operand" "rYsi,rmi")
+			       (match_operand:QI 2 "general_operand" "rYs,rm")])
+              (label_ref (match_operand          3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   CMP.B\t%1, %2 { J%R0\t%l3
+   CMP%X0.B\t%1, %2 { J%R0\t%l3"
+  )
+
+(define_insn "cbranchhi4_reversed"
+  [(set (pc) (if_then_else
+ (match_operator 0 "msp430_reversible_cmp_operator"
+			      [(match_operand:HI 1 "general_operand" "rYsi,rmi")
+			       (match_operand:HI 2 "general_operand" "rYs,rm")])
+              (label_ref (match_operand          3 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   CMP.W\t%1, %2 { J%R0\t%l3
+   CMP%X0.W\t%1, %2 { J%R0\t%l3"
+  )
+
+
+(define_insn "*bitbranch<mode>4"
+  [(set (pc) (if_then_else
+ (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYs,rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rYsi,rmi"))
+		  (const_int 0))
+              (label_ref (match_operand 2 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   BIT%x0%B0\t%1, %0 { JNE\t%l2
+   BIT%X0%B0\t%1, %0 { JNE\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4"
+  [(set (pc) (if_then_else
+	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+		  (const_int 0))
+              (label_ref (match_operand 2 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%x0%X0%B0\t%1, %0 { JEQ\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4"
+  [(set (pc) (if_then_else
+	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+		  (const_int 0))
+              (pc)
+	      (label_ref (match_operand 2 "" ""))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%X0%B0\t%1, %0 { JNE\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4"
+  [(set (pc) (if_then_else
+	      (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+		  (const_int 0))
+              (pc)
+	      (label_ref (match_operand 2 "" ""))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%X0%B0\t%1, %0 { JEQ\t%l2"
+  )
+
+;;------------------------------------------------------------
+;; zero-extend versions of the above
+
+(define_insn "*bitbranch<mode>4_z"
+  [(set (pc) (if_then_else
+ (ne (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYs,rm")
+				    (const_int 1)
+				    (match_operand 1 "msp430_bitpos" "i,i"))
+		  (const_int 0))
+              (label_ref (match_operand 2 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "@
+   BIT%x0%B0\t%p1, %0 { JNE\t%l2
+   BIT%X0%B0\t%p1, %0 { JNE\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4_z"
+  [(set (pc) (if_then_else
+ (eq (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+				   (const_int 1)
+				   (match_operand 1 "msp430_bitpos" "i"))
+		  (const_int 0))
+              (label_ref (match_operand 2 "" ""))
+	      (pc)))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%x0%X0%B0\t%p1, %0 { JEQ\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4_z"
+  [(set (pc) (if_then_else
+ (eq (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+				   (const_int 1)
+				   (match_operand 1 "msp430_bitpos" "i"))
+		  (const_int 0))
+              (pc)
+	      (label_ref (match_operand 2 "" ""))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%X0%B0\t%p1, %0 { JNE\t%l2"
+  )
+
+(define_insn "*bitbranch<mode>4_z"
+  [(set (pc) (if_then_else
+ (ne (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
+				   (const_int 1)
+				   (match_operand 1 "msp430_bitpos" "i"))
+		  (const_int 0))
+              (pc)
+	      (label_ref (match_operand 2 "" ""))))
+   (clobber (reg:CC CARRY))
+   ]
+  ""
+  "BIT%X0%B0\t%p1, %0 { JEQ\t%l2"
+  )
+
+;;------------------------------------------------------------
+;; Misc
+
+(define_insn "nop"
+  [(const_int 0)]
+  "1"
+  "NOP"
+)
+  Index: gcc/config/msp430/constraints.md
===================================================================
--- gcc/config/msp430/constraints.md	(revision 0)
+++ gcc/config/msp430/constraints.md	(revision 0)
@@ -0,0 +1,74 @@
+;;  Machine Description for TI MSP43* processors
+;;  Copyright (C) 2013 Free Software Foundation, Inc.
+;;  Contributed by Red Hat.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_register_constraint "R12" "R12_REGS"
+  "Register R12.")
+
+(define_register_constraint "R13" "R13_REGS"
+  "Register R13.")
+
+(define_constraint "K"
+  "Integer constant 1."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 1, 1)")))
+
+(define_constraint "L"
+  "Integer constant -1^20..1^19."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, -1 << 20, 1 << 19)")))
+
+(define_constraint "M"
+  "Integer constant 1-4."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 1, 4)")))
+
+;; We do not allow arbitrary constants, eg symbols or labels,
+;; because their address may be above the 16-bit address limit
+;; supported by the offset used in the MOVA instruction.
+(define_constraint "Ya"
+  "Memory reference, any type, but restricted range of constants"
+  (and (match_code "mem")
+       (ior (match_code "reg" "0")
+	    (and (match_code "plus" "0")
+		 (match_code "reg" "00")
+		 (match_test ("CONST_INT_P (XEXP (XEXP (op, 0), 1))")))
+	    (match_test "CONSTANT_P (XEXP (op, 0))")
+	    )))
+
+(define_constraint "Yl"
+  "Memory reference, labels only."
+  (and (match_code "mem")
+       (match_code "label_ref" "0")))
+
+
+;; These are memory references that are safe to use with the X suffix,
+;; because we know/assume they need not index across the 64k boundary.
+(define_constraint "Ys"
+  "Memory reference, stack only."
+  (and (match_code "mem")
+       (ior
+	(and (match_code "plus" "0")
+	     (and (match_code "reg" "00")
+		  (match_test ("CONST_INT_P (XEXP (XEXP (op, 0), 1))"))
+ (match_test ("IN_RANGE (INTVAL (XEXP (XEXP (op, 0), 1)), -1 << 15, (1 << 15)-1)"))))
+	(match_code "reg" "0")
+	)))
+  +
Index: gcc/config/msp430/t-msp430
===================================================================
--- gcc/config/msp430/t-msp430	(revision 0)
+++ gcc/config/msp430/t-msp430	(revision 0)
@@ -0,0 +1,43 @@
+# Makefile fragment for building GCC for the TI MSP430 target.
+# Copyright (C) 2012-2013 Free Software Foundation, Inc.
+# Contributed by Red Hat.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published
+# by the Free Software Foundation; either version 3, or (at your
+# option) any later version.
+#
+# GCC is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See
+# the GNU General Public License for more details.
+#
+# You should have received a copy of the  GNU General Public
+# License along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Enable multilibs:
+
+MULTILIB_OPTIONS    = mmcu=msp430x mlarge
+MULTILIB_DIRNAMES   = 430x large
+
+# Match msp430X with msp430x.
+MULTILIB_MATCHES    = mmcu?msp430x=mmcu?msp430X
+
+# each supported MCU needs a line like this:
+# MULTILIB_MATCHES  += mmcu?msp430x123=mmcu?msp430x
+
+# The only way I figured this out was to hack the script to SHOW me
+# what it's doing.  It's non-obvious, but it matches the directory
+# structure of the multilib tree, but using the options, not the
+# directory names.  A shell CASE statement is generated from these, so
+# the usual CASE wildcards are supported.
+
+MULTILIB_EXCEPTIONS = mlarge
+
+MULTILIB_EXTRA_OPTS =
+
+msp430-c.o: $(srcdir)/config/msp430/msp430-c.c $(RTL_H) $(TREE_H) $(CONFIG_H) $(TM_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
Index: gcc/config/msp430/msp430.c
===================================================================
--- gcc/config/msp430/msp430.c	(revision 0)
+++ gcc/config/msp430/msp430.c	(revision 0)
@@ -0,0 +1,1836 @@
+/* Subroutines used for code generation on TI MSP430 processors.
+   Copyright (C) 2012-2013 Free Software Foundation, Inc.
+   Contributed by Red Hat.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "toplev.h"
+#include "reload.h"
+#include "df.h"
+#include "ggc.h"
+#include "tm_p.h"
+#include "debug.h"
+#include "target.h"
+#include "target-def.h"
+#include "langhooks.h"
+#include "msp430-protos.h"
+#include "dumpfile.h"
+#include "opts.h"
+
+
+
+static void msp430_compute_frame_info (void);
+static rtx gen_msp430_store_vector (unsigned int low, unsigned int high);
+static rtx gen_msp430_load_vector (unsigned int low, unsigned int high);
+
+
+
+/* Run-time Target Specification */
+
+bool msp430x = false;
+
+struct GTY(()) machine_function
+{
+  /* If set, the rest of the fields have been computed.  */
+  int computed;
+  /* Which registers need to be saved in the pro/epilogue.  */
+  int need_to_save [FIRST_PSEUDO_REGISTER];
+
+  /* These fields describe the frame layout...  */
+  /* arg pointer */
+  /* 2/4 bytes for saved PC */
+  int framesize_regs;
+  /* frame pointer */
+  int framesize_locals;
+  int framesize_outgoing;
+  /* stack pointer */
+  int framesize;
+
+  /* How much we adjust the stack when returning from an exception
+     handler.  */
+  rtx eh_stack_adjust;
+};
+
+/* This is our init_machine_status, as set in
+   msp_option_override.  */
+static struct machine_function *
+msp430_init_machine_status (void)
+{
+  struct machine_function *m;
+
+  m = ggc_alloc_cleared_machine_function ();
+
+  return m;
+}
+
+#undef  TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION msp430_handle_option
+
+bool
+msp430_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED,
+		      struct gcc_options *opts_set ATTRIBUTE_UNUSED,
+		      const struct cl_decoded_option *decoded ATTRIBUTE_UNUSED,
+		      location_t loc ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+#undef  TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE		msp430_option_override
+
+static void
+msp430_option_override (void)
+{
+  init_machine_status = msp430_init_machine_status;
+
+  if (target_cpu
+      && (strstr (target_cpu, "430x")
+	  || strstr (target_cpu, "430X")))
+    msp430x = true;
+
+  if (TARGET_LARGE && !msp430x)
+    error ("-mlarge requires a 430X-compatible -mcpu=");
+
+  if (flag_exceptions || flag_non_call_exceptions
+      || flag_unwind_tables || flag_asynchronous_unwind_tables)
+    flag_omit_frame_pointer = false;
+  else
+    flag_omit_frame_pointer = true;
+
+  /* This is a hack to work around a problem with the newlib build
+     mechanism.  Newlib always appends CFLAGS to the end of the GCC
+     command line and always sets -O2 in CFLAGS.  Thus it is not
+     possible to build newlib with -Os enabled.  Until now...  */
+  if (TARGET_OPT_SPACE && optimize < 3)
+    optimize_size = 1;
+}
+
+
+
+/* Storage Layout */
+
+#undef  TARGET_MS_BITFIELD_LAYOUT_P
+#define TARGET_MS_BITFIELD_LAYOUT_P msp430_ms_bitfield_layout_p
+
+bool
+msp430_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
+{
+  return false;
+}
+
+
+
+/* Register Usage */
+
+/* Implements HARD_REGNO_NREGS.  MSP430X registers can hold a single
+   PSImode value, but not an SImode value.  */
+int
+msp430_hard_regno_nregs (int regno ATTRIBUTE_UNUSED,
+			 enum machine_mode mode)
+{
+  if (mode == PSImode && msp430x)
+    return 1;
+  return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
+	  / UNITS_PER_WORD);
+}
+
+/* Implements HARD_REGNO_MODE_OK.  */
+int
+msp430_hard_regno_mode_ok (int regno ATTRIBUTE_UNUSED,
+			   enum machine_mode mode)
+{
+ return regno <= (ARG_POINTER_REGNUM - msp430_hard_regno_nregs (regno, mode));
+}
+
+/* Implements MODES_TIEABLE_P.  */
+bool
+msp430_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+  if ((mode1 == PSImode || mode2 == SImode)
+      || (mode1 == SImode || mode2 == PSImode))
+    return false;
+
+  return ((GET_MODE_CLASS (mode1) == MODE_FLOAT
+	   || GET_MODE_CLASS (mode1) == MODE_COMPLEX_FLOAT)
+	  == (GET_MODE_CLASS (mode2) == MODE_FLOAT
+	      || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT));
+}
+
+#undef  TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED hook_bool_void_false
+
+#undef  TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE		msp430_can_eliminate
+
+static bool
+msp430_can_eliminate (const int from_reg ATTRIBUTE_UNUSED,
+		      const int to_reg ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+/* Implements INITIAL_ELIMINATION_OFFSET.  */
+int
+msp430_initial_elimination_offset (int from ATTRIBUTE_UNUSED,
+				   int to ATTRIBUTE_UNUSED)
+{
+  int rv = 0; /* as if arg to arg */
+
+  msp430_compute_frame_info ();
+
+  switch (to)
+    {
+    case STACK_POINTER_REGNUM:
+      rv += cfun->machine->framesize_outgoing;
+      rv += cfun->machine->framesize_locals;
+      /* Fall through.  */
+    case FRAME_POINTER_REGNUM:
+      rv += cfun->machine->framesize_regs;
+      /* Allow for the saved return address.  */
+      rv += (TARGET_LARGE ? 4 : 2);
+      /* NB/ No need to allow for crtl->args.pretend_args_size.
+         GCC does that for us.  */
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      /* Allow for the fall through above.  */
+      rv -= (TARGET_LARGE ? 4 : 2);
+      rv -= cfun->machine->framesize_regs;
+    case ARG_POINTER_REGNUM:
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  return rv;
+}
+
+/* Named Address Space support */
+
+
+/* Return the appropriate mode for a named address pointer.  */
+#undef  TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE msp430_addr_space_pointer_mode
+#undef  TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE msp430_addr_space_pointer_mode
+
+static enum machine_mode
+msp430_addr_space_pointer_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    default:
+    case ADDR_SPACE_GENERIC:
+      return Pmode;
+    case ADDR_SPACE_NEAR:
+      return HImode;
+    case ADDR_SPACE_FAR:
+      return PSImode;
+    }
+}
+
+/* Function pointers are stored in unwind_word sized
+   variables, so make sure that unwind_word is big enough.  */
+#undef  TARGET_UNWIND_WORD_MODE
+#define TARGET_UNWIND_WORD_MODE msp430_unwind_word_mode
+
+static enum machine_mode
+msp430_unwind_word_mode (void)
+{
+  return TARGET_LARGE ? SImode : HImode;
+}
+
+/* Determine if one named address space is a subset of another.  */
+#undef  TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P msp430_addr_space_subset_p
+static bool
+msp430_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+  if (subset == superset)
+    return true;
+  else
+    return (subset != ADDR_SPACE_FAR && superset == ADDR_SPACE_FAR);
+}
+
+#undef  TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT msp430_addr_space_convert
+/* Convert from one address space to another.  */
+static rtx
+msp430_addr_space_convert (rtx op, tree from_type, tree to_type)
+{
+  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
+  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
+  rtx result;
+
+  if (to_as != ADDR_SPACE_FAR && from_as == ADDR_SPACE_FAR)
+    {
+      /* This is unpredictable, as we're truncating off usable address
+	 bits.  */
+
+      if (CONSTANT_P (op))
+	return gen_rtx_CONST (HImode, op);
+
+      result = gen_reg_rtx (HImode);
+      emit_insn (gen_truncpsihi2 (result, op));
+      return result;
+    }
+  else if (to_as == ADDR_SPACE_FAR && from_as != ADDR_SPACE_FAR)
+    {
+      /* This always works.  */
+
+      if (CONSTANT_P (op))
+	return gen_rtx_CONST (PSImode, op);
+
+      result = gen_reg_rtx (PSImode);
+      emit_insn (gen_zero_extendhipsi2 (result, op));
+      return result;
+    }
+  else
+    gcc_unreachable ();
+}
+
+/* Stack Layout and Calling Conventions.  */
+
+/* For each function, we list the gcc version and the TI version on
+   each line, where we're converting the function names.  */
+static char const * const special_convention_function_names [] =
+{
+  "__muldi3", "__mspabi_mpyll",
+  "__udivdi3", "__mspabi_divull",
+  "__umoddi3", "__mspabi_remull",
+  "__divdi3", "__mspabi_divlli",
+  "__moddi3", "__mspabi_remlli",
+  "__mspabi_srall",
+  "__mspabi_srlll",
+  "__mspabi_sllll",
+  "__adddf3", "__mspabi_addd",
+  "__subdf3", "__mspabi_subd",
+  "__muldf3", "__mspabi_mpyd",
+  "__divdf3", "__mspabi_divd",
+  "__mspabi_cmpd",
+  NULL
+};
+
+/* TRUE if the function passed is a "speical" function.  Special
+   functions pass two DImode parameters in registers.  */
+static bool
+msp430_special_register_convention_p (const char *name)
+{
+  int i;
+
+  for (i = 0; special_convention_function_names [i]; i++)
+    if (! strcmp (name, special_convention_function_names [i]))
+      return true;
+
+  return false;
+}
+
+#undef  TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P msp430_function_value_regno_p
+
+bool
+msp430_function_value_regno_p (unsigned int regno)
+{
+  return regno == 12;
+}
+
+
+#undef  TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE msp430_function_value
+
+rtx
+msp430_function_value (const_tree ret_type,
+		       const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+		       bool outgoing ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (TYPE_MODE (ret_type), 12);
+}
+
+#undef  TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE msp430_libcall_value
+
+rtx
+msp430_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (mode, 12);
+}
+
+/* Implements INIT_CUMULATIVE_ARGS.  */
+void
+msp430_init_cumulative_args (CUMULATIVE_ARGS *ca,
+			     tree fntype ATTRIBUTE_UNUSED,
+			     rtx libname ATTRIBUTE_UNUSED,
+			     tree fndecl ATTRIBUTE_UNUSED,
+			     int n_named_args ATTRIBUTE_UNUSED)
+{
+  const char *fname;
+  memset (ca, 0, sizeof(*ca));
+
+  ca->can_split = 1;
+
+  if (fndecl)
+    fname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+  else if (libname)
+    fname = XSTR (libname, 0);
+  else
+    fname = NULL;
+
+  if (fname && msp430_special_register_convention_p (fname))
+    ca->special_p = 1;
+}
+
+/* Helper function for argument passing; this function is the common
+   code that determines where an argument will be passed.  */
+static void
+msp430_evaluate_arg (cumulative_args_t cap,
+		     enum machine_mode mode,
+		     const_tree type ATTRIBUTE_UNUSED,
+		     bool named)
+{
+  CUMULATIVE_ARGS *ca = get_cumulative_args (cap);
+  int nregs = GET_MODE_SIZE (mode);
+  int i;
+
+  ca->reg_count = 0;
+  ca->mem_count = 0;
+
+  if (!named)
+    return;
+
+  if (mode == PSImode)
+    nregs = 1;
+  else
+    nregs = (nregs + 1) / 2;
+
+  if (ca->special_p)
+    {
+      /* Function is passed two DImode operands, in R8:R11 and
+	 R12:15.  */
+      ca->start_reg = 8;
+      ca->reg_count = 4;
+      return;
+    }
+
+  switch (nregs)
+    {
+    case 1:
+      for (i = 0; i < 4; i++)
+	if (! ca->reg_used [i])
+	  {
+	    ca->reg_count = 1;
+	    ca->start_reg = CA_FIRST_REG + i;
+	    return;
+	  }
+      break;
+    case 2:
+      for (i = 0; i < 3; i++)
+	if (! ca->reg_used [i] && ! ca->reg_used [i + 1])
+	  {
+	    ca->reg_count = 2;
+	    ca->start_reg = CA_FIRST_REG + i;
+	    return;
+	  }
+      if (! ca->reg_used [3] && ca->can_split)
+	{
+	  ca->reg_count = 1;
+	  ca->mem_count = 2;
+	  ca->start_reg = CA_FIRST_REG + 3;
+	  return;
+	}
+      break;
+    case 3:
+    case 4:
+      ca->can_split = 0;
+      if (! ca->reg_used [0]
+	  && ! ca->reg_used [1]
+	  && ! ca->reg_used [2]
+	  && ! ca->reg_used [3])
+	{
+	  ca->reg_count = 4;
+	  ca->start_reg = CA_FIRST_REG;
+	  return;
+	}
+      break;
+    }
+}
+
+#undef  TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_false
+
+#undef  TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG msp430_function_arg
+
+rtx
+msp430_function_arg (cumulative_args_t cap,
+		     enum machine_mode mode,
+		     const_tree type,
+		     bool named)
+{
+  CUMULATIVE_ARGS *ca = get_cumulative_args (cap);
+
+  msp430_evaluate_arg (cap, mode, type, named);
+
+  if (ca->reg_count)
+    return gen_rtx_REG (mode, ca->start_reg);
+
+  return 0;
+}
+
+#undef  TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES msp430_arg_partial_bytes
+
+int
+msp430_arg_partial_bytes (cumulative_args_t cap,
+			  enum machine_mode mode,
+			  tree type,
+			  bool named)
+{
+  CUMULATIVE_ARGS *ca = get_cumulative_args (cap);
+
+  msp430_evaluate_arg (cap, mode, type, named);
+
+  if (ca->reg_count && ca->mem_count)
+    return ca->reg_count * UNITS_PER_WORD;
+
+  return 0;
+}
+
+#undef  TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE msp430_pass_by_reference
+
+static bool
+msp430_pass_by_reference (cumulative_args_t cap ATTRIBUTE_UNUSED,
+			  enum machine_mode mode,
+			  const_tree type,
+			  bool named ATTRIBUTE_UNUSED)
+{
+  return (mode == BLKmode
+	  || (type && TREE_CODE (type) == RECORD_TYPE)
+	  || (type && TREE_CODE (type) == UNION_TYPE));
+}
+
+#undef  TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES msp430_callee_copies
+
+static bool
+msp430_callee_copies (cumulative_args_t cap ATTRIBUTE_UNUSED,
+                     enum machine_mode mode ATTRIBUTE_UNUSED,
+                     const_tree type ATTRIBUTE_UNUSED,
+                     bool named ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+#undef  TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE msp430_function_arg_advance
+
+void
+msp430_function_arg_advance (cumulative_args_t cap,
+			     enum machine_mode mode,
+			     const_tree type,
+			     bool named)
+{
+  CUMULATIVE_ARGS *ca = get_cumulative_args (cap);
+  int i;
+
+  msp430_evaluate_arg (cap, mode, type, named);
+
+  if (ca->start_reg >= CA_FIRST_REG)
+    for (i = 0; i < ca->reg_count; i ++)
+      ca->reg_used [i + ca->start_reg - CA_FIRST_REG] = 1;
+
+  ca->special_p = 0;
+}
+
+#undef  TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY msp430_function_arg_boundary
+
+static unsigned int
+msp430_function_arg_boundary (enum machine_mode mode, const_tree type)
+{
+  if (mode == BLKmode
+      && int_size_in_bytes (type) > 1)
+    return 16;
+  if (GET_MODE_BITSIZE (mode) > 8)
+    return 16;
+  return 8;
+}
+
+#undef  TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY msp430_return_in_memory
+
+static bool
+msp430_return_in_memory (const_tree ret_type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+  enum machine_mode mode = TYPE_MODE (ret_type);
+
+  if (mode == BLKmode
+      || (fntype && TREE_CODE (TREE_TYPE (fntype)) == RECORD_TYPE)
+      || (fntype && TREE_CODE (TREE_TYPE (fntype)) == UNION_TYPE))
+    return true;
+
+  if (GET_MODE_SIZE (mode) > 8)
+    return true;
+
+  return false;
+}
+
+#undef  TARGET_GET_RAW_RESULT_MODE
+#define TARGET_GET_RAW_RESULT_MODE msp430_get_raw_result_mode
+
+static enum machine_mode
+msp430_get_raw_result_mode (int regno ATTRIBUTE_UNUSED)
+{
+  return Pmode;
+}
+
+/* Addressing Modes */
+
+#undef  TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P msp430_legitimate_address_p
+
+static bool
+reg_ok_for_addr (rtx r, bool strict)
+{
+  int rn = REGNO (r);
+
+  if (strict && rn >= FIRST_PSEUDO_REGISTER)
+    rn = reg_renumber [rn];
+  if (strict && 0 <= rn && rn < FIRST_PSEUDO_REGISTER)
+    return true;
+  if (!strict)
+    return true;
+  return false;
+}
+
+bool
+msp430_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+			     rtx x ATTRIBUTE_UNUSED,
+			     bool strict ATTRIBUTE_UNUSED)
+{
+  switch (GET_CODE (x))
+    {
+    case MEM:
+      return false;
+
+    case PLUS:
+      if (REG_P (XEXP (x, 0)))
+	{
+	  if (GET_MODE (x) != GET_MODE (XEXP (x, 0)))
+	    return false;
+	  if (!reg_ok_for_addr (XEXP (x, 0), strict))
+	    return false;
+	  switch (GET_CODE (XEXP (x, 1)))
+	    {
+	    case CONST:
+	    case SYMBOL_REF:
+	    case CONST_INT:
+	      return true;
+	    default:
+	      return false;
+	    }
+	}
+      return false;
+
+    case REG:
+      if (!reg_ok_for_addr (x, strict))
+	return false;
+      /* else... */
+    case CONST:
+    case SYMBOL_REF:
+    case CONST_INT:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+#undef  TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P msp430_legitimate_constant
+
+static bool
+msp430_legitimate_constant (enum machine_mode mode, rtx x)
+{
+  return ! CONST_INT_P (x)
+    || mode != PSImode
+    /* GCC does not know the width of the PSImode, so make
+       sure that it does not try to use a constant value that
+       is out of range.  */
+    || (INTVAL (x) < (1 << 20) && INTVAL (x) >= (-1 << 20));
+}
+
+
+#undef  TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS msp430_rtx_costs
+
+static bool msp430_rtx_costs (rtx   x ATTRIBUTE_UNUSED,
+			      int   code,
+			      int   outer_code ATTRIBUTE_UNUSED,
+			      int   opno ATTRIBUTE_UNUSED,
+			      int * total,
+			      bool  speed ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case SIGN_EXTEND:
+      if (GET_MODE (x) == SImode && outer_code == SET)
+	{
+	  *total = COSTS_N_INSNS (4);
+	  return true;
+	}
+      break;
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      if (!msp430x)
+	{
+	  *total = COSTS_N_INSNS (100);
+	  return true;
+	}
+      break;
+    }
+  return false;
+}
+
+/* Function Entry and Exit */
+
+/* The MSP430 call frame looks like this:
+
+   <higher addresses>
+   +--------------------+
+   |                    |
+   | Stack Arguments    |
+   |                    |
+   +--------------------+ <-- "arg pointer"
+   |                    |
+   | PC from call       |  (2 bytes for 430, 4 for TARGET_LARGE)
+   |                    |
+   +--------------------+  <-- SP before prologue, also AP
+   |                    |
+ | Saved Regs | (2 bytes per reg for 430, 4 per for TARGET_LARGE)
+   |                    |
+   +--------------------+  <-- "frame pointer"
+   |                    |
+   | Locals             |
+   |                    |
+   +--------------------+
+   |                    |
+   | Outgoing Args      |
+   |                    |
+   +--------------------+  <-- SP during function
+   <lower addresses>
+
+*/
+
+/* We use this to wrap all emitted insns in the prologue, so they get
+   the "frame-related" (/f) flag set.  */
+static rtx
+F (rtx x)
+{
+  if (INSN_P (x) && GET_CODE (PATTERN (x)) == PARALLEL)
+    {
+      unsigned int i;
+
+      x = PATTERN (x);
+      for (i = 0; i < (unsigned) XVECLEN (x, 0); i++)
+	{
+	  rtx r = XVECEXP (x, 0, i);
+	  if (GET_CODE (r) == SET)
+	    RTX_FRAME_RELATED_P (r) = 1;
+	}
+    }
+  else
+    RTX_FRAME_RELATED_P (x) = 1;
+  return x;
+}
+
+/* This is the one spot that decides if a register is to be saved and
+   restored in the prologue/epilogue.  */
+static bool
+msp430_preserve_reg_p (int regno)
+{
+  /* PC, SP, SR, and the constant generator.  */
+  if (regno <= 3)
+    return false;
+
+  /* FIXME: add interrupt, EH, etc.  */
+  if (crtl->calls_eh_return)
+    return true;
+
+  /* Shouldn't be more than the above, but just in case...  */
+  if (fixed_regs [regno])
+    return false;
+
+  if (!call_used_regs [regno]
+      && df_regs_ever_live_p (regno))
+    return true;
+
+  return false;
+}
+
+/* Compute all the frame-related fields in our machine_function
+   structure.  */
+static void
+msp430_compute_frame_info (void)
+{
+  int i;
+
+  cfun->machine->computed = 1;
+  cfun->machine->framesize_regs = 0;
+  cfun->machine->framesize_locals = get_frame_size ();
+  cfun->machine->framesize_outgoing = crtl->outgoing_args_size;
+
+  for (i = 0; i < 16; i ++)
+    if (msp430_preserve_reg_p (i))
+      {
+	cfun->machine->need_to_save [i] = 1;
+	cfun->machine->framesize_regs += (msp430x ? 4 : 2);
+      }
+    else
+      cfun->machine->need_to_save [i] = 0;
+
+ if ((cfun->machine->framesize_locals + cfun->machine->framesize_outgoing) & 1)
+    cfun->machine->framesize_locals ++;
+
+  cfun->machine->framesize = (cfun->machine->framesize_regs
+			      + cfun->machine->framesize_locals
+			      + cfun->machine->framesize_outgoing);
+}
+
+#undef  TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE	msp430_start_function
+
+static void
+msp430_start_function (FILE *outfile, HOST_WIDE_INT hwi_local ATTRIBUTE_UNUSED)
+{
+  int r, n;
+
+  fprintf (outfile, "; start of function\n");
+ fprintf (outfile, "; framesize_regs: %d\n", cfun->machine->framesize_regs); + fprintf (outfile, "; framesize_locals: %d\n", cfun->machine->framesize_locals); + fprintf (outfile, "; framesize_outgoing: %d\n", cfun->machine->framesize_outgoing); + fprintf (outfile, "; framesize: %d\n", cfun->machine->framesize); + fprintf (outfile, "; elim ap -> fp %d\n", msp430_initial_elimination_offset (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM)); + fprintf (outfile, "; elim fp -> sp %d\n", msp430_initial_elimination_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM));
+
+  n = 0;
+  fprintf (outfile, "; saved regs:");
+  for (r = 0; r < 16; r++)
+    if (cfun->machine->need_to_save [r])
+      {
+	fprintf (outfile, " %s", reg_names [r]);
+	n = 1;
+      }
+  if (n == 0)
+    fprintf (outfile, "(none)");
+  fprintf (outfile, "\n");
+}
+
+/* Common code to change the stack pointer.  */
+static void
+increment_stack (HOST_WIDE_INT amount)
+{
+  rtx inc;
+  rtx sp =  stack_pointer_rtx;
+
+  if (amount == 0)
+    return;
+
+  if (amount < 0)
+    {
+      inc = GEN_INT (- amount);
+      if (TARGET_LARGE)
+	F (emit_insn (gen_subpsi3 (sp, sp, inc)));
+      else
+	F (emit_insn (gen_subhi3 (sp, sp, inc)));
+    }
+  else
+    {
+      inc = GEN_INT (amount);
+      if (TARGET_LARGE)
+	emit_insn (gen_addpsi3 (sp, sp, inc));
+      else
+	emit_insn (gen_addhi3 (sp, sp, inc));
+    }
+}
+
+void
+msp430_expand_prologue (void)
+{
+  int i, j;
+  int fs;
+  /* Always use stack_pointer_rtx instead of calling
+     rtx_gen_REG ourselves.  Code elsewhere in GCC assumes
+     that there is a single rtx representing the stack pointer,
+     namely stack_pointer_rtx, and uses == to recognize it.  */
+  rtx sp = stack_pointer_rtx;
+  rtx p;
+
+  emit_insn (gen_prologue_start_marker ());
+
+  if (!cfun->machine->computed)
+    msp430_compute_frame_info ();
+
+  if (flag_stack_usage_info)
+    current_function_static_stack_size = cfun->machine->framesize;
+  +  if (crtl->args.pretend_args_size)
+    {
+      rtx note;
+
+      gcc_assert (crtl->args.pretend_args_size == 2);
+      +      p = emit_insn (gen_grow_and_swap ());
+
+      /* Document the stack decrement...  */
+      note = F (gen_rtx_SET (Pmode, stack_pointer_rtx,
+			     gen_rtx_MINUS (Pmode, stack_pointer_rtx, GEN_INT (2))));
+      add_reg_note (p, REG_FRAME_RELATED_EXPR, note);
+
+ /* ...and the establishment of a new location for the return address. */
+      note = F (gen_rtx_SET (Pmode, gen_rtx_MEM (Pmode,
+						 gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (-2))),
+			     pc_rtx));
+      add_reg_note (p, REG_CFA_OFFSET, note);
+      F (p);
+    }
+
+  for (i = 15; i >= 4; i--)
+    if (cfun->machine->need_to_save [i])
+      {
+	int seq, count;
+	rtx note;
+
+	for (seq = i - 1; seq >= 4 && cfun->machine->need_to_save[seq]; seq --)
+	  ;
+	count = i - seq;
+
+	if (msp430x)
+	  {
+ /* Note: with TARGET_LARGE we still use PUSHM as PUSHX.A is two bytes bigger. */
+	    p = F (emit_insn (gen_pushm (GEN_INT (count * 4),
+					 gen_msp430_store_vector (i-count+1, i))));
+
+	    i -= count - 1;
+	  }
+	else
+	  F (emit_insn (gen_push (gen_rtx_REG (Pmode, i))));
+      }
+
+  if (frame_pointer_needed)
+    F (emit_move_insn (gen_rtx_REG (Pmode, FRAME_POINTER_REGNUM), sp));
+
+  fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+
+  increment_stack (- fs);
+
+  emit_insn (gen_prologue_end_marker ());
+}
+
+void
+msp430_expand_epilogue (int is_eh)
+{
+  int i;
+  int fs;
+  int helper_n = 0;
+
+  if (cfun->machine->need_to_save [10])
+    {
+      /* Check for a helper function.  */
+      helper_n = 7; /* for when the loop below never sees a match.  */
+      for (i = 9; i >= 4; i--)
+	if (!cfun->machine->need_to_save [i])
+	  {
+	    helper_n = 10 - i;
+	    for (; i >= 4; i--)
+	      if (cfun->machine->need_to_save [i])
+		{
+		  helper_n = 0;
+		  break;
+		}
+	    break;
+	  }
+    }
+
+  emit_insn (gen_epilogue_start_marker ());
+
+  fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+
+  increment_stack (fs);
+
+  if (is_eh)
+    {
+      /* We need to add the right "SP" register save just after the
+	 regular ones, so that when we pop it off we're in the EH
+	 return frame, not this one.  This overwrites our own return
+	 address, but we're not going to be returning anyway.  */
+      rtx r12 = gen_rtx_REG (Pmode, 12);
+ rtx (*addPmode)(rtx, rtx, rtx) = TARGET_LARGE ? gen_addpsi3 : gen_addhi3;
+
+      /* R12 will hold the new SP.  */
+      i = cfun->machine->framesize_regs;
+      emit_move_insn (r12, stack_pointer_rtx);
+      emit_insn (addPmode (r12, r12, EH_RETURN_STACKADJ_RTX));
+      emit_insn (addPmode (r12, r12, GEN_INT (i)));
+ emit_move_insn (gen_rtx_MEM (Pmode, plus_constant (Pmode, stack_pointer_rtx, i)), r12);
+    }
+
+  for (i = 4; i <= 15; i++)
+    if (cfun->machine->need_to_save [i])
+      {
+	int seq, count;
+
+	for (seq = i + 1; seq <= 15 && cfun->machine->need_to_save[seq]; seq ++)
+	  ;
+	count = seq - i;
+
+	if (msp430x)
+	  {
+	    /* Note: With TARGET_LARGE we still use POPM as POPX.A is two
+	       bytes bigger.
+	       Note: See the popm pattern for the explanation of the strange
+	       arguments.  */
+	    emit_insn (gen_popm (GEN_INT (count * 4),
+				 gen_msp430_load_vector (i, seq - 1)));
+	    i += count - 1;
+	  }
+	else if (i == 11 - helper_n
+		 && crtl->args.pretend_args_size == 0
+		 /* Calling the helper takes as many bytes as the POP;RET sequence.  */
+		 && helper_n != 1
+		 && !is_eh)
+	  {
+	    emit_insn (gen_epilogue_helper (GEN_INT (helper_n)));
+	    return;
+	  }
+	else
+	  emit_insn (gen_pop (gen_rtx_REG (Pmode, i)));
+      }
+
+  if (is_eh)
+    {
+      /* Also pop SP, which puts us into the EH return frame.  Except
+	 that you can't "pop" sp, you have to just load it off the
+	 stack.  */
+ emit_move_insn (stack_pointer_rtx, gen_rtx_MEM (Pmode, stack_pointer_rtx));
+    }
+
+  if (crtl->args.pretend_args_size)
+    emit_insn (gen_swap_and_shrink ());
+    +  emit_jump_insn (gen_msp_return ());
+}
+
+/* Implements EH_RETURN_STACKADJ_RTX.  Saved and used later in
+   m32c_emit_eh_epilogue.  */
+rtx
+msp430_eh_return_stackadj_rtx (void)
+{
+  if (!cfun->machine->eh_stack_adjust)
+    {
+      rtx sa;
+
+      sa = gen_rtx_REG (Pmode, 15);
+      cfun->machine->eh_stack_adjust = sa;
+    }
+  return cfun->machine->eh_stack_adjust;
+}
+
+/* This function is called before reload, to "fix" the stack in
+   preparation for an EH return.  */
+void
+msp430_expand_eh_return (rtx eh_handler)
+{
+  /* These are all Pmode */
+  rtx ap, sa, ra, tmp;
+
+  ap = arg_pointer_rtx;
+  sa = msp430_eh_return_stackadj_rtx ();
+  ra = eh_handler;
+
+  tmp = ap;
+  tmp = gen_rtx_PLUS (Pmode, ap, sa);
+  tmp = plus_constant (Pmode, tmp, TARGET_LARGE ? -4 : -2);
+  tmp = gen_rtx_MEM (Pmode, tmp);
+  emit_move_insn (tmp, ra);
+}
+
+bool
+msp430_can_use_return_insn_p (void)
+{
+  if (! reload_completed || frame_pointer_needed)
+    return 0;
+
+  if (!cfun->machine->computed)
+    msp430_compute_frame_info ();
+
+  return (cfun->machine->framesize == 0);
+}
+
+/* This is a list of MD patterns that implement fixed-count shifts.  */
+static struct {
+  const char *name;
+  int count;
+  int need_430x;
+  rtx (*genfunc)(rtx,rtx);
+} const_shift_helpers[] = {
+#define CSH(N,C,X,G) { "__mspabi_"N, C, X, gen_##G }
+
+  CSH ("slli", 1, 1, slli_1),
+  CSH ("slll", 1, 1, slll_1),
+  CSH ("slll", 2, 1, slll_2),
+
+  CSH ("srai", 1, 0, srai_1),
+  CSH ("sral", 1, 0, sral_1),
+  CSH ("sral", 2, 0, sral_2),
+
+  CSH ("srll", 1, 0, srll_1),
+  CSH ("srll", 2, 1, srll_2x),
+  { 0, 0, 0, 0 }
+#undef CSH
+};
+
+/* The MSP430 ABI defines a number of helper functions that should be
+   used for, for example, 32-bit shifts.  This function is called to
+   emit such a function, using the table above to optimize some
+   cases.  */
+void
+msp430_expand_helper (rtx *operands, const char *helper_name, bool const_variants)
+{
+  rtx c, f;
+  char *helper_const = NULL;
+  int arg2 = 13;
+  int arg1sz = 1;
+  enum machine_mode arg0mode = GET_MODE (operands[0]);
+  enum machine_mode arg1mode = GET_MODE (operands[1]);
+  enum machine_mode arg2mode = GET_MODE (operands[2]);
+  int have_430x = msp430x ? 1 : 0;
+
+  if (CONST_INT_P (operands[2]))
+    {
+      int i;
+
+      for (i=0; const_shift_helpers[i].name; i++)
+	{
+	  if (const_shift_helpers[i].need_430x <= have_430x
+	      && strcmp (helper_name, const_shift_helpers[i].name) == 0
+	      && INTVAL (operands[2]) == const_shift_helpers[i].count)
+	    {
+ emit_insn (const_shift_helpers[i].genfunc (operands[0], operands[1]));
+	      return;
+	    }
+	}
+    }
+
+  if (arg1mode == VOIDmode)
+    arg1mode = arg0mode;
+  if (arg2mode == VOIDmode)
+    arg2mode = arg0mode;
+
+  if (arg1mode == SImode)
+    {
+      arg2 = 14;
+      arg1sz = 2;
+    }
+
+  if (const_variants
+      && CONST_INT_P (operands[2])
+      && INTVAL (operands[2]) >= 1
+      && INTVAL (operands[2]) <= 15)
+    {
+ /* Note that the INTVAL is limited in value and length by the conditional above. */
+      int len = strlen (helper_name) + 4;
+      helper_const = (char *) xmalloc (len);
+ snprintf (helper_const, len, "%s_%d", helper_name, (int) INTVAL (operands[2]));
+    }
+
+  emit_move_insn (gen_rtx_REG (arg1mode, 12),
+		  operands[1]);
+  if (!helper_const)
+    emit_move_insn (gen_rtx_REG (arg2mode, arg2),
+		    operands[2]);
+
+  c = gen_call_value_internal (gen_rtx_REG (arg0mode, 12),
+ gen_rtx_SYMBOL_REF (VOIDmode, helper_const ? helper_const : helper_name),
+			       GEN_INT (0));
+  c = emit_call_insn (c);
+  RTL_CONST_CALL_P (c) = 1;
+
+  f = 0;
+  use_regs (&f, 12, arg1sz);
+  if (!helper_const)
+    use_regs (&f, arg2, 1);
+  add_function_usage_to (c, f);
+
+  emit_move_insn (operands[0],
+		  gen_rtx_REG (arg0mode, 12));
+}
+
+/* Called by cbranch<mode>4 to coerce operands into usable forms.  */
+void
+msp430_fixup_compare_operands (enum machine_mode my_mode, rtx * operands)
+{
+  /* constants we're looking for, not constants which are allowed.  */
+  int const_op_idx = 1;
+
+  if (msp430_reversible_cmp_operator (operands[0], VOIDmode))
+    const_op_idx = 2;
+
+  if (GET_CODE (operands[const_op_idx]) != REG
+      && GET_CODE (operands[const_op_idx]) != MEM)
+ operands[const_op_idx] = copy_to_mode_reg (my_mode, operands[const_op_idx]);
+}
+
+/* Simplify_gen_subreg() doesn't handle memory references the way we
+   need it to below, so we use this function for when we must get a
+   valid subreg in a "natural" state.  */
+rtx
+msp430_subreg (enum machine_mode mode, rtx r, enum machine_mode omode, int byte)
+{
+  rtx rv;
+
+  if (GET_CODE (r) == SUBREG
+      && SUBREG_BYTE (r) == 0)
+    {
+      rtx ireg = SUBREG_REG (r);
+      enum machine_mode imode = GET_MODE (ireg);
+
+      /* special case for (HI (SI (PSI ...), 0)) */
+      if (imode == PSImode
+	  && mode == HImode
+	  && byte == 0)
+	rv = gen_rtx_SUBREG (mode, ireg, byte);
+      else
+	rv = simplify_gen_subreg (mode, ireg, imode, byte);
+    }
+  else if (GET_CODE (r) == MEM)
+    rv = adjust_address (r, mode, byte);
+  else
+    rv = simplify_gen_subreg (mode, r, omode, byte);
+
+  if (!rv)
+    gcc_unreachable ();
+
+  return rv;
+}
+
+/* Called by movsi_x to generate the HImode operands.  */
+void
+msp430_split_movsi (rtx *operands)
+{
+  rtx op00, op02, op10, op12;
+
+  op00 = msp430_subreg (HImode, operands[0], SImode, 0);
+  op02 = msp430_subreg (HImode, operands[0], SImode, 2);
+
+  if (GET_CODE (operands[1]) == CONST
+      || GET_CODE (operands[1]) == SYMBOL_REF)
+    {
+ op10 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (0));
+      op10 = gen_rtx_CONST (HImode, op10);
+ op12 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (16));
+      op12 = gen_rtx_CONST (HImode, op12);
+    }
+  else
+    {
+      op10 = msp430_subreg (HImode, operands[1], SImode, 0);
+      op12 = msp430_subreg (HImode, operands[1], SImode, 2);
+    }
+
+  if (rtx_equal_p (operands[0], operands[1]))
+    {
+      operands[2] = op02;
+      operands[4] = op12;
+      operands[3] = op00;
+      operands[5] = op10;
+    }
+  else if (rtx_equal_p (op00, op12)
+	   /* Catch the case where we are loading (rN, rN+1) from mem (rN).  */
+	   || (REG_P (op00) && reg_mentioned_p (op00, op10))
+	   /* Or storing (rN) into mem (rN).  */
+	   || (REG_P (op10) && reg_mentioned_p (op10, op00))
+	   )
+    {
+      operands[2] = op02;
+      operands[4] = op12;
+      operands[3] = op00;
+      operands[5] = op10;
+    }
+  else
+    {
+      operands[2] = op00;
+      operands[4] = op10;
+      operands[3] = op02;
+      operands[5] = op12;
+    }
+}
+
+/* PUSHM #n,Rdst - push N regsters, starting with Rdst and ending with
+   Rdst-n+1.  */
+
+/* Generate a PUSHM instruction that matches the given operands.  */
+
+char *
+msp430_emit_pushm (rtx * operands)
+{
+  static char pattern[20];
+  HOST_WIDE_INT last_reg;
+  rtx first_push;
+
+  gcc_assert (CONST_INT_P (operands[0]));
+  last_reg = (INTVAL (operands[0]) / 4) - 1;
+
+  gcc_assert (GET_CODE (operands[1]) == PARALLEL);
+  first_push = XVECEXP (operands[1], 0, 1);
+  gcc_assert (GET_CODE (first_push) == SET);
+  first_push = SET_SRC (first_push);
+  gcc_assert (REG_P (first_push));
+
+  sprintf (pattern, "PUSHM.A\t#%d,%s",
+	   last_reg + 1,
+	   reg_names [REGNO (first_push)]);
+  return pattern;
+}
+
+/* Generate a PARALLEL that will pass the msp430_store_multiple_vector_p predicate. */
+
+static rtx
+gen_msp430_store_vector (unsigned int low, unsigned int high)
+{
+  unsigned int i;
+  unsigned int count = (high - low) + 2;
+  rtx vector;
+
+  vector = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
+
+  XVECEXP (vector, 0, 0) =
+    gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+		 gen_rtx_MINUS (Pmode, stack_pointer_rtx,
+				GEN_INT ((count - 1) * 4)));
+
+  for (i = 0; i < count - 1; i++)
+    XVECEXP (vector, 0, i + 1) =
+      gen_rtx_SET (VOIDmode,
+		   gen_rtx_MEM (PSImode,
+				gen_rtx_MINUS (Pmode, stack_pointer_rtx,
+					       GEN_INT ((i + 1) * 4))),
+		   gen_rtx_REG (PSImode, high - i));
+  return vector;
+}
+
+/* POPM #n,Rdst - push N regsters, starting with Rdst-n+1 and ending
+   with Rdst.  */
+
+/* Generate a POPM instruction that matches the given operands.  */
+
+char *
+msp430_emit_popm (rtx * operands)
+{
+  static char pattern[20];
+  HOST_WIDE_INT last_reg;
+  rtx first_push;
+
+  gcc_assert (CONST_INT_P (operands[0]));
+  last_reg = (INTVAL (operands[0]) / 4) - 1;
+
+  gcc_assert (GET_CODE (operands[1]) == PARALLEL);
+  first_push = XVECEXP (operands[1], 0, 1);
+  gcc_assert (GET_CODE (first_push) == SET);
+  first_push = SET_DEST (first_push);
+  gcc_assert (REG_P (first_push));
+
+  sprintf (pattern, "POPM.A\t#%d,%s",
+	   last_reg + 1,
+	   reg_names [REGNO (first_push)]);
+  return pattern;
+}
+
+/* Generate a PARALLEL that will pass the msp430_store_multiple_vector_p predicate. */
+
+static rtx
+gen_msp430_load_vector (unsigned int low, unsigned int high)
+{
+  unsigned int i;
+  unsigned int count = (high - low) + 2;
+  rtx vector;
+
+  vector = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
+
+  XVECEXP (vector, 0, 0) =
+    gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+		 gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+				GEN_INT ((count - 1) * 4)));
+
+  for (i = 0; i < count - 1; i++)
+    XVECEXP (vector, 0, i + 1) =
+      gen_rtx_SET (VOIDmode,
+		   gen_rtx_REG (PSImode, high - i),
+		   gen_rtx_MEM (PSImode,
+				gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+					      GEN_INT (count * 4 - (i + 2) * 4))));
+  return vector;
+}
+
+
+
+/* The MSPABI specifies the names of various helper functions, many of
+   which are compatible with GCC's helpers.  This table maps the GCC
+   name to the MSPABI name.  */
+static const struct
+{
+  char const * const gcc_name;
+  char const * const ti_name;
+}
+  helper_function_name_mappings [] =
+{
+  /* Floating point to/from integer conversions.  */
+  { "__truncdfsf2", "__mspabi_cvtdf" },
+  { "__extendsfdf2", "__mspabi_cvtfd" },
+  { "__fixdfhi", "__mspabi_fixdi" },
+  { "__fixdfsi", "__mspabi_fixdli" },
+  { "__fixdfdi", "__mspabi_fixdlli" },
+  { "__fixunsdfhi", "__mspabi_fixdu" },
+  { "__fixunsdfsi", "__mspabi_fixdul" },
+  { "__fixunsdfdi", "__mspabi_fixdull" },
+  { "__fixsfhi", "__mspabi_fixfi" },
+  { "__fixsfsi", "__mspabi_fixfli" },
+  { "__fixsfdi", "__mspabi_fixflli" },
+  { "__fixunsfhi", "__mspabi_fixfu" },
+  { "__fixunsfsi", "__mspabi_fixful" },
+  { "__fixunsfdi", "__mspabi_fixfull" },
+  { "__floathisf", "__mspabi_fltif" },
+  { "__floatsisf", "__mspabi_fltlif" },
+  { "__floatdisf", "__mspabi_fltllif" },
+  { "__floathidf", "__mspabi_fltid" },
+  { "__floatsidf", "__mspabi_fltlid" },
+  { "__floatdidf", "__mspabi_fltllid" },
+  { "__floatunhisf", "__mspabi_fltuf" },
+  { "__floatunsisf", "__mspabi_fltulf" },
+  { "__floatundisf", "__mspabi_fltullf" },
+  { "__floatunhidf", "__mspabi_fltud" },
+  { "__floatunsidf", "__mspabi_fltuld" },
+  { "__floatundidf", "__mspabi_fltulld" },
+
+  /* Floating point comparisons.  */
+  /* GCC uses individual functions for each comparison, TI uses one
+     compare <=> function.  */
+
+  /* Floating point arithmatic */
+  { "__adddf3", "__mspabi_addd" },
+  { "__addsf3", "__mspabi_addf" },
+  { "__divdf3", "__mspabi_divd" },
+  { "__divsf3", "__mspabi_divf" },
+  { "__muldf3", "__mspabi_mpyd" },
+  { "__mulsf3", "__mspabi_mpyf" },
+  { "__subdf3", "__mspabi_subd" },
+  { "__subsf3", "__mspabi_subf" },
+  /* GCC does not use helper functions for negation */
+
+  /* Integer multiply, divide, remainder.  */
+  /* Note: gcc doesn't know about hardware multiply options (yet?)  */
+  { "__mulhi3", "__mspabi_mpyi" },
+  { "__mulsi3", "__mspabi_mpyl" },
+  { "__muldi3", "__mspabi_mpyll" },
+#if 0
+  /* Clarify signed vs unsigned first.  */
+ { "__mulhisi3", "__mspabi_mpysl" }, /* gcc doesn't use widening multiply (yet?) */ + { "__mulsidi3", "__mspabi_mpysll" }, /* gcc doesn't use widening multiply (yet?) */
+#endif
+
+  { "__divhi3", "__mspabi_divi" },
+  { "__divsi3", "__mspabi_divli" },
+  { "__divdi3", "__mspabi_divlli" },
+  { "__udivhi3", "__mspabi_divu" },
+  { "__udivsi3", "__mspabi_divlu" },
+  { "__udivdi3", "__mspabi_divllu" },
+  { "__modhi3", "__mspabi_remi" },
+  { "__modsi3", "__mspabi_remli" },
+  { "__moddi3", "__mspabi_remlli" },
+  { "__umodhi3", "__mspabi_remu" },
+  { "__umodsi3", "__mspabi_remul" },
+  { "__umoddi3", "__mspabi_remull" },
+
+  /* Bitwise operations.  */
+  /* Rotation - no rotation support yet.  */
+  /* Logical left shift - gcc already does these itself.  */
+  /* Arithmetic left shift - gcc already does these itself.  */
+  /* Arithmetic right shift - gcc already does these itself.  */
+
+  { NULL, NULL }
+};
+
+/* This function does the same as the default, but it will replace GCC
+   function names with the MSPABI-specified ones.  */
+void
+msp430_output_labelref (FILE *file, const char *name)
+{
+  int i;
+
+  for (i = 0; helper_function_name_mappings [i].gcc_name; i++)
+    if (! strcmp (helper_function_name_mappings [i].gcc_name, name))
+      {
+	fputs (helper_function_name_mappings [i].ti_name, file);
+	return;
+      }
+
+  fputs (name, file);
+}
+
+#undef  TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND		msp430_print_operand
+
+/* Common code for msp430_print_operand().  */
+static void
+msp430_print_operand_raw (FILE * file, rtx op, int letter ATTRIBUTE_UNUSED)
+{
+  int i;
+
+  switch (GET_CODE (op))
+    {
+    case REG:
+      fprintf (file, "%s", reg_names [REGNO (op)]);
+      break;
+
+    case CONST_INT:
+      i = INTVAL (op);
+      if (TARGET_ASM_HEX)
+	fprintf (file, "%#x", i);
+      else
+	fprintf (file, "%d", i);
+      break;
+
+    case CONST:
+    case PLUS:
+    case MINUS:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      output_addr_const (file, op);
+      break;
+
+    default:
+      print_rtl (file, op);
+      break;
+    }
+}
+
+static void
+msp430_print_operand (FILE * file, rtx op, int letter)
+{
+  rtx addr;
+
+  /* We can't use c, n, a, or l.  */
+  switch (letter)
+    {
+    case 'Z':
+      gcc_assert (CONST_INT_P (op));
+      /* Print the constant value, less one.  */
+      fprintf (file, "#%ld", INTVAL (op) - 1);
+      return;
+    case 'Y':
+      gcc_assert (CONST_INT_P (op));
+      /* Print the constant value, less four.  */
+      fprintf (file, "#%ld", INTVAL (op) - 4);
+      return;
+    case 'D':
+      /* print a decimal value without a leading '#'.  */
+      if (GET_CODE (op) == CONST_INT)
+	{
+	  int i = INTVAL (op);
+	  fprintf (file, "%d", i);
+	  return;
+	}
+      break;
+    case 'I':
+      if (GET_CODE (op) == CONST_INT)
+	{
+	  /* Inverse of constants */
+	  int i = INTVAL (op);
+	  fprintf (file, "%d", ~i);
+	  return;
+	}
+      op = XEXP (op, 0);
+      break;
+    case 'r': /* Conditional jump where the condition is reversed.  */
+      switch (GET_CODE (op))
+	{
+	case EQ: fprintf (file, "NE"); break;
+	case NE: fprintf (file, "EQ"); break;
+	case GEU: fprintf (file, "LO"); break;
+	case LTU: fprintf (file, "HS"); break;
+	case GE: fprintf (file, "L"); break;
+	case LT: fprintf (file, "GE"); break;
+	  /* Assume these have reversed operands.  */
+	case GTU: fprintf (file, "HS"); break;
+	case LEU: fprintf (file, "LO"); break;
+	case GT: fprintf (file, "GE"); break;
+	case LE: fprintf (file, "L"); break;
+	default:
+	  msp430_print_operand_raw (file, op, letter);
+	  break;
+	}
+      return;
+    case 'R': /* Conditional jump where the operands are reversed.  */
+      switch (GET_CODE (op))
+	{
+	case GTU: fprintf (file, "LO"); break;
+	case LEU: fprintf (file, "HS"); break;
+	case GT: fprintf (file, "L"); break;
+	case LE: fprintf (file, "GE"); break;
+	default:
+	  msp430_print_operand_raw (file, op, letter);
+	  break;
+	}
+      return;
+    case 'p': /* Bit position. 0 == 0x01, 3 = 0x08 etc.  */
+      gcc_assert (CONST_INT_P (op));
+      fprintf (file, "#%d", 1 << INTVAL (op));
+      return;
+    case 'B':
+      switch (GET_MODE (op))
+	{
+	case QImode: fprintf (file, ".B"); return;
+	case HImode: fprintf (file, ".W"); return;
+	case PSImode: fprintf (file, ".A"); return;
+	case SImode: fprintf (file, ".A"); return;
+	default:
+	  return;
+	}
+    case 'L': /* Low half.  */
+      switch (GET_CODE (op))
+	{
+	case MEM:
+	  op = adjust_address (op, Pmode, 0);
+	  break;
+	case REG:
+	  break;
+	case CONST_INT:
+	  op = GEN_INT (INTVAL (op) & 0xffff);
+	  letter = 0;
+	  break;
+	default:
+	  /* If you get here, figure out a test case :-) */
+	  gcc_unreachable ();
+	}
+      break;
+    case 'H': /* high half */
+      switch (GET_CODE (op))
+	{
+	case MEM:
+	  op = adjust_address (op, Pmode, 2);
+	  break;
+	case REG:
+	  op = gen_rtx_REG (Pmode, REGNO (op) + 1);
+	  break;
+	case CONST_INT:
+	  op = GEN_INT (INTVAL (op) >> 16);
+	  letter = 0;
+	  break;
+	default:
+	  /* If you get here, figure out a test case :-) */
+	  gcc_unreachable ();
+	}
+      break;
+
+    case 'X':
+      /* This is used to turn, for example, an ADD opcode into an ADDX
+	 opcode when we're using 20-bit addresses.  */
+      if (TARGET_LARGE)
+	fprintf (file, "X");
+      /* We don't care which operand we use, but we want 'X' in the MD
+	 file, so we do it this way.  */
+      return;
+
+    case 'x':
+ /* Similarly, but only for PSImodes. BIC, for example, needs this. */
+      if (TARGET_LARGE && GET_MODE (op) == PSImode)
+	fprintf (file, "X");
+      return;
+
+    case 'A':
+      /* Likewise, for BR -> BRA.  */
+      if (TARGET_LARGE)
+	fprintf (file, "A");
+      return;
+    }
+
+  switch (GET_CODE (op))
+    {
+    case REG:
+      msp430_print_operand_raw (file, op, letter);
+      break;
+
+    case MEM:
+      addr = XEXP (op, 0);
+      switch (GET_CODE (addr))
+	{
+	case REG:
+	  fprintf (file, "@%s", reg_names [REGNO (addr)]);
+	  break;
+	case PLUS:
+	  msp430_print_operand_raw (file, XEXP (addr, 1), letter);
+	  fprintf (file, "(%s)", reg_names [REGNO (XEXP (addr, 0))]);
+	  break;
+	case CONST:
+	case CONST_INT:
+	case SYMBOL_REF:
+	case LABEL_REF:
+	  fprintf (file, "&");
+	  msp430_print_operand_raw (file, addr, letter);
+	  break;
+
+	default:
+	  print_rtl (file, addr);
+	  break;
+	}
+      break;
+
+    case CONST_INT:
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      if (letter == 0)
+	fprintf (file, "#");
+      msp430_print_operand_raw (file, op, letter);
+      break;
+
+    case EQ: fprintf (file, "EQ"); break;
+    case NE: fprintf (file, "NE"); break;
+    case GEU: fprintf (file, "HS"); break;
+    case LTU: fprintf (file, "LO"); break;
+    case GE: fprintf (file, "GE"); break;
+    case LT: fprintf (file, "L"); break;
+
+    default:
+      print_rtl (file, op);
+      break;
+    }
+
+}
+
+
+/* Frame stuff.  */
+
+rtx
+msp430_return_addr_rtx (int count)
+{
+  int ra_size;
+  if (count)
+    return NULL_RTX;
+
+  ra_size = TARGET_LARGE ? 4 : 2;
+  if (crtl->args.pretend_args_size)
+    ra_size += 2;
+
+ return gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, arg_pointer_rtx, GEN_INT (- ra_size)));
+}
+
+rtx
+msp430_incoming_return_addr_rtx (void)
+{
+  return gen_rtx_MEM (Pmode, stack_pointer_rtx);
+}
+
+/* Instruction generation stuff.  */
+
+/* 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)
+{
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    /* Low word of dest == source word.  */
+ return "BIT.W #0x8000, %L0 { SUBC.W %H0, %H0 { INV.W %H0, %H0"; /* 8-bytes. */
+
+  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.  */
+ return "MOV.W %1, %L0 { BIT.W #0x8000, %L0 { SUBC.W %H0, %H0 { INV.W %H0, %H0"; /* 10-bytes. */
+  +  if (REGNO (operands[0]) + 1 == REGNO (operands[1]))
+    /* High word of dest == source word.  */
+    return "MOV.W %1, %L0 { RPT #15 { RRAX.W %H0"; /* 6-bytes.  */
+
+  /* No overlap between dest and source.  */
+ return "MOV.W %1, %L0 { MOV.W %1, %H0 { RPT #15 { RRAX.W %H0"; /* 8-bytes. */
+}
+
+/* Likewise for logical right shifts.  */
+const char *
+msp430x_logical_shift_right (rtx amount)
+{
+  /* The MSP430X's logical right shift instruction - RRUM - does
+     not use an extension word, so we cannot encode a repeat count.
+     Try various alternatives to work around this.  If the count
+     is in a register we are stuck, hence the assert.  */
+  gcc_assert (CONST_INT_P (amount));
+
+  if (INTVAL (amount) <= 0
+      || INTVAL (amount) >= 16)
+    return "# nop logical shift.";
+
+  if (INTVAL (amount) > 0	    +      && INTVAL (amount) < 5)
+    return "rrum.w\t%2, %0"; /* Two bytes.  */
+
+  if (INTVAL (amount) > 4	    +      && INTVAL (amount) < 9)
+    return "rrum.w\t#4, %0 { rrum.w\t%Y2, %0 "; /* Four bytes.  */
+
+  /* First we logically shift right by one.  Now we know
+     that the top bit is zero and we can use the arithmetic
+     right shift instruction to perform the rest of the shift.  */
+  return "rrum.w\t#1, %0 { rpt\t%Z2 { rrax.w\t%0"; /* Six bytes.  */
+}
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+#include "gt-msp430.h"
Index: gcc/config/msp430/msp430-c.c
===================================================================
--- gcc/config/msp430/msp430-c.c	(revision 0)
+++ gcc/config/msp430/msp430-c.c	(revision 0)
@@ -0,0 +1,36 @@
+/* MSP430 C-specific support
+   Copyright (C) 2013 Free Software Foundation, Inc.
+   Contributed by Red Hat, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "c-family/c-common.h"
+#include "msp430-protos.h"
+
+/* Implements REGISTER_TARGET_PRAGMAS.  */
+void
+msp430_register_pragmas (void)
+{
+  c_register_addr_space ("__near", ADDR_SPACE_NEAR);
+  if (msp430x)
+    c_register_addr_space ("__far", ADDR_SPACE_FAR);
+}
Index: gcc/config/msp430/msp430.opt
===================================================================
--- gcc/config/msp430/msp430.opt	(revision 0)
+++ gcc/config/msp430/msp430.opt	(revision 0)
@@ -0,0 +1,26 @@
+msim
+Target
+Use simulator runtime
+
+masm-hex
+Target Mask(ASM_HEX)
+Force assembly output to always use hex constants
+
+mmcu=
+Target Joined RejectNegative Var(target_cpu)
+Specify the cpu to build for. If the name begins with 'msp430x' then the 430X instructions are enabled
+
+mlarge
+Target Mask(LARGE) RejectNegative
+Select large model - 20-bit addresses/pointers
+
+msmall
+Target InverseMask(LARGE) RejectNegative
+Select small model - 16-bit addresses/pointers (default)
+
+mrelax
+Target Report
+Optimize opcode sizes at link time
+
+mOs
+Target Undocumented Mask(OPT_SPACE)
Index: gcc/config/msp430/msp430-modes.def
===================================================================
--- gcc/config/msp430/msp430-modes.def	(revision 0)
+++ gcc/config/msp430/msp430-modes.def	(revision 0)
@@ -0,0 +1,3 @@
+/* 20-bit address */
+PARTIAL_INT_MODE (SI);
+
Index: gcc/config/msp430/README.txt
===================================================================
--- gcc/config/msp430/README.txt	(revision 0)
+++ gcc/config/msp430/README.txt	(revision 0)
@@ -0,0 +1,7 @@
+Random Notes
+------------
+
+The MSP430 port does not use leading underscores.  However, the
+assembler has no way of differentiating between, for example, register
+R12 and symbol R12.  So, if you do "int r12;" in your C program, you
+may get an assembler error, and will certainly have runtime problems.
Index: gcc/config/msp430/msp430.h
===================================================================
--- gcc/config/msp430/msp430.h	(revision 0)
+++ gcc/config/msp430/msp430.h	(revision 0)
@@ -0,0 +1,399 @@
+/* GCC backend definitions for the TI MSP430 Processor
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Contributed by Red Hat.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 3, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+
+/* Run-time Target Specification */
+
+/* True if the MSP430x extensions are enabled.  */
+#ifndef IN_LIBGCC2
+extern bool msp430x;
+#endif
+
+#define TARGET_CPU_CPP_BUILTINS()               \
+  do                                            \
+    {                                           \
+      builtin_define ("__MSP430__"); 		\
+      if (msp430x)				\
+	{					\
+	  builtin_define ("__MSP430X__");	\
+	  builtin_assert ("cpu=MSP430X");	\
+	  if (TARGET_LARGE)			\
+	    builtin_define ("__MSP430X_LARGE__");	\
+	}					\
+      else					\
+	builtin_assert ("cpu=MSP430"); 		\
+    }                                           \
+  while (0)
+
+#undef  STARTFILE_SPEC
+#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:crt0.o%s} crtbegin.o%s"
+
+/* -lgcc is included because crtend.o needs __mspabi_func_epilog_1.  */
+#undef  ENDFILE_SPEC
+#define ENDFILE_SPEC "crtend.o%s crtn.o%s -lgcc"
+
+#define ASM_SPEC "-mP " /* Enable polymorphic instructions.  */ \
+ "%{mmcu=msp430x:-mmcu=msp430X;mmcu=*:-mmcu=%*} " /* Pass the MCU type on to the assembler. */ \
+  "%{mrelax=-mQ} " /* Pass the relax option on to the assembler.  */ \
+ "%{mlarge:-ml} " /* Tell the assembler if we are building for the LARGE pointer model. */ \ + "%{ffunction-sections:-gdwarf-sections}" /* If function sections are being created then create DWARF line number sections as well. */
+
+/* Enable linker section garbage collection by default, unless we
+   are creating a relocatable binary (gc does not work) or debugging
+ is enabled (the GDB testsuite relies upon unused entities not being deleted). */
+#define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}}"
+
+#undef  LIB_SPEC
+#define LIB_SPEC "					\
+--start-group						\
+-lc							\
+-lgcc							\
+%{msim:-lsim}						\
+%{!msim:-lnosys}					\
+--end-group					   	\
+%{!T*: %{msim: %{mlarge:%Tmsp430xl-sim.ld}%{!mlarge:%Tmsp430-sim.ld}}%{!msim:%Tmsp430.ld}} \
+"
+
+
+/* Storage Layout */
+
+#define BITS_BIG_ENDIAN 		0
+#define BYTES_BIG_ENDIAN 		0
+#define WORDS_BIG_ENDIAN 		0
+
+
+#ifdef IN_LIBGCC2
+/* This is to get correct SI and DI modes in libgcc2.c (32 and 64 bits). */
+#define	UNITS_PER_WORD			4
+/* We have a problem with libgcc2.  It only defines two versions of
+   each function, one for "int" and one for "long long".  Ie it assumes
+   that "sizeof (int) == sizeof (long)".  For the MSP430 this is not true
+   and we need a third set of functions.  We explicitly define
+   LIBGCC2_UNITS_PER_WORD here so that it is clear that we are expecting
+   to get the SI and DI versions from the libgcc2.c sources, and we
+   provide our own set of HI functions, which is why this
+   definition is surrounded by #ifndef..#endif.  */
+#ifndef LIBGCC2_UNITS_PER_WORD
+#define LIBGCC2_UNITS_PER_WORD 		4
+#endif
+#else
+/* Actual width of a word, in units (bytes).  */
+#define	UNITS_PER_WORD 			2
+#endif
+
+#define SHORT_TYPE_SIZE			16
+#define INT_TYPE_SIZE			16
+#define LONG_TYPE_SIZE			32
+#define LONG_LONG_TYPE_SIZE		64
+
+#define FLOAT_TYPE_SIZE 		32
+#define DOUBLE_TYPE_SIZE 		64
+#define LONG_DOUBLE_TYPE_SIZE		64 /*DOUBLE_TYPE_SIZE*/
+
+#define LIBGCC2_HAS_DF_MODE		1
+#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE   64
+
+#define DEFAULT_SIGNED_CHAR		0
+
+#define STRICT_ALIGNMENT 		1
+#define FUNCTION_BOUNDARY 		16
+#define BIGGEST_ALIGNMENT 		16
+#define STACK_BOUNDARY 			16
+#define PARM_BOUNDARY 			8
+#define PCC_BITFIELD_TYPE_MATTERS	1
+
+#define STACK_GROWS_DOWNWARD		1
+#define FRAME_GROWS_DOWNWARD		1
+#define FIRST_PARM_OFFSET(FNDECL) 	0
+
+#define MAX_REGS_PER_ADDRESS 		1
+
+#define Pmode 				(TARGET_LARGE ? PSImode : HImode)
+/* Note: 32 is a lie.  Large pointers are actually 20-bits wide.  But gcc
+   thinks that any non-power-of-2 pointer size equates to BLKmode, which
+   causes all kinds of problems...  */
+#define POINTER_SIZE			(TARGET_LARGE ? 32 : 16)
+#define	POINTERS_EXTEND_UNSIGNED	1
+
+#define ADDR_SPACE_NEAR	1
+#define ADDR_SPACE_FAR	2
+
+#define REGISTER_TARGET_PRAGMAS() msp430_register_pragmas()
+
+#if 1 /* XXX */
+/* Define this macro if it is advisable to hold scalars in registers
+   in a wider mode than that declared by the program.  In such cases,
+   the value is constrained to be within the bounds of the declared
+   type, but kept valid in the wider mode.  The signedness of the
+   extension may differ from that of the type.  */
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE)	\
+  if (GET_MODE_CLASS (MODE) == MODE_INT		\
+      && GET_MODE_SIZE (MODE) < 2)      	\
+    (MODE) = HImode;
+#endif
+
+/* Layout of Source Language Data Types */
+
+#undef  SIZE_TYPE
+#define SIZE_TYPE			(TARGET_LARGE ? "long unsigned int" : "unsigned int")
+#undef  PTRDIFF_TYPE
+#define PTRDIFF_TYPE			(TARGET_LARGE ? "long int" : "int")
+#undef  WCHAR_TYPE
+#define WCHAR_TYPE			"long int"
+#undef  WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE			BITS_PER_WORD
+#define FUNCTION_MODE 			HImode
+#define CASE_VECTOR_MODE		Pmode
+#define HAS_LONG_COND_BRANCH		0
+#define HAS_LONG_UNCOND_BRANCH		0
+
+#define LOAD_EXTEND_OP(M)		ZERO_EXTEND
+/*#define WORD_REGISTER_OPERATIONS	1*/
+
+#define MOVE_MAX 			8
+#define STARTING_FRAME_OFFSET		0
+
+#define INCOMING_RETURN_ADDR_RTX \
+  msp430_incoming_return_addr_rtx ()
+
+#define RETURN_ADDR_RTX(COUNT, FA)		\
+  msp430_return_addr_rtx (COUNT)
+
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC)   1
+
+#define SLOW_BYTE_ACCESS		0
+
+
+/* Register Usage */
+
+/* gas doesn't recognize PC (R0), SP (R1), and SR (R2) as register
+   names.  */
+#define REGISTER_NAMES						\
+{								\
+  "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",		\
+    "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",	\
+  "argptr"							\
+}
+
+enum reg_class
+{
+  NO_REGS,
+  R12_REGS,
+  R13_REGS,
+  GEN_REGS,
+  ALL_REGS,
+  LIM_REG_CLASSES
+};
+
+#define REG_CLASS_NAMES \
+{			\
+  "NO_REGS",		\
+  "R12_REGS",		\
+  "R13_REGS",		\
+  "GEN_REGS",		\
+  "ALL_REGS"		\
+}
+
+#define REG_CLASS_CONTENTS \
+{			   \
+  0x00000000,		   \
+  0x00001000,		   \
+  0x00002000,		   \
+  0x0000fff2,		   \
+  0x0001ffff		   \
+}
+
+#define GENERAL_REGS			GEN_REGS
+#define BASE_REG_CLASS  		GEN_REGS
+#define INDEX_REG_CLASS			GEN_REGS
+#define N_REG_CLASSES			(int) LIM_REG_CLASSES
+
+#define PC_REGNUM 		        0
+#define STACK_POINTER_REGNUM 	        1
+#define CC_REGNUM                       2
+#define FRAME_POINTER_REGNUM 		4 /* not usually used, call preserved */
+#define ARG_POINTER_REGNUM 		16
+#define STATIC_CHAIN_REGNUM 		5 /* FIXME */
+
+#define FIRST_PSEUDO_REGISTER 		17
+
+#define REGNO_REG_CLASS(REGNO)          ((REGNO) < 17 \
+					 ? GEN_REGS : NO_REGS)
+
+#define TRAMPOLINE_SIZE			4 /* FIXME */
+#define TRAMPOLINE_ALIGNMENT		16 /* FIXME */
+
+#define ELIMINABLE_REGS					\
+{{ ARG_POINTER_REGNUM,   STACK_POINTER_REGNUM },	\
+ { ARG_POINTER_REGNUM,   FRAME_POINTER_REGNUM },	\
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }}
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)	\
+  (OFFSET) = msp430_initial_elimination_offset ((FROM), (TO))
+
+
+#define FUNCTION_ARG_REGNO_P(N)	  	((N) >= 8 && (N) < ARG_POINTER_REGNUM)
+#define DEFAULT_PCC_STRUCT_RETURN	0
+
+/* 1 == register can't be used by gcc, in general
+   0 == register can be used by gcc, in general */
+#define FIXED_REGISTERS					\
+{							\
+  1,0,1,1, 0,0,0,0,					\
+  0,0,0,0, 0,0,0,0,					\
+  1,							\
+}
+
+/* 1 == value changes across function calls
+   0 == value is the same after a call      */
+/* R4 through R10 are callee-saved */
+#define CALL_USED_REGISTERS				\
+{							\
+  1,0,1,1, 0,0,0,0,					\
+  0,0,0,1, 1,1,1,1,					\
+  1,						\
+}
+
+#define REG_ALLOC_ORDER					\
+  { 12, 13, 14, 15, 10, 9, 8, 7, 6, 5, 4, 11, 0, 1, 2, 3, 16 }
+/*  { 11, 15, 14, 13, 12, 10, 9, 8, 7, 6, 5, 4, 0, 1, 2, 3, 16 }*/
+
+#define REGNO_OK_FOR_BASE_P(regno)	1
+#define REGNO_OK_FOR_INDEX_P(regno)	1
+
+
+
+typedef struct {
+  /* These two are the current argument status.  */
+  char reg_used[4];
+#define CA_FIRST_REG 12
+  char can_split;
+  /* These two are temporaries used internally.  */
+  char start_reg;
+  char reg_count;
+  char mem_count;
+  char special_p;
+} CUMULATIVE_ARGS;
+
+#define INIT_CUMULATIVE_ARGS(CA, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
+ msp430_init_cumulative_args (&CA, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS)
+
+
+/* FIXME */
+#define NO_PROFILE_COUNTERS     1
+#define PROFILE_BEFORE_PROLOGUE 1
+
+#define FUNCTION_PROFILER(FILE, LABELNO)	\
+    fprintf (FILE, "\tcall\t__mcount\n");
+
+#define HARD_REGNO_NREGS(REGNO, MODE)            \
+  msp430_hard_regno_nregs (REGNO, MODE)
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE) 			\
+  msp430_hard_regno_mode_ok (REGNO, MODE)
+
+#define MODES_TIEABLE_P(MODE1, MODE2)				\
+  msp430_modes_tieable_p (MODE1, MODE2)
+
+/* Exception Handling */
+
+/* R12,R13,R14 - EH data
+   R15 - stack adjustment */
+
+#define EH_RETURN_DATA_REGNO(N) \
+  (((N) < 3) ? ((N) + 12) : INVALID_REGNUM)
+
+#define EH_RETURN_HANDLER_RTX \
+ gen_rtx_MEM(Pmode, gen_rtx_PLUS (Pmode, gen_rtx_REG(Pmode, SP_REGNO), gen_rtx_REG (Pmode, 15)))
+
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 15)
+
+#define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) DW_EH_PE_udata4
+
+
+/* Stack Layout and Calling Conventions */
+
+
+/* Addressing Modes */
+
+
+
+#define TEXT_SECTION_ASM_OP ".text"
+#define DATA_SECTION_ASM_OP ".data"
+#define BSS_SECTION_ASM_OP   "\t.section .bss"
+
+#define ASM_COMMENT_START	" ;"
+#define ASM_APP_ON		""
+#define ASM_APP_OFF 		""
+#define LOCAL_LABEL_PREFIX	".L"
+#undef  USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX	""
+
+#define GLOBAL_ASM_OP 		"\t.global\t"
+
+#define ASM_OUTPUT_LABELREF(FILE, SYM) msp430_output_labelref ((FILE), (SYM))
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+  fprintf (FILE, "\t.long .L%d\n", VALUE)
+
+/* This is how to output an element of a case-vector that is relative.
+   Note: The local label referenced by the "3b" below is emitted by
+   the tablejump insn.  */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+  fprintf (FILE, "\t.long .L%d - 1b\n", VALUE)
+
+
+#define ASM_OUTPUT_ALIGN(STREAM, LOG)		\
+  do						\
+    {						\
+      if ((LOG) == 0)				\
+        break;					\
+      fprintf (STREAM, "\t.balign %d\n", 1 << (LOG));	\
+    }						\
+  while (0)
+
+#define JUMP_TABLES_IN_TEXT_SECTION	1
+
+#undef	DWARF2_ADDR_SIZE
+#define	DWARF2_ADDR_SIZE			4
+
+#define INCOMING_FRAME_SP_OFFSET		(POINTER_SIZE / BITS_PER_UNIT)
+
+#undef  PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+#define DWARF2_ASM_LINE_DEBUG_INFO		1
+
+/* Prevent reload (and others) from choosing HImode stack slots
+   when spilling hard registers when they may contain PSImode values.  */
+#define HARD_REGNO_CALLER_SAVE_MODE(REGNO,NREGS,MODE) \
+ ((TARGET_LARGE && ((NREGS) <= 2)) ? PSImode : choose_hard_reg_mode ((REGNO), (NREGS), false))
+
+/* Also stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)).  */
+#define CANNOT_CHANGE_MODE_CLASS(FROM,TO,CLASS) \
+  (   ((TO) == PSImode && (FROM) == SImode)	\
+   || ((TO) == SImode  && (FROM) == PSImode)    \
+   || ((TO) == DImode  && (FROM) == PSImode)    \
+   || ((TO) == PSImode && (FROM) == DImode)     \
+      )
+
+#define ACCUMULATE_OUTGOING_ARGS 1

[wwwdocs]

? htdocs/.#index.html.1.882
Index: htdocs/backends.html
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/backends.html,v
retrieving revision 1.46
diff -p -U 5 -r1.46 backends.html
--- htdocs/backends.html	16 Jul 2012 21:19:20 -0000	1.46
+++ htdocs/backends.html	14 Aug 2013 20:49:01 -0000
@@ -90,10 +90,11 @@ mep      |       F C       p g  d t s
 microblaze         CB          bd   s
 mips     |     Q   CB   qr p   bda  s
mmix | HM Q C q p b a e mn10300 | ?? c g s
 ms1      |   S   F  B      p g bd
+msp430   |    L  FI    l   p g      s
pa | ? Q CBD qr m da e pdp11 | L IC qrcp e rs6000 | Q C qr da s390 | ? Q qr p g bda e sh | Q CB qr bda Index: htdocs/index.html
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/index.html,v
retrieving revision 1.887
diff -p -U 5 -r1.887 index.html
--- htdocs/index.html	31 May 2013 11:41:08 -0000	1.887
+++ htdocs/index.html	14 Aug 2013 20:49:01 -0000
@@ -51,10 +51,14 @@ mission statement</a>.</p>
  <h2 style="margin-top:0pt;" id="news">News</h2>
  <dl class="news">
 +<dt><span>TI MSP430 support</span>
+    <span class="date">[2013-05-09]</span></dt>
+<dd>A port for the TI MSP430 has been contributed by Red Hat Inc.</dd>
+
 <dt><span><a href="gcc-4.8/">GCC 4.8.1</a> released</span>
     <span class="date">[2013-05-31]</span></dt>
     <dd></dd>
  <dt><span><a href="gcc-4.6/">GCC 4.6.4</a> released</span>
Index: htdocs/readings.html
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/readings.html,v
retrieving revision 1.231
diff -p -U 5 -r1.231 readings.html
--- htdocs/readings.html	1 Apr 2013 10:49:54 -0000	1.231
+++ htdocs/readings.html	14 Aug 2013 20:49:01 -0000
@@ -213,10 +213,15 @@ Intel&reg;64 and IA-32 Architectures Sof
  <li>mn10300
   <br />Manufacturer: Matsushita
   <br />Alternate chip name: AM30
   <br />GDB includes a simulator.
  </li>
+
+ <li>msp430
+  <br />Manufacturer: Texas Instruments
+  <br />GDB includes a simulator.
+ </li>
    <li>ns32k
   <br />Manufacturer: National Semiconductor
<br /><a href="http://www.national.com/pf/NS/NS32FX200.html";>NS32FX200 Home Page</a>
  </li>





Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]