;; Machine Description for TI MSP43* processors ;; Copyright (C) 2013-2021 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 ])

;; Instruction length is calculated by examining the type and number of ;; operands. ;; Whether the insn uses the 430X extension word, or is a 430X address ;; instruction also has an effect. ;; “Cheap” source operands do not contribute to the overall length of the insn ;; and are register (Rn), indirect post-increment (@Rn+) and indirect register ;; (@Rn). ;; The lengths of instructions in bytes are: ;; Single-op 430: Cheap op == 2 ;; (also CALLA) Other op == 4 ;; Double-op 430: Source is not cheap == 2 ;; (also MOVA, Dest is register == 2 ;; CMPA, ADDA, Dest is not a register == 4 ;; SUBA) (sum the source and dest cost) ;; Single-op 430X: For insn names ending in ‘X’ add 2 to single-op 430 cost. ;; Double-op 430X: Insn name ends in ‘M’ == 2 ;; Others have the same cost as double-op 430 but add 2. ;; ;; The insn type describes whether it is a single or double operand MSP430 ;; instruction (some single-operand GCC instructions are actually ;; double-operand on the target). ;; “triple” and “cmp” types use the costs of a double operand type but ;; instead assume that the src operand is in op2, and also cmp types assume the ;; dst operand is in op1. ;; This attribute also describes which operands are safe to examine ;; when calculating the length or extension. GCC will segfault trying to ;; examine a non-existant operand of an insn. (define_attr “type” “none,single,double,triple,cmp” (const_string “none”))

;; The M extension is for instructions like RRAM - they always ;; only, and the operand must be a register. (define_attr “extension” “none,x,a,m” (cond [(eq_attr “type” “none”) (const_string “none”) (match_operand 0 “msp430_high_memory_operand” "") (const_string “x”) (and (eq_attr “type” “double”) (match_operand 1 “msp430_high_memory_operand” "")) (const_string “x”) (and (ior (eq_attr “type” “triple”) (eq_attr “type” “cmp”)) (ior (match_operand 1 “msp430_high_memory_operand” "") (match_operand 2 “msp430_high_memory_operand” ""))) (const_string “x”)] (const_string “none”)))

;; Multiply the default length by this constant value. (define_attr “length_multiplier” "" (const_int 1))

;; Add an additional amount to the total length of the insn. (define_attr “extra_length” "" (const_int 0))

;; FIXME for some reason if we move the addition of 2 for extension == x to ;; ADJUST_INSN_LENGTH, codesize gets much worse. (define_attr “length” "" (cond [(eq_attr “extension” “m”) (const_int 2) (eq_attr “type” “single”) (plus (if_then_else (match_operand 0 “msp430_cheap_operand” "") (const_int 2) (const_int 4)) (if_then_else (eq_attr “extension” “x”) (const_int 2) (const_int 0))) (eq_attr “type” “double”) (plus (plus (if_then_else (match_operand 0 “register_operand” "") (const_int 2) (const_int 4)) (if_then_else (match_operand 1 “msp430_cheap_operand” "") (const_int 0) (const_int 2))) (if_then_else (eq_attr “extension” “x”) (const_int 2) (const_int 0))) (eq_attr “type” “triple”) (plus (plus (if_then_else (match_operand 0 “register_operand” "") (const_int 2) (const_int 4)) (if_then_else (match_operand 2 “msp430_cheap_operand” "") (const_int 0) (const_int 2))) (if_then_else (eq_attr “extension” “x”) (const_int 2) (const_int 0))) (eq_attr “type” “cmp”) (plus (plus (if_then_else (match_operand 1 “register_operand” "") (const_int 2) (const_int 4)) (if_then_else (match_operand 2 “msp430_cheap_operand” "") (const_int 0) (const_int 2))) (if_then_else (eq_attr “extension” “x”) (const_int 2) (const_int 0)))] (const_int 2)))

(include “predicates.md”) (include “constraints.md”)

(define_mode_iterator QHI [QI HI PSI]) (define_mode_iterator HPSI [HI PSI]) (define_mode_iterator HDI [HI PSI SI DI])

;; Mapping of all shift operators (define_code_iterator any_shift [ashift ashiftrt lshiftrt])

;; Base name for define_insn (define_code_attr shift_insn [(ashift “ashl”) (lshiftrt “lshr”) (ashiftrt “ashr”)])

;; 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” [(set_attr “type” “single”)] )

(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” [(set_attr “type” “single”) (set_attr “extension” “x”)] )

(define_insn “pushm” [(unspec_volatile [(match_operand 0 “register_operand” “r”) (match_operand 1 “immediate_operand” “n”)] UNS_PUSHM)] "" “PUSHM%b0\t%1, %0” [(set_attr “type” “single”) (set_attr “extension” “m”)] )

(define_insn “pop” [(set (match_operand:HI 0 “register_operand” “=r”) (mem:HI (post_inc:HI (reg:HI SP_REGNO))))] "" “POP\t%0” [(set_attr “type” “single”)] )

(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” [(set_attr “type” “single”) (set_attr “extension” “x”)] )

;; 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” [(set_attr “type” “single”) (set_attr “extension” “m”)] )

;; 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)"; " [(set (attr “length”) (if_then_else (match_test “TARGET_LARGE”) (const_int 8) (const_int 6)))] )

(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"; " [(set (attr “length”) (if_then_else (match_test “TARGET_LARGE”) (const_int 10) (const_int 8)))] )

