| ;; Machine Description for TI MSP43* processors |
| ;; Copyright (C) 2013-2015 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 |
| |
| UNS_DELAY_32 |
| UNS_DELAY_32X |
| UNS_DELAY_16 |
| UNS_DELAY_16X |
| UNS_DELAY_2 |
| UNS_DELAY_1 |
| UNS_DELAY_START |
| UNS_DELAY_END |
| ]) |
| |
| (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_topbyte" |
| [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=r") |
| (subreg:QI (match_operand:PSI 1 "msp_general_operand" "r") 2))] |
| "msp430x" |
| "PUSHM.A\t#1,%1 { POPM.W\t#1,%0 { POPM.W\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"))] |
| "" |
| "@ |
| MOVA\t%1, %0 |
| MOVA\t%1, %0 |
| MOVX.A\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))] |
| "msp430x" |
| "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 "msp430_nonsubreg_operand") |
| (match_operand:SI 2 "msp430_nonsubreg_or_imm_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); |
| |
| /* BZ 64160: Do not use this splitter when the dest partially overlaps the source. */ |
| if (reg_overlap_mentioned_p (operands[3], operands[7]) |
| || reg_overlap_mentioned_p (operands[3], operands[8])) |
| FAIL; |
| |
| 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))] |
| "msp430x" |
| "RLAM.A #4, %0 { RRAM.A #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")))] |
| "msp430x" |
| "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))] |
| "msp430x" |
| "@ |
| 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)))] |
| "msp430x" |
| "RLAM.A #4, %0 { RRAM.A #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)))] |
| "msp430x" |
| "RLAM.A #4, %0 { RRAM.A #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.... |
| ; |
| ; Note: We use a + constraint on operand 0 as otherwise GCC gets confused |
| ; about extending a single PSI mode register into a pair of SImode registers |
| ; with the same starting register. It thinks that the upper register of |
| ; the pair is unused and so it can clobber it. Try compiling 20050826-2.c |
| ; at -O2 to see this. |
| |
| (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")))] |
| "msp430x" |
| "* |
| /* 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%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 { INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0\"; |
| else |
| return \"INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0\"; |
| " |
| ) |
| |
| (define_insn "delay_cycles_start" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i")] |
| UNS_DELAY_START)] |
| "" |
| "; Begin %J0 cycle delay" |
| ) |
| |
| (define_insn "delay_cycles_end" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i")] |
| UNS_DELAY_END)] |
| "" |
| "; End %J0 cycle delay" |
| ) |
| |
| (define_insn "delay_cycles_32" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i") |
| (match_operand 1 "immediate_operand" "i") |
| ] UNS_DELAY_32)] |
| "" |
| "PUSH r13 |
| PUSH r14 |
| MOV.W %A0, r13 |
| MOV.W %B0, r14 |
| 1: SUB.W #1, r13 |
| SUBC.W #0, r14 |
| JNE 1b |
| TST.W r13 |
| JNE 1b |
| POP r14 |
| POP r13" |
| ) |
| |
| (define_insn "delay_cycles_32x" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i") |
| (match_operand 1 "immediate_operand" "i") |
| ] UNS_DELAY_32X)] |
| "" |
| "PUSHM.A #2,r13 |
| MOV.W %A0, r13 |
| MOV.W %B0, r14 |
| 1: SUB.W #1, r13 |
| SUBC.W #0, r14 |
| JNE 1b |
| TST.W r13 |
| JNE 1b |
| POPM.A #2,r13" |
| ) |
| |
| (define_insn "delay_cycles_16" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i") |
| (match_operand 1 "immediate_operand" "i") |
| ] UNS_DELAY_16)] |
| "" |
| "PUSH r13 |
| MOV.W %0, r13 |
| 1: SUB.W #1, r13 |
| JNE 1b |
| POP r13" |
| ) |
| |
| (define_insn "delay_cycles_16x" |
| [(unspec_volatile [(match_operand 0 "immediate_operand" "i") |
| (match_operand 1 "immediate_operand" "i") |
| ] UNS_DELAY_16X)] |
| "" |
| "PUSHM.A #1,r13 |
| MOV.W %0, r13 |
| 1: SUB.W #1, r13 |
| JNE 1b |
| POPM.A #1,r13" |
| ) |
| |
| (define_insn "delay_cycles_2" |
| [(unspec_volatile [(const_int 0) ] UNS_DELAY_2)] |
| "" |
| "JMP .+2" |
| ) |
| |
| (define_insn "delay_cycles_1" |
| [(unspec_volatile [(const_int 0) ] UNS_DELAY_1)] |
| "" |
| "NOP" |
| ) |
| |
| (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_type != NONE" |
| "* |
| if (msp430_use_f5_series_hwmult ()) |
| return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x04C2 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0 { POP.W sr\"; |
| else |
| return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0132 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\"; |
| " |
| ) |
| |
| (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_type != NONE" |
| "* |
| if (msp430_use_f5_series_hwmult ()) |
| return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x04C0 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0 { POP.W sr\"; |
| else |
| return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0130 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\"; |
| " |
| ) |
| |
| (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_type != NONE" |
| "* |
| if (msp430_use_f5_series_hwmult ()) |
| return \"PUSH.W sr { DINT { NOP { 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 { POP.W sr\"; |
| else |
| return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0144 { MOV.W %H1, &0x0146 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\"; |
| " |
| ) |
| |
| (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_type != NONE" |
| "* |
| if (msp430_use_f5_series_hwmult ()) |
| return \"PUSH.W sr { DINT { NOP { 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 { POP.W sr\"; |
| else |
| return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0140 { MOV.W %H1, &0x0142 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\"; |
| " |
| ) |