diff options
Diffstat (limited to 'gcc-4.9/gcc/config/msp430/msp430.md')
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430.md | 1370 |
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\"; + " +) |