; 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” [(set_attr “type” “double”)] )

(define_insn “movqi_topbyte” [(set (match_operand:QI 0 “msp430_general_dst_operand” “=r”) (subreg:QI (match_operand:PSI 1 “msp430_general_operand” “r”) 2))] “msp430x” “PUSHM.A\t#1,%1 { POPM.W\t#1,%0 { POPM.W\t#1,%0” [(set_attr “length” “6”) (set_attr “type” “double”)] )

(define_insn “movqi” [(set (match_operand:QI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (match_operand:QI 1 “msp430_general_operand” “riYsYx,rmi”))] "" “@ MOV.B\t%1, %0 MOVX.B\t%1, %0” [(set_attr “type” “double”)] )

(define_insn “movhi” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=r,rYsYx,rm”) (match_operand:HI 1 “msp430_general_operand” “N,riYsYx,rmi”))] "" “@ MOV.B\t%1, %0 MOV.W\t%1, %0 MOVX.W\t%1, %0” [(set_attr “type” “double”)] )

(define_expand “movsi” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand”) (match_operand:SI 1 “general_operand”))] "" "" )

(define_insn_and_split “movsi_s” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=rm”) (subreg:SI (match_operand:PSI 1 “msp430_symbol_operand” “i”) 0))] "" "" “reload_completed” [(set (match_operand:HI 2 “msp430_general_dst_nonv_operand”) (match_operand:HI 4 “general_operand”)) (set (match_operand:HI 3 “msp430_general_dst_nonv_operand”) (match_operand:HI 5 “general_operand”))] “msp430_split_movsi (operands);” [(set_attr “type” “double”)] )

(define_insn_and_split “movsi_x” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=rm”) (match_operand:SI 1 “general_operand” “rmi”))] "" “#” “reload_completed” [(set (match_operand:HI 2 “msp430_general_dst_nonv_operand”) (match_operand:HI 4 “general_operand”)) (set (match_operand:HI 3 “msp430_general_dst_nonv_operand”) (match_operand:HI 5 “general_operand”))] “msp430_split_movsi (operands);” [(set_attr “type” “double”)] )

;; FIXME: Some MOVX.A cases can be done with MOVA, this is only a few of them. (define_insn “movpsi” [(set (match_operand:PSI 0 “msp430_general_dst_operand” “=r,r,r,Ya,rm”) (match_operand:PSI 1 “msp430_general_operand” “N,O,riYa,r,rmi”))] "" “@ MOV.B\t%1, %0 MOV.W\t%1, %0 MOVA\t%1, %0 MOVA\t%1, %0 MOVX.A\t%1, %0” [(set_attr “extension” “none,none,a,a,x”) (set_attr “type” “double”)] )

; 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” [(set_attr “length” “6”) (set_attr “type” “double”)] )

;; Produced when converting a pointer to an integer via a union, eg gcc.dg/pr47201.c. (define_insn “*movpsihi2_lo” [(set (match_operand:HI 0 “register_operand” “=r”) (subreg:HI (match_operand:PSI 1 “msp430_symbol_operand” “i”) 0))] “msp430x” “MOVA\t%1, %0” [(set_attr “extension” “a”) (set_attr “type” “double”)] )

;;------------------------------------------------------------ ;; Math

(define_insn “addpsi3” [(set (match_operand:PSI 0 “msp430_general_dst_operand” “=r,rm”) (plus:PSI (match_operand:PSI 1 “msp430_general_operand” “%0,0”) (match_operand:PSI 2 “msp430_general_operand” “rLs,rmi”)))] "" “@ ADDA\t%2, %0 ADDX.A\t%2, %0” [(set_attr “extension” “a,x”) (set_attr “type” “triple”)] )

(define_insn “addqi3” [(set (match_operand:QI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (plus:QI (match_operand:QI 1 “msp430_general_operand” “%0,0”) (match_operand:QI 2 “msp430_general_operand” “riYsYx,rmi”)))] "" “@ ADD.B\t%2, %0 ADDX.B\t%2, %0” [(set_attr “type” “triple”)] )

(define_insn “addhi3” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (plus:HI (match_operand:HI 1 “msp430_general_operand” “%0,0”) (match_operand:HI 2 “msp430_general_operand” “riYsYx,rmi”)))] "" “@ ADD.W\t%2, %0 ADDX.W\t%2, %0” [(set_attr “type” “triple”)] )

; 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%X2.W\t%L2, %L0 { ADDC%X2.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0” [(set (attr “length”) (if_then_else (match_operand 2 “register_operand” "") (const_int 10) (if_then_else (match_operand 2 “msp430_high_memory_operand” "") (const_int 18) (const_int 14)))) (set_attr “type” “triple”)] )

(define_insn “addsi3” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=&rYsYx,rm”) (plus:SI (match_operand:SI 1 “general_operand” “%0,0”) (match_operand:SI 2 “general_operand” “rYsYxi,mi”)))] "" “@ ADD\t%L2, %L0 { ADDC\t%H2, %H0 ADDX\t%L2, %L0 { ADDCX\t%H2, %H0” [(set_attr “length_multiplier” “2”) (set_attr “type” “triple”)] )

; 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.

; match_operand 3 is likely to be the same as op2 most of the time - except ; when op2 is a post_inc and we have stripped the post_inc from match_operand 3

(define_insn “addhi3_cy” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (plus:HI (match_operand:HI 1 “msp430_general_operand” “%0,0”) (match_operand:HI 2 “msp430_nonimmediate_operand” “rYsYxi,rm”))) (set (reg:BI CARRY) (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1)) (zero_extend:SI (match_operand:HI 3 “msp430_nonimmediate_operand” “rYsYxi,rm”))) (const_int 16)))) ] "" “@ ADD\t%2, %1 ; cy ADDX\t%2, %1 ; cy” [(set_attr “type” “triple”)] )

