aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/config/msp430/msp430.md
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.9/gcc/config/msp430/msp430.md')
-rw-r--r--gcc-4.9/gcc/config/msp430/msp430.md1370
1 files changed, 1370 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/config/msp430/msp430.md b/gcc-4.9/gcc/config/msp430/msp430.md
new file mode 100644
index 000000000..c0c97dae6
--- /dev/null
+++ b/gcc-4.9/gcc/config/msp430/msp430.md
@@ -0,0 +1,1370 @@
+;; Machine Description for TI MSP43* processors
+;; Copyright (C) 2013-2014 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
+
+ UNS_DINT
+ UNS_EINT
+ UNS_PUSH_INTR
+ UNS_POP_INTR
+ UNS_BIC_SR
+ UNS_BIS_SR
+
+ UNS_REFSYM_NEED_EXIT
+ ])
+
+(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"
+ [(unspec_volatile [(match_operand 0 "register_operand" "r")
+ (match_operand 1 "immediate_operand" "n")] UNS_PUSHM)]
+ ""
+ "PUSHM%b0\t%1, %0"
+ )
+
+(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"
+ )
+
+;; This is nasty. Operand0 is bogus. It is only there so that we can get a
+;; mode for the %b0 to work. We should use operand1 for this, but that does
+;; not have a mode.
+;;
+;; Operand1 is actually a register, but we cannot accept (REG...) because the
+;; cprop_hardreg pass can and will renumber registers even inside
+;; unspec_volatiles. So we take an integer register number parameter and
+;; fudge it to be a register name when we generate the assembler.
+;;
+;; The pushm pattern does not have this problem because of all of the
+;; frame info cruft attached to it, so cprop_hardreg leaves it alone.
+(define_insn "popm"
+ [(unspec_volatile [(match_operand 0 "register_operand" "r")
+ (match_operand 1 "immediate_operand" "i")
+ (match_operand 2 "immediate_operand" "i")] UNS_POPM)]
+ ""
+ "POPM%b0\t%2, r%J1"
+ )
+
+;; 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)]
+ ""
+ "*
+ if (TARGET_LARGE)
+ return \"SUBA\t#2, r1 { MOVX.A\t2(r1), 0(r1)\";
+ return \"SUB\t#2, r1 { MOV.W\t2(r1), 0(r1)\";
+ "
+)
+
+(define_insn "swap_and_shrink"
+ [(unspec_volatile [(const_int 0)] UNS_SWAP_AND_SHRINK)]
+ ""
+ "* return TARGET_LARGE
+ ? \"MOVX.A\t0(r1), 2(r1) { ADDA\t#2, SP\"
+ : \"MOV.W\t0(r1), 2(r1) { ADD\t#2, SP\";
+ ")
+
+; I set LOAD_EXTEND_OP and WORD_REGISTER_OPERATIONS, but gcc puts in a
+; zero_extend anyway. Catch it here.
+(define_insn "movqihi"
+ [(set (match_operand:HI 0 "register_operand" "=r,r")
+ (zero_extend:HI (match_operand:QI 1 "memory_operand" "Ys,m")))]
+ ""
+ "@
+ MOV.B\t%1, %0
+ MOV%X1.B\t%1, %0"
+)
+
+(define_insn "movqi"
+ [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYs,rm")
+ (match_operand:QI 1 "msp_general_operand" "riYs,rmi"))]
+ ""
+ "@
+ MOV.B\t%1, %0
+ MOV%X0.B\t%1, %0"
+)
+
+(define_insn "movhi"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,rm")
+ (match_operand:HI 1 "msp_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 "msp_nonimmediate_operand" "=r,Ya,rm")
+ (match_operand:PSI 1 "msp_general_operand" "riYa,r,rmi"))]
+ ""
+ "@
+ MOV%Q0\t%1, %0
+ MOV%Q0\t%1, %0
+ MOV%X0.%Q0\t%1, %0")
+
+; This pattern is identical to the truncsipsi2 pattern except
+; that it uses a SUBREG instead of a TRUNC. It is needed in
+; order to prevent reload from converting (set:SI (SUBREG:PSI (SI)))
+; into (SET:PSI (PSI)).
+;
+; Note: using POPM.A #1 is two bytes smaller than using POPX.A....
+
+(define_insn "movsipsi2"
+ [(set (match_operand:PSI 0 "register_operand" "=r")
+ (subreg:PSI (match_operand:SI 1 "register_operand" "r") 0))]
+ "TARGET_LARGE"
+ "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A #1, %0 ; Move reg-pair %L1:%H1 into pointer %0"
+)
+
+;;------------------------------------------------------------
+;; Math
+
+(define_insn "addpsi3"
+ [(set (match_operand:PSI 0 "msp_nonimmediate_operand" "=r,rm")
+ (plus:PSI (match_operand:PSI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:PSI 2 "msp_general_operand" "rLs,rmi")))]
+ ""
+ "@
+ ADDA\t%2, %0
+ ADDX.A\t%2, %0"
+)
+
+(define_insn "addqi3"
+ [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYs,rm")
+ (plus:QI (match_operand:QI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:QI 2 "msp_general_operand" "riYs,rmi")))]
+ ""
+ "@
+ ADD.B\t%2, %0
+ ADD%X0.B\t%2, %0"
+)
+
+(define_insn "addhi3"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,rm")
+ (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "msp_general_operand" "riYs,rmi")))]
+ ""
+ "@
+ ADD.W\t%2, %0
+ ADD%X0.W\t%2, %0"
+)
+
+; This pattern is needed in order to avoid reload problems.
+; It takes an SI pair of registers, adds a value to them, and
+; then converts them into a single PSI register.
+
+(define_insn "addsipsi3"
+ [(set (subreg:SI (match_operand:PSI 0 "register_operand" "=&r") 0)
+ (plus:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand 2 "general_operand" "rmi")))]
+ ""
+ "ADD.W\t%L2, %L0 { ADDC.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %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")))]
+ ""
+ "@
+ 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.
+;
+; NOTE - we are playing a dangerous game with GCC here. 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. But we have
+; not annotated all the other patterns that set the CARRY flag as doing so
+; results in an overall increase in code size[1]. Instead we just *hope*
+; that GCC will not move a carry-setting instruction in between the first
+; and second adds.
+;
+; So far our experiments have shown that GCC is likely to move MOV and CMP
+; instructions in between the two adds, but not other instructions. MOV is
+; safe, CMP is not. So we have annotated the CMP patterns and left the
+; subtract, shift and other add patterns alone. At the moment this is
+; working, but with future changes to the generic parts of GCC that might
+; change.
+;
+; [1] It is not clear exactly why the code size increases. The cause appears
+; to be that reload is more prevelent to spilling a variable onto the stack
+; but why it does this is unknown. Possibly the additional CLOBBERs necessary
+; to correctly annotate the other patterns makes reload think that there is
+; increased register pressure. Or possibly reload does not handle ADD patterns
+; that are not single_set() very well.
+
+(define_insn "addhi3_cy"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rm")
+ (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "msp_nonimmediate_operand" "r,rm")))
+ (set (reg:BI CARRY)
+ (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1))
+ (zero_extend:SI (match_dup 2)))
+ (const_int 16))))
+ ]
+ ""
+ "@
+ ADD\t%2, %1 ; cy
+ ADD%X0\t%2, %1 ; cy"
+ )
+
+(define_insn "addhi3_cy_i"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,rm")
+ (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "immediate_operand" "i,i")))
+ (set (reg:BI CARRY)
+ (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1))
+ (match_operand 3 "immediate_operand" "i,i"))
+ (const_int 16))))
+ ]
+ ""
+ "@
+ ADD\t%2, %1 ; cy
+ ADD%X0\t%2, %1 ; cy"
+ )
+
+; Version of addhi that adds the carry, for SImode adds.
+(define_insn "addchi4_cy"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rm")
+ (plus:HI (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:HI 2 "msp_general_operand" "ri,rmi"))
+ (zero_extend:HI (reg:BI CARRY))))
+ ]
+ ""
+ "@
+ ADDC\t%2, %1
+ ADDC%X0\t%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")))
+ ]
+ ""
+ [(parallel [(set (match_operand:HI 3 "nonimmediate_operand" "=&rm")
+ (plus:HI (match_dup 4)
+ (match_dup 5)))
+ (set (reg:BI CARRY)
+ (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 4))
+ (match_dup 9))
+ (const_int 16))))
+ ])
+ (set (match_operand:HI 6 "nonimmediate_operand" "=&rm")
+ (plus:HI (plus:HI (match_dup 7)
+ (match_dup 8))
+ (zero_extend:HI (reg:BI 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]);
+ }
+ "
+ )
+
+
+;; Alternatives 2 and 3 are to handle cases generated by reload.
+(define_insn "subpsi3"
+ [(set (match_operand:PSI 0 "nonimmediate_operand" "=r, rm, &?r, ?&r")
+ (minus:PSI (match_operand:PSI 1 "general_operand" "0, 0, !r, !i")
+ (match_operand:PSI 2 "general_operand" "rLs, rmi, rmi, r")))]
+ ""
+ "@
+ SUBA\t%2, %0
+ SUBX.A\t%2, %0
+ MOVX.A\t%1, %0 { SUBX.A\t%2, %0
+ MOVX.A\t%1, %0 { SUBA\t%2, %0"
+)
+
+;; Alternatives 2 and 3 are to handle cases generated by reload.
+(define_insn "subqi3"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=rYs, rm, &?r, ?&r")
+ (minus:QI (match_operand:QI 1 "general_operand" "0, 0, !r, !i")
+ (match_operand:QI 2 "general_operand" " riYs, rmi, rmi, r")))]
+ ""
+ "@
+ SUB.B\t%2, %0
+ SUB%X0.B\t%2, %0
+ MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0
+ MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0"
+)
+
+;; Alternatives 2 and 3 are to handle cases generated by reload.
+(define_insn "subhi3"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rYs, rm, &?r, ?&r")
+ (minus:HI (match_operand:HI 1 "general_operand" "0, 0, !r, !i")
+ (match_operand:HI 2 "general_operand" " riYs, rmi, rmi, r")))]
+ ""
+ "@
+ SUB.W\t%2, %0
+ SUB%X0.W\t%2, %0
+ MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0
+ MOV%X0.W\t%1, %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")))]
+ ""
+ "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_operator" "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")))]
+ ""
+ "@
+ AND%x0%b0\t%2, %0
+ AND%X0%b0\t%2, %0"
+)
+
+(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")))]
+ ""
+ "@
+ BIS%x0%b0\t%2, %0
+ BIS%X0%b0\t%2, %0"
+)
+
+(define_insn "xor<mode>3"
+ [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm")
+ (xor:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0")
+ (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))]
+ ""
+ "@
+ 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 "msp_nonimmediate_operand" "=rYs,m")
+ (not:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "0,0")))]
+ ""
+ "@
+ INV%x0%b0\t%0
+ INV%X0%b0\t%0"
+)
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,m")
+ (sign_extend:HI (match_operand:QI 1 "msp_nonimmediate_operand" "0,0")))]
+ ""
+ "@
+ SXT%X0\t%0
+ SXT%X0\t%0"
+)
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,m")
+ (zero_extend:HI (match_operand:QI 1 "msp_nonimmediate_operand" "0,0")))]
+ ""
+ "@
+ AND\t#0xff, %0
+ AND%X0\t#0xff, %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 "msp_nonimmediate_operand" "=r,m")
+ (zero_extend:PSI (match_operand:HI 1 "msp_nonimmediate_operand" "rm,r")))]
+ ""
+ "MOVX\t%1, %0"
+)
+
+(define_insn "truncpsihi2"
+ [(set (match_operand:HI 0 "msp_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")))]
+ ""
+ { return msp430x_extendhisi (operands); }
+)
+
+(define_insn "extendhipsi2"
+ [(set (match_operand:PSI 0 "nonimmediate_operand" "=r")
+ (subreg:PSI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")) 0))]
+ "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"
+)
+
+(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))]
+ "TARGET_LARGE"
+ "@
+ AND.W\t#-1,%0
+ MOV.W\t%1,%0"
+)
+
+(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)))]
+ "TARGET_LARGE"
+ "RLAM #4, %0 { RRAM #3, %0"
+)
+
+(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)))]
+ "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")))]
+ ""
+ "*
+ 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\t#1, %1 { ADDX.W\t#4, @r1 { POPX.W\t%L0 { POPX.W\t%H0 ; get stack pointer into %L0:%H0\";
+ else
+ return \"PUSHM.A\t#1, %1 { POPX.W\t%L0 { POPX.W\t%H0 ; move pointer in %1 into reg-pair %L0:%H0\";
+ "
+)
+
+;; We also need to be able to sign-extend pointer types (eg ptrdiff_t).
+;; Since (we assume) pushing a 20-bit value onto the stack zero-extends
+;; it, we use a different method here.
+
+(define_insn "extendpsisi2"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (sign_extend:SI (match_operand:PSI 1 "register_operand" "r")))]
+ "TARGET_LARGE"
+ "*
+ /* The intention here is that we copy the bottom 16-bits of
+ %1 into %L0 (zeroing the top four bits). Then we copy the
+ entire 20-bits of %1 into %H0 and then arithmetically shift
+ it right by 16 bits, to get the top four bits of the pointer
+ sign-extended in %H0. */
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"MOVX.A\t%1, %H0 { MOV.W\t%1, %L0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\";
+ else
+ return \"MOV.W\t%1, %L0 { MOVX.A\t%1, %H0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\";
+ "
+)
+
+; 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\t%H1 { PUSH.W\t%L1 { POPM.A\t#1, %L0"
+)
+
+;;------------------------------------------------------------
+;; 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")))]
+ ""
+ {
+ 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)))]
+ ""
+ "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")))]
+ "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)))]
+ ""
+ "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)))]
+ ""
+ "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")))]
+ ""
+ "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")))]
+ ""
+ {
+ 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 "msp_nonimmediate_operand" "=rm")
+ (ashiftrt:HI (match_operand:HI 1 "msp_general_operand" "0")
+ (const_int 1)))]
+ ""
+ "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")))]
+ "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)))]
+ "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)))]
+ "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)))]
+ ""
+ "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)))]
+ ""
+ "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")))]
+ ""
+ "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")))]
+ ""
+ {
+ 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)))]
+ ""
+ "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")))]
+ "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)))]
+ ""
+ "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)))]
+ ""
+ "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)))]
+ "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")))]
+ ""
+ "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%Q0\t#__mspabi_func_epilog_%J0"
+ )
+
+
+(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"
+ )
+
+;; This makes the linker add a call to exit() after the call to main()
+;; in crt0
+(define_insn "msp430_refsym_need_exit"
+ [(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)]
+ ""
+ ".refsym\t__crt0_call_exit"
+ )
+
+;;------------------------------------------------------------
+;; Jumps
+
+(define_expand "call"
+ [(call:HI (match_operand 0 "")
+ (match_operand 1 ""))]
+ ""
+ ""
+)
+
+(define_insn "call_internal"
+ [(call (mem:HI (match_operand 0 "general_operand" "rYci"))
+ (match_operand 1 ""))]
+ ""
+ "CALL%Q0\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" "rYci"))
+ (match_operand 2 "")))]
+ ""
+ "CALL%Q0\t%1"
+)
+
+(define_insn "msp_return"
+ [(return)]
+ ""
+ { return msp430_is_interrupt_func () ? "RETI" : (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%Q0\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%Q0\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:BI 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:BI CARRY))
+ ]
+ ""
+ "@
+ CMP%Q0\t%2, %1 { J%0\t%l3
+ CMPX.A\t%2, %1 { J%0\t%l3
+ CMPX.A\t%2, %1 { J%0\t%l3"
+ )
+
+(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:BI 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:BI 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:BI CARRY))
+ ]
+ ""
+ "@
+ CMP%Q0\t%1, %2 { J%R0\t%l3
+ CMPX.A\t%1, %2 { J%R0\t%l3
+ CMPX.A\t%1, %2 { J%R0\t%l3"
+ )
+
+(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:BI 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:BI 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:BI 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:BI 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:BI 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:BI CARRY))
+ ]
+ ""
+ "BIT%X0%b0\t%1, %0 { JEQ\t%l2"
+ )
+
+;;------------------------------------------------------------
+;; zero-extract 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:BI 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:BI 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:BI 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:BI CARRY))
+ ]
+ ""
+ "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
+ )
+
+;;------------------------------------------------------------
+;; Misc
+
+(define_insn "nop"
+ [(const_int 0)]
+ "1"
+ "NOP"
+)
+
+(define_insn "disable_interrupts"
+ [(unspec_volatile [(const_int 0)] UNS_DINT)]
+ ""
+ "DINT \; NOP"
+ )
+
+(define_insn "enable_interrupts"
+ [(unspec_volatile [(const_int 0)] UNS_EINT)]
+ ""
+ "EINT"
+ )
+
+(define_insn "push_intr_state"
+ [(unspec_volatile [(const_int 0)] UNS_PUSH_INTR)]
+ ""
+ "PUSH\tSR"
+ )
+
+(define_insn "pop_intr_state"
+ [(unspec_volatile [(const_int 0)] UNS_POP_INTR)]
+ ""
+ "POP\tSR"
+ )
+
+;; Clear bits in the copy of the status register that is currently
+;; saved on the stack at the top of the interrupt handler.
+(define_insn "bic_SR"
+ [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIC_SR)]
+ ""
+ "BIC.W\t%0, %O0(SP)"
+ )
+
+;; Set bits in the copy of the status register that is currently
+;; saved on the stack at the top of the interrupt handler.
+(define_insn "bis_SR"
+ [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIS_SR)]
+ ""
+ "BIS.W\t%0, %O0(SP)"
+ )
+
+;; For some reason GCC is generating (set (reg) (and (neg (reg)) (int)))
+;; very late on in the compilation and not splitting it into separate
+;; instructions, so we provide a pattern to support it here.
+(define_insn "andneghi3"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (and:HI (neg:HI (match_operand:HI 1 "register_operand" "r"))
+ (match_operand 2 "immediate_operand" "n")))]
+ ""
+ "*
+ if (REGNO (operands[0]) != REGNO (operands[1]))
+ return \"MOV.W\t%1, %0 { SUB.W\t#0, %0 { AND.W\t%2, %0\";
+ else
+ return \"SUB.W\t#0, %0 { AND.W\t%2, %0\";
+ "
+ )
+
+(define_insn "mulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
+ (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
+ "optimize > 2 && msp430_hwmult_enabled ()"
+ "*
+ if (msp430_is_f5_mcu ())
+ return \"MOV.W %1, &0x04C2 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0\";
+ else
+ return \"MOV.W %1, &0x0132 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0\";
+ "
+)
+
+(define_insn "umulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0"))
+ (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
+ "optimize > 2 && msp430_hwmult_enabled ()"
+ "*
+ if (msp430_is_f5_mcu ())
+ return \"MOV.W %1, &0x04C0 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0\";
+ else
+ return \"MOV.W %1, &0x0130 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0\";
+ "
+)
+
+(define_insn "mulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+ (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+ "optimize > 2 && msp430_hwmult_enabled ()"
+ "*
+ if (msp430_is_f5_mcu ())
+ return \"MOV.W %L1, &0x04D4 { MOV.W %H1, &0x04D6 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0\";
+ else
+ return \"MOV.W %L1, &0x0144 { MOV.W %H1, &0x0146 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0\";
+ "
+)
+
+(define_insn "umulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+ (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+ "optimize > 2 && msp430_hwmult_enabled ()"
+ "*
+ if (msp430_is_f5_mcu ())
+ return \"MOV.W %L1, &0x04D0 { MOV.W %H1, &0x04D2 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0\";
+ else
+ return \"MOV.W %L1, &0x0140 { MOV.W %H1, &0x0141 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0\";
+ "
+)