===================================================================
@@ -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
===================================================================
@@ -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")
+ )))
+ +
===================================================================
@@ -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) $<
===================================================================
@@ -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"
===================================================================
@@ -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);
+}
===================================================================
@@ -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)
===================================================================
@@ -0,0 +1,3 @@
+/* 20-bit address */
+PARTIAL_INT_MODE (SI);
+
===================================================================
@@ -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.
===================================================================
@@ -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
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/backends.html,v
retrieving revision 1.46
@@ -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
@@ -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>
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/readings.html,v
retrieving revision 1.231
@@ -213,10 +213,15 @@ Intel®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>