(define_insn “addhi3_cy_i” [(set (match_operand:HI 0 “msp430_general_dst_nonv_operand” “=r,rm”) (plus:HI (match_operand:HI 1 “general_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” [(set_attr “type” “triple”)] )

; Version of addhi that adds the carry, for SImode adds. (define_insn “addchi4_cy” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (plus:HI (plus:HI (match_operand:HI 1 “msp430_general_operand” “%0,0”) (match_operand:HI 2 “msp430_general_operand” “riYsYx,rmi”)) (zero_extend:HI (reg:BI CARRY)))) ] "" “@ ADDC\t%2, %1 ADDCX\t%2, %1” [(set_attr “type” “triple”)] )

; 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. ; We use the ugly predicate “msp430_nonsubregnonpostinc_or_imm_operand” to ; enforce the position of a post_inc into op2 if present (define_split [(set (match_operand:SI 0 “msp430_nonsubreg_dst_operand”) (plus:SI (match_operand:SI 1 “msp430_nonsubregnonpostinc_or_imm_operand”) (match_operand:SI 2 “msp430_nonsubreg_or_imm_operand”))) ] "" [(parallel [(set (match_operand:HI 3 “msp430_general_dst_nonv_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 “msp430_general_dst_nonv_operand” “=&rm”) (plus:HI (plus:HI (match_dup 7) (match_dup 8)) (zero_extend:HI (reg:BI CARRY)))) ] " if (msp430_split_addsi (operands)) FAIL; " )

;; Alternatives 2 and 3 are to handle cases generated by reload. (define_insn “subpsi3” [(set (match_operand:PSI 0 “msp430_general_dst_nonv_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” [(set_attr “type” “triple”) (set_attr “extension” “a,x,x,x”) (set_attr “length_multiplier” “1,1,2,2”)] )

;; Alternatives 2 and 3 are to handle cases generated by reload. (define_insn “subqi3” [(set (match_operand:QI 0 “msp430_general_dst_nonv_operand” “=rYsYx, rm, &?r, ?&r”) (minus:QI (match_operand:QI 1 “general_operand” “0, 0, !r, !i”) (match_operand:QI 2 “general_operand” " riYsYx, rmi, rmi, r")))] "" “@ SUB.B\t%2, %0 SUBX.B\t%2, %0 MOV%X2.B\t%1, %0 { SUB%X2.B\t%2, %0 MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0” [(set_attr “length_multiplier” “1,1,2,2”) (set_attr “type” “triple”)] )

;; Alternatives 2 and 3 are to handle cases generated by reload. (define_insn “subhi3” [(set (match_operand:HI 0 “msp430_general_dst_nonv_operand” “=rYsYx, rm, &?r, ?&r”) (minus:HI (match_operand:HI 1 “general_operand” “0, 0, !r, !i”) (match_operand:HI 2 “general_operand” " riYsYx, rmi, rmi, r")))] "" “@ SUB.W\t%2, %0 SUBX.W\t%2, %0 MOV%X2.W\t%1, %0 { SUB%X2.W\t%2, %0 MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0” [(set_attr “length_multiplier” “1,1,2,2”) (set_attr “type” “triple”)] )

(define_insn “subsi3” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=&rYsYx,m”) (minus:SI (match_operand:SI 1 “general_operand” “0,0”) (match_operand:SI 2 “general_operand” “riYsYx,mi”)))] "" “@ SUB\t%L2, %L0 { SUBC\t%H2, %H0 SUBX\t%L2, %L0 { SUBCX\t%H2, %H0” [(set_attr “length_multiplier” “2”) (set_attr “type” “triple”)] )

(define_insn “*bic_cg” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=rYs,m”) (and:QHI (match_operand:QHI 1 “msp430_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” [(set_attr “length” “2”) ; Smaller length achieved by using constant generator (set_attr “type” “double”)] )

(define_insn “bic3” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (and:QHI (not:QHI (match_operand:QHI 1 “msp430_general_operand” “rYsYx,rmn”)) (match_operand:QHI 2 “msp430_general_operand” “0,0”)))] "" “@ BIC%x0%b0\t%1, %0 BICX%b0\t%1, %0” [(set_attr “type” “double”)] )

(define_insn “and3” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=r,rYsYx,rm”) (and:QHI (match_operand:QHI 1 “msp430_general_operand” “%0,0,0”) (match_operand:QHI 2 “msp430_general_operand” “N,riYsYx,rmi”)))] "" “@ AND%x0.B\t%2, %0 AND%x0%b0\t%2, %0 ANDX%b0\t%2, %0” [(set_attr “type” “triple”)] )

(define_insn “ior3” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (ior:QHI (match_operand:QHI 1 “msp430_general_operand” “%0,0”) (match_operand:QHI 2 “msp430_general_operand” “riYsYx,rmi”)))] "" “@ BIS%x0%b0\t%2, %0 BISX%b0\t%2, %0” [(set_attr “type” “triple”)] )

(define_insn “xor3” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=rYsYx,rm”) (xor:QHI (match_operand:QHI 1 “msp430_general_operand” “%0,0”) (match_operand:QHI 2 “msp430_general_operand” “riYsYx,rmi”)))] "" “@ XOR%x0%b0\t%2, %0 XORX%b0\t%2, %0” [(set_attr “type” “triple”)] )

;; Macro : XOR #~0, %0 (define_insn “one_cmpl2” [(set (match_operand:QHI 0 “msp430_general_dst_operand” “=rYs,m”) (not:QHI (match_operand:QHI 1 “msp430_general_operand” “0,0”)))] "" “@ INV%x0%b0\t%0 INV%X0%b0\t%0” [(set_attr “type” “double”)] )

(define_insn “extendqihi2” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rYs,m”) (sign_extend:HI (match_operand:QI 1 “msp430_general_operand” “0,0”)))] "" “@ SXT%X0\t%0 SXT%X0\t%0” [(set_attr “type” “single”)] )

(define_insn “extendqipsi2” [(set (match_operand:PSI 0 “msp430_general_dst_operand” “=r,m”) (sign_extend:PSI (match_operand:QI 1 “msp430_general_operand” “0,0”)))] "" “@ SXT\t%0 SXTX.A\t%0” [(set_attr “type” “single”) (set_attr “extension” “none,x”)] )

;; ------------------------ ;; ZERO EXTEND INSTRUCTIONS ;; Byte-writes to registers clear bits 19:8 ;; * Byte-writes to memory do not affect bits 15:8 ;; Word-writes to registers clear bits 19:16 ;; PSImode writes to memory clear bits 15:4 of the second memory word ;; We define all possible insns since that results in better code than if ;; they are inferred. ;; ------------------------

(define_insn “zero_extendqihi2” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rYs,r,r,m”) (zero_extend:HI (match_operand:QI 1 “msp430_general_operand” “0,rYs,m,0”)))] "" “@ AND\t#0xff, %0 MOV.B\t%1, %0 MOV%X1.B\t%1, %0 AND%X0\t#0xff, %0” [(set_attr “type” “double”)] )

(define_insn “zero_extendqipsi2” [(set (match_operand:PSI 0 “register_operand” “=r,r”) (zero_extend:PSI (match_operand:QI 1 “general_operand” “rYs,m”)))] “msp430x” “@ MOV.B\t%1, %0 MOV%X1.B\t%1, %0” [(set_attr “type” “double”)] )

(define_insn “zero_extendqisi2” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=r,r”) (zero_extend:SI (match_operand:QI 1 “nonimmediate_operand” “0,rm”)))] "" “@ CLR\t%H0 MOV%X1.B\t%1,%L0 { CLR\t%H0” [(set_attr “extra_length” “2”) (set_attr “length_multiplier” “1,2”) (set_attr “type” “double”)] )

(define_insn “zero_extendhipsi2” [(set (match_operand:PSI 0 “msp430_general_dst_operand” “=r,r,m”) (zero_extend:PSI (match_operand:HI 1 “msp430_general_operand” “rYs,m,r”)))] “msp430x” “@ MOV.W\t%1, %0 MOV%X1\t%1, %0 MOVX.A\t%1, %0” [(set_attr “type” “double”)] )

(define_insn “zero_extendhisi2” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=rm,r”) (zero_extend:SI (match_operand:HI 1 “general_operand” “0,r”)))] "" “@ MOV%X0.W\t#0,%H0 MOV.W\t%1,%L0 { MOV.W\t#0,%H0” [(set_attr “length_multiplier” “1,2”) (set_attr “type” “double”)] )

(define_insn “zero_extendhisipsi2” [(set (match_operand:PSI 0 “msp430_general_dst_nonv_operand” “=r,r”) (subreg:PSI (zero_extend:SI (match_operand:HI 1 “general_operand” “0,r”)) 0))] “msp430x” “@ AND.W\t#-1,%0 MOV.W\t%1,%0” [(set_attr “length” “4,2”) (set_attr “type” “double”)] )

; 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.

; FIXME we can use MOVA for r->m if m is &abs20 or z16(rdst) (define_insn “zero_extendpsisi2” [(set (match_operand:SI 0 “register_operand” “+r,m”) (zero_extend:SI (match_operand:PSI 1 “register_operand” “r,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"; MOVX.A %1, %0" [(set (attr “length”) (cond [(match_test “REGNO (operands[1]) == SP_REGNO”) (const_int 18) (eq_attr “alternative” “1”) (const_int 6)] (const_int 10))) (set_attr “type” “double”)] )

;; Below are unnamed insn patterns to catch pointer manipulation insns ;; generated by combine. ;; We get large code size bloat when a PSImode pointer is stored in ;; memory, so we try to avoid that where possible and keep point manipulation ;; between registers. ; FIXME many of these should be unnnecessary once combine deals with ; (sign_extend (zero_extend)) or (sign_extend (subreg)) BZ 91865.

;; This is just another way of writing movqipsi/zero_extendqipsi (define_insn "" [(set (match_operand:PSI 0 “register_operand” “=r”) (sign_extend:PSI (subreg:HI (match_operand:QI 1 “general_operand” “rm”) 0)))] “msp430x” “MOV%X1.B\t%1, %0” [(set_attr “type” “double”)] )

(define_insn "" [(set (match_operand:PSI 0 “register_operand” “=r,r”) (sign_extend:PSI (zero_extend:HI (match_operand:QI 1 “general_operand” “rYs,m”))))] “msp430x” “@ MOV.B\t%1, %0 MOV%X1.B\t%1, %0” [(set_attr “type” “double”)] )

;; The next three insns emit identical assembly code. ;; They take a QImode and shift it in SImode. Only shift counts <= 8 ;; are handled since that is the simple case where the high 16-bits (i.e. the ;; high register) are always 0. (define_insn "" [(set (match_operand:SI 0 “register_operand” “=r,r,r”) (ashift:SI (zero_extend:SI (match_operand:QI 1 “general_operand” “0,rm,rm”)) (match_operand:HI 2 “const_1_to_8_operand” “M,M,i”)))] “msp430x” “@ RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0” [(set_attr “length” “4,,”) (set_attr “extra_length” “0,4,6”) (set_attr “type” “double”)] )

(define_insn "" [(set (match_operand:SI 0 “register_operand” “=r,r,r”) (ashift:SI (zero_extend:SI (subreg:HI (match_operand:QI 1 “general_operand” “0,rm,rm”) 0)) (match_operand:HI 2 “const_1_to_8_operand” “M,M,i”)))] “msp430x” “@ RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0” [(set_attr “length” “4,,”) (set_attr “extra_length” “0,4,6”) (set_attr “type” “double”)] )

;; Same as above but with a NOP sign_extend round the subreg (define_insn "" [(set (match_operand:SI 0 “register_operand” “=r,r,r”) (ashift:SI (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 “general_operand” “0,rm,rm”) 0))) (match_operand:HI 2 “const_1_to_8_operand” “M,M,i”)))] “msp430x” “@ RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0 MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0” [(set_attr “length” “4,,”) (set_attr “extra_length” “0,4,6”) (set_attr “type” “double”)] )

(define_insn "" [(set (match_operand:SI 0 “register_operand” “=r”) (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 “general_operand” “rm”) 0))))] “msp430x” “MOV%X1.B %1, %L0 { CLR %H0” [(set_attr “extra_length” “4”) (set_attr “type” “double”)] )

(define_insn "" [(set (match_operand:PSI 0 “register_operand” “=r,r,r”) (ashift:PSI (sign_extend:PSI (subreg:HI (match_operand:QI 1 “general_operand” “0,rm,rm”) 0)) (match_operand:HI 2 “const_1_to_19_operand” “M,M,i”)))] “msp430x” “@ RLAM.W %2, %0 MOV%X1.B %1, %0 { RLAM.W %2, %0 MOV%X1.B %1, %0 { RPT %2 { RLAX.A %0” [(set_attr “length” “2,,”) (set_attr “extra_length” “0,2,4”) (set_attr “type” “double”)] ) ;; END msp430 pointer manipulation combine insn patterns

;; 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 “truncpsihi2” [(set (match_operand:HI 0 “msp430_general_dst_operand” “=rm”) (truncate:HI (match_operand:PSI 1 “register_operand” “r”)))] "" “MOVX\t%1, %0” [(set_attr “extension” “m”) (set_attr “type” “double”)] )

(define_insn “extendhisi2” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=r”) (sign_extend:SI (match_operand:HI 1 “nonimmediate_operand” “r”)))] "" { msp430x_extendhisi (operands, 0); return ""; } [(set (attr “length”) (symbol_ref “msp430x_extendhisi (operands, 1)”)) (set_attr “type” “double”)] )

(define_insn “extendhipsi2” [(set (match_operand:PSI 0 “msp430_general_dst_nonv_operand” “=r”) (subreg:PSI (sign_extend:SI (match_operand:HI 1 “general_operand” “0”)) 0))] “msp430x” “RLAM.A #4, %0 { RRAM.A #4, %0” [(set_attr “length_multiplier” “2”) (set_attr “extension” “m”) (set_attr “type” “double”)] )

;; 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 “extend_and_shift1_hipsi2” [(set (subreg:SI (match_operand:PSI 0 “msp430_general_dst_nonv_operand” “=r”) 0) (ashift:SI (sign_extend:SI (match_operand:HI 1 “general_operand” “0”)) (const_int 1)))] “msp430x” “RLAM.A #4, %0 { RRAM.A #3, %0” [(set_attr “length_multiplier” “2”) (set_attr “extension” “m”) (set_attr “type” “double”)] )

(define_insn “extend_and_shift2_hipsi2” [(set (subreg:SI (match_operand:PSI 0 “msp430_general_dst_nonv_operand” “=r”) 0) (ashift:SI (sign_extend:SI (match_operand:HI 1 “general_operand” “0”)) (const_int 2)))] “msp430x” “RLAM.A #4, %0 { RRAM.A #2, %0” [(set_attr “length_multiplier” “2”) (set_attr “extension” “m”) (set_attr “type” “double”)] )

;; 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"; " [(set_attr “length” “10”) (set_attr “type” “double”)] )

; 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” [(set_attr “length” “6”) (set_attr “type” “single”)] )

;;------------------------------------------------------------ ;; 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 ;; 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” behavior. [6.5.7 (3)] ;; ;; We avoid emitting insns in msp430_expand_shift, since we would have to handle ;; many extra cases such as op0 != op1, or, op0 or op1 in memory. Instead we ;; let reload coerce op0 and op1 into the same register.

(define_expand “<shift_insn>3” [(set (match_operand:HDI 0 “msp430_general_dst_nonv_operand”) (any_shift:HDI (match_operand:HDI 1 “general_operand”) (match_operand:HDI 2 “general_operand”)))] "" { if (msp430_expand_shift (, mode, operands)) DONE; /* Otherwise, fallthrough. */ } )

;; All 430 HImode constant shifts (define_insn “<shift_insn>hi3_430” [(set (match_operand:HI 0 “msp430_general_dst_nonv_operand” “=rm”) (any_shift:HI (match_operand:HI 1 “general_operand” “0”) (match_operand:HI 2 “const_int_operand” “n”)))] “!msp430x” “* msp430_output_asm_shift_insns (, HImode, operands, false); return "";” [(set (attr “length”) (symbol_ref “msp430_output_asm_shift_insns (, HImode, operands, true)”)) (set_attr “type” “single”)] )

;; All 430 and 430X SImode constant shifts (define_insn “<shift_insn>si3_const” [(set (match_operand:SI 0 “msp430_general_dst_nonv_operand” “=rm”) (any_shift:SI (match_operand:SI 1 “general_operand” “0”) (match_operand:SI 2 “const_int_operand” “n”)))] "" “* msp430_output_asm_shift_insns (, SImode, operands, false); return "";” [(set (attr “length”) (symbol_ref “msp430_output_asm_shift_insns (, SImode, operands, true)”)) (set_attr “type” “single”)] )

(define_insn “ashl3_430x” [(set (match_operand:HPSI 0 “msp430_general_dst_nonv_operand” “=r,r,r,r”) (ashift:HPSI (match_operand:HPSI 1 “general_operand” “0 ,0,0,0”) (match_operand:HPSI 2 “const_int_operand” “M ,P,K,i”)))] “msp430x” "@ RLAM%b0\t%2, %0 RPT\t%2 { RLAX%b0\t%0 RPT\t#16 { RLAX%b0\t%0 { RPT\t%W2 { RLAX%b0\t%0

undefined behavior left shift of %1 by %2"

[(set_attr “length” “2,4,8,0”) (set_attr “type” “single”)] )

(define_insn “ashr3_430x” [(set (match_operand:HPSI 0 “msp430_general_dst_nonv_operand” “=r,r,r,r”) (ashiftrt:HPSI (match_operand:HPSI 1 “general_operand” “0,0,0,0”) (match_operand:HPSI 2 “const_int_operand” “M,P,K,i”)))] “msp430x” "@ RRAM%b0\t%2, %0 RPT\t%2 { RRAX%b0\t%0 RPT\t#16 { RRAX%b0\t%0 { RPT\t%W2 { RRAX%b0\t%0

undefined behavior arithmetic right shift of %1 by %2"

[(set_attr “length” “2,4,8,0”) (set_attr “type” “single”)] )

(define_insn “lshr3_430x” [(set (match_operand:HPSI 0 “msp430_general_dst_nonv_operand” “=r,r,r,r”) (lshiftrt:HPSI (match_operand:HPSI 1 “general_operand” “0,0,0,0”) (match_operand:HPSI 2 “const_int_operand” “M,P,K,i”)))] “msp430x” "@ RRUM%b0\t%2, %0 RPT\t%2 { RRUX%b0\t%0 RPT\t#16 { RRUX%b0\t%0 { RPT\t%W2 { RRUX%b0\t%0

undefined behavior logical right shift of %1 by %2"

[(set_attr “length” “2,4,8,0”) (set_attr “type” “single”)] )

;;------------------------------------------------------------ ;; 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” [(set (pc) (unspec_volatile [(match_operand 0 “immediate_operand” “i”)] UNS_EPILOGUE_HELPER)) (return)] “!msp430x” “BR%Q0\t#_mspabi_func_epilog%J0” [(set_attr “length” “2”)] )

(define_insn “prologue_start_marker” [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_START_MARKER)] "" “; start of prologue” [(set_attr “length” “0”)] )

(define_insn “prologue_end_marker” [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_END_MARKER)] "" “; end of prologue” [(set_attr “length” “0”)] )

(define_insn “epilogue_start_marker” [(unspec_volatile [(const_int 0)] UNS_EPILOGUE_START_MARKER)] "" “; start of epilogue” [(set_attr “length” “0”)] )

;; 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” [(set_attr “length” “0”)] )

;;------------------------------------------------------------ ;; 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” [(set_attr “extension” “none”) (set_attr “type” “single”)] )

(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” [(set_attr “extension” “none”) (set_attr “type” “single”)] )

(define_insn “msp430_return” [(return)] "" { return msp430_is_interrupt_func () ? “RETI” : (TARGET_LARGE ? “RETA” : “RET”); } [(set_attr “length” “2”)] )

;; 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;” [(set_attr “length” “40”)] )

(define_insn “jump” [(set (pc) (label_ref (match_operand 0 "" "")))] "" “BR%Q0\t#%l0” [(set_attr “length” “4”)] )

;; 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” [(set (attr “length”) (if_then_else (match_operand 0 “register_operand” "") (const_int 2) (const_int 4)))] )

;;------------------------------------------------------------ ;; Various Conditionals

(define_expand “cbranch4” [(parallel [(set (pc) (if_then_else (match_operator 0 "" [(match_operand:QHI 1 “msp430_general_dst_nonv_operand”) (match_operand:QHI 2 “general_operand”)]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:BI CARRY))] )] "" “msp430_fixup_compare_operands (mode, operands);” )

(define_insn “cbranchpsi4_real” [(set (pc) (if_then_else (match_operator 0 “msp430_cmp_operator” [(match_operand:PSI 1 “msp430_general_dst_nonv_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” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(define_insn “cbranchqi4_real” [(set (pc) (if_then_else (match_operator 0 “msp430_cmp_operator” [(match_operand:QI 1 “msp430_general_dst_nonv_operand” “rYsYx,rm”) (match_operand:QI 2 “general_operand” “rYsYxi,rmi”)]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ CMP.B\t%2, %1 { J%0\t%l3 CMPX.B\t%2, %1 { J%0\t%l3” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(define_insn “cbranchhi4_real” [(set (pc) (if_then_else (match_operator 0 “msp430_cmp_operator” [(match_operand:HI 1 “msp430_general_dst_nonv_operand” “rYsYx,rm”) (match_operand:HI 2 “general_operand” “rYsYxi,rmi”)]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ CMP.W\t%2, %1 { J%0\t%l3 CMPX.W\t%2, %1 { J%0\t%l3” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(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 “msp430_general_dst_nonv_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” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(define_insn “cbranchqi4_reversed” [(set (pc) (if_then_else (match_operator 0 “msp430_reversible_cmp_operator” [(match_operand:QI 1 “general_operand” “rYsYxi,rmi”) (match_operand:QI 2 “msp430_general_dst_nonv_operand” “rYsYx,rm”)]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ CMP.B\t%1, %2 { J%R0\t%l3 CMPX.B\t%1, %2 { J%R0\t%l3” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(define_insn “cbranchhi4_reversed” [(set (pc) (if_then_else (match_operator 0 “msp430_reversible_cmp_operator” [(match_operand:HI 1 “general_operand” “rYsYxi,rmi”) (match_operand:HI 2 “msp430_general_dst_nonv_operand” “rYsYx,rm”)]) (label_ref (match_operand 3 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ CMP.W\t%1, %2 { J%R0\t%l3 CMPX.W\t%1, %2 { J%R0\t%l3” [(set_attr “extra_length” “2”) (set_attr “type” “cmp”)] )

(define_insn “*bitbranch4” [(set (pc) (if_then_else (ne (and:QHI (match_operand:QHI 0 “msp430_general_dst_operand” “rYsYx,rm”) (match_operand:QHI 1 “msp430_general_operand” “rYsYxi,rmi”)) (const_int 0)) (label_ref (match_operand 2 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ BIT%x0%b0\t%1, %0 { JNE\t%l2 BITX%b0\t%1, %0 { JNE\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4” [(set (pc) (if_then_else (eq (and:QHI (match_operand:QHI 0 “msp430_general_dst_operand” “rYsYx,rm”) (match_operand:QHI 1 “msp430_general_operand” “rYsYxi,rmi”)) (const_int 0)) (label_ref (match_operand 2 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “@ BIT%x0%b0\t%1, %0 { JEQ\t%l2 BITX%b0\t%1, %0 { JEQ\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4” [(set (pc) (if_then_else (eq (and:QHI (match_operand:QHI 0 “msp430_general_dst_operand” “rYsYx,rm”) (match_operand:QHI 1 “msp430_general_operand” “rYsYxi,rmi”)) (const_int 0)) (pc) (label_ref (match_operand 2 "" "")))) (clobber (reg:BI CARRY)) ] "" “@ BIT%x0%b0\t%1, %0 { JNE\t%l2 BITX%b0\t%1, %0 { JNE\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4” [(set (pc) (if_then_else (ne (and:QHI (match_operand:QHI 0 “msp430_general_dst_operand” “rYsYx,rm”) (match_operand:QHI 1 “msp430_general_operand” “rYsYxi,rmi”)) (const_int 0)) (pc) (label_ref (match_operand 2 "" "")))) (clobber (reg:BI CARRY)) ] "" “@ BIT%x0%b0\t%1, %0 { JEQ\t%l2 BITX%b0\t%1, %0 { JEQ\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

;;------------------------------------------------------------ ;; zero-extract versions of the above

(define_insn “*bitbranch4_z” [(set (pc) (if_then_else (ne (zero_extract:HI (match_operand:QHI 0 “msp430_general_dst_operand” “rYs,rm”) (const_int 1) (match_operand 1 “const_0_to_15_operand” “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” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4_z” [(set (pc) (if_then_else (eq (zero_extract:HI (match_operand:QHI 0 “msp430_general_dst_operand” “rm”) (const_int 1) (match_operand 1 “const_0_to_15_operand” “i”)) (const_int 0)) (label_ref (match_operand 2 "" "")) (pc))) (clobber (reg:BI CARRY)) ] "" “BIT%X0%b0\t%p1, %0 { JEQ\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4_z” [(set (pc) (if_then_else (eq (zero_extract:HI (match_operand:QHI 0 “msp430_general_dst_operand” “rm”) (const_int 1) (match_operand 1 “const_0_to_15_operand” “i”)) (const_int 0)) (pc) (label_ref (match_operand 2 "" "")))) (clobber (reg:BI CARRY)) ] "" “BIT%X0%b0\t%p1, %0 { JNE\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

(define_insn “*bitbranch4_z” [(set (pc) (if_then_else (ne (zero_extract:HI (match_operand:QHI 0 “msp430_general_dst_operand” “rm”) (const_int 1) (match_operand 1 “const_0_to_15_operand” “i”)) (const_int 0)) (pc) (label_ref (match_operand 2 "" "")))) (clobber (reg:BI CARRY)) ] "" “BIT%X0%b0\t%p1, %0 { JEQ\t%l2” [(set_attr “extra_length” “2”) (set_attr “type” “double”)] )

;;------------------------------------------------------------ ;; Misc

(define_insn “nop” [(const_int 0)] “1” “NOP” [(set_attr “length” “2”)] )

(define_insn “disable_interrupts” [(unspec_volatile [(const_int 0)] UNS_DINT)] "" “DINT ; NOP” [(set_attr “length” “2”)] )

(define_insn “enable_interrupts” [(unspec_volatile [(const_int 0)] UNS_EINT)] "" “EINT” [(set_attr “length” “2”)] )

(define_insn “push_intr_state” [(unspec_volatile [(const_int 0)] UNS_PUSH_INTR)] "" “PUSH\tSR” [(set_attr “length” “2”)] )

(define_insn “pop_intr_state” [(unspec_volatile [(const_int 0)] UNS_POP_INTR)] "" “POP\tSR” [(set_attr “length” “2”)] )

;; 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_attr “type” “single”) (set_attr “extra_length” “2”)] )

;; 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)” [(set_attr “type” “single”) (set_attr “extra_length” “2”)] )

;; 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,r”) (and:HI (neg:HI (match_operand:HI 1 “general_operand” “0,rm”)) (match_operand 2 “immediate_operand” “n,n”)))] "" “@ INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0 MOV%X1.W\t%1, %0 { INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0” [(set_attr “length” “12,14”) (set_attr “type” “double”)] )

(define_insn “delay_cycles_start” [(unspec_volatile [(match_operand 0 “immediate_operand” “i”)] UNS_DELAY_START)] "" “; Begin %J0 cycle delay” [(set_attr “length” “0”)] )

(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” [(set_attr “length” “32”)] )

(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,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 POPM.A #2,r14” [(set_attr “length” “28”)] )

(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” [(set_attr “length” “14”)] )

(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” [(set_attr “length” “14”)] )

(define_insn “delay_cycles_2” [(unspec_volatile [(const_int 0) ] UNS_DELAY_2)] "" “JMP .+2” [(set_attr “length” “2”)] )

(define_insn “delay_cycles_1” [(unspec_volatile [(const_int 0) ] UNS_DELAY_1)] "" “NOP” [(set_attr “length” “2”)] )

(define_expand “mulhi3” [(set (match_operand:HI 0 “register_operand” “=r”) (mult:HI (match_operand:HI 1 “register_operand” “%0”) (match_operand:HI 2 “register_operand” “r”)))] “msp430_has_hwmult ()” { msp430_expand_helper (operands, “__mspabi_mpyi”, false); DONE; } )

(define_expand “mulsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (mult:SI (match_operand:SI 1 “register_operand” “%0”) (match_operand:SI 2 “register_operand” “r”)))] “msp430_has_hwmult ()” { msp430_expand_helper (operands, “__mspabi_mpyl”, false); DONE; } )

; libgcc helper functions for widening multiplication aren‘t currently ; generated by gcc, so we can’t catch them later and map them to the mspabi ; functions. ; We catch the patterns here and either generate a call to the helper function, ; or emit the hardware multiply instruction sequence inline. ; ; If we don't have hardware multiply support, it will generally be slower and ; result in larger code to call the mspabi library function to perform the ; widening multiplication than just leaving GCC to widen the arguments itself.

(define_expand “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”))))] “msp430_has_hwmult ()” { /* Leave the other case for the inline insn. */ if (!(optimize > 2 && msp430_has_hwmult ())) { msp430_expand_helper (operands, “__mspabi_mpysl”, false); DONE; } } )

(define_expand “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”))))] “msp430_has_hwmult ()” { /* Leave the other case for the inline insn. */ if (!(optimize > 2 && msp430_has_hwmult ())) { msp430_expand_helper (operands, “__mspabi_mpyul”, false); DONE; } } )

(define_expand “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”))))] “msp430_has_hwmult ()” { /* Leave the other case for the inline insn. */ if (!(optimize > 2 && msp430_has_hwmult ())) { msp430_expand_helper (operands, “__mspabi_mpysll”, false); DONE; } } )

(define_expand “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”))))] “msp430_has_hwmult ()” { /* Leave the other case for the inline insn. */ if (!(optimize > 2 && msp430_has_hwmult ())) { msp430_expand_helper (operands, “__mspabi_mpyull”, false); DONE; } } )

(define_insn “*mulhisi3_inline” [(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_has_hwmult ()” "* 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"; " [(set_attr “length” “24”)] )

(define_insn “*umulhisi3_inline” [(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_has_hwmult ()” "* 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"; " [(set_attr “length” “24”)] )

(define_insn “*mulsidi3_inline” [(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_has_hwmult ()” "* 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"; " [(set_attr “length” “40”)] )

(define_insn “*umulsidi3_inline” [(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_has_hwmult ()” "* 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"; " [(set_attr “length” “40”)] )