;; Machine Description for TI MSP43* processors
-;; Copyright (C) 2013-2019 Free Software Foundation, Inc.
+;; Copyright (C) 2013-2023 Free Software Foundation, Inc.
;; Contributed by Red Hat.
;; This file is part of GCC.
UNS_DELAY_END
])
-;; This is an approximation.
-(define_attr "length" "" (const_int 4))
+;; 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:
;;
(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
(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
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"
"* 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.
"@
MOV.B\t%1, %0
MOV%X1.B\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_insn "movqi_topbyte"
(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"
"@
MOV.B\t%1, %0
MOVX.B\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_insn "movhi"
MOV.B\t%1, %0
MOV.W\t%1, %0
MOVX.W\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_expand "movsi"
(match_operand:SI 1 "general_operand"))]
""
""
- )
+)
(define_insn_and_split "movsi_s"
[(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
(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")
(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.
MOV.W\t%1, %0
MOVA\t%1, %0
MOVA\t%1, %0
- MOVX.A\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
(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.
(subreg:HI (match_operand:PSI 1 "msp430_symbol_operand" "i") 0))]
"msp430x"
"MOVA\t%1, %0"
+ [(set_attr "extension" "a")
+ (set_attr "type" "double")]
)
;;------------------------------------------------------------
"@
ADDA\t%2, %0
ADDX.A\t%2, %0"
+ [(set_attr "extension" "a,x")
+ (set_attr "type" "triple")]
)
(define_insn "addqi3"
"@
ADD.B\t%2, %0
ADDX.B\t%2, %0"
+ [(set_attr "type" "triple")]
)
(define_insn "addhi3"
"@
ADD.W\t%2, %0
ADDX.W\t%2, %0"
+ [(set_attr "type" "triple")]
)
; This pattern is needed in order to avoid reload problems.
(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"
"@
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.
"@
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")
"@
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"
"@
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
if (msp430_split_addsi (operands))
FAIL;
"
- )
+)
;; Alternatives 2 and 3 are to handle cases generated by reload.
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.
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.
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"
"@
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<mode>_cg"
"@
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 "bic<mode>3"
"@
BIC%x0%b0\t%1, %0
BICX%b0\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_insn "and<mode>3"
AND%x0.B\t%2, %0
AND%x0%b0\t%2, %0
ANDX%b0\t%2, %0"
+ [(set_attr "type" "triple")]
)
(define_insn "ior<mode>3"
"@
BIS%x0%b0\t%2, %0
BISX%b0\t%2, %0"
+ [(set_attr "type" "triple")]
)
(define_insn "xor<mode>3"
"@
XOR%x0%b0\t%2, %0
XORX%b0\t%2, %0"
+ [(set_attr "type" "triple")]
)
;; Macro : XOR #~0, %0
"@
INV%x0%b0\t%0
INV%X0%b0\t%0"
+ [(set_attr "type" "double")]
)
(define_insn "extendqihi2"
"@
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")))]
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_dup 1)))]
)
-(define_insn "zero_extendhipsi2"
- [(set (match_operand:PSI 0 "msp430_general_dst_operand" "=r,m")
- (zero_extend:PSI (match_operand:HI 1 "msp430_general_operand" "rm,r")))]
- ""
- "@
- MOVX\t%1, %0
- MOVX.A\t%1, %0"
-)
-
(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")))]
""
- { return msp430x_extendhisi (operands); }
+ { msp430x_extendhisi (operands, 0); return ""; }
+ [(set (attr "length")
+ (symbol_ref "msp430x_extendhisi (operands, 1)"))
+ (set_attr "type" "double")]
)
(define_insn "extendhipsi2"
(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
;; patterns. Doing these manually allows for alternate optimization
;; paths.
-(define_insn "zero_extendqisi2"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=r")
- (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "rm")))]
- ""
- "MOV%X1.B\t%1,%L0 { CLR\t%H0"
-)
-
-(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"
-)
-
-(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"
-)
-
(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"
(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\";
- "
+ [(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).
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
(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")]
)
;;------------------------------------------------------------
;; 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.
-;; signed A << C
-
-(define_expand "ashlhi3"
- [(set (match_operand:HI 0 "msp430_general_dst_nonv_operand")
- (ashift:HI (match_operand:HI 1 "general_operand")
- (match_operand:HI 2 "general_operand")))]
+(define_expand "<shift_insn><mode>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 ((GET_CODE (operands[1]) == SUBREG
- && REG_P (XEXP (operands[1], 0)))
- || MEM_P (operands[1]))
- operands[1] = force_reg (HImode, operands[1]);
- 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 if (CONST_INT_P (operands[2])
- && INTVAL (operands[2]) == 1)
- emit_insn (gen_slli_1 (operands[0], operands[1]));
- else
- /* The const variants of mspabi shifts have larger code size than the
- generic version, so use the generic version if optimizing for
- size. */
- msp430_expand_helper (operands, \"__mspabi_slli\", !optimize_size);
- DONE;
+ if (msp430_expand_shift (<CODE>, <MODE>mode, operands))
+ DONE;
+ /* Otherwise, fallthrough. */
}
)
-(define_insn "slli_1"
- [(set (match_operand:HI 0 "msp430_general_dst_nonv_operand" "=rm")
- (ashift:HI (match_operand:HI 1 "general_operand" "0")
- (const_int 1)))]
- ""
- "RLA%X0.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 "msp430_general_dst_nonv_operand" "=rm")
- (ashift:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 1)))]
- ""
- "RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
-)
-
-(define_insn "slll_2"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
- (ashift:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 2)))]
- ""
- "RLA%X0.W\t%L0 { RLC%X0.W\t%H0 { RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
-)
-
-(define_expand "ashlsi3"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand")
- (ashift:SI (match_operand:SI 1 "general_operand")
- (match_operand:SI 2 "general_operand")))]
- ""
- "msp430_expand_helper (operands, \"__mspabi_slll\", !optimize_size);
- DONE;"
-)
-
-(define_expand "ashldi3"
- [(set (match_operand:DI 0 "msp430_general_dst_nonv_operand")
- (ashift:DI (match_operand:DI 1 "general_operand")
- (match_operand:DI 2 "general_operand")))]
- ""
- {
- /* No const_variant for 64-bit shifts. */
- msp430_expand_helper (operands, \"__mspabi_sllll\", false);
- DONE;
- }
+;; 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 (<CODE>, HImode, operands, false); return \"\";"
+ [(set (attr "length")
+ (symbol_ref "msp430_output_asm_shift_insns (<CODE>, HImode, operands, true)"))
+ (set_attr "type" "single")]
)
-;;----------
-
-;; signed A >> C
-
-(define_expand "ashrhi3"
- [(set (match_operand:HI 0 "msp430_general_dst_nonv_operand")
- (ashiftrt:HI (match_operand:HI 1 "general_operand")
- (match_operand:HI 2 "general_operand")))]
+;; 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")))]
""
- {
- if ((GET_CODE (operands[1]) == SUBREG
- && REG_P (XEXP (operands[1], 0)))
- || MEM_P (operands[1]))
- operands[1] = force_reg (HImode, operands[1]);
- 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 if (CONST_INT_P (operands[2])
- && INTVAL (operands[2]) == 1)
- emit_insn (gen_srai_1 (operands[0], operands[1]));
- else
- msp430_expand_helper (operands, \"__mspabi_srai\", !optimize_size);
- DONE;
- }
-)
-
-(define_insn "srai_1"
- [(set (match_operand:HI 0 "msp430_general_dst_operand" "=rm")
- (ashiftrt:HI (match_operand:HI 1 "msp430_general_operand" "0")
- (const_int 1)))]
- ""
- "RRA%X0.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"
+ "* msp430_output_asm_shift_insns (<CODE>, SImode, operands, false); return \"\";"
+ [(set (attr "length")
+ (symbol_ref "msp430_output_asm_shift_insns (<CODE>, SImode, operands, true)"))
+ (set_attr "type" "single")]
)
-(define_insn "srap_2"
- [(set (match_operand:PSI 0 "register_operand" "=r")
- (ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
- (const_int 2)))]
+(define_insn "ashl<mode>3_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"
- "RRAM.A #2,%0"
-)
-
-(define_insn "sral_1"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
- (ashiftrt:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 1)))]
- ""
- "RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_insn "sral_2"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
- (ashiftrt:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 2)))]
- ""
- "RRA%X0.W\t%H0 { RRC%X0.W\t%L0 { RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_expand "ashrsi3"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand")
- (ashiftrt:SI (match_operand:SI 1 "general_operand")
- (match_operand:SI 2 "general_operand")))]
- ""
- "msp430_expand_helper (operands, \"__mspabi_sral\", !optimize_size);
- DONE;"
-)
-
-(define_expand "ashrdi3"
- [(set (match_operand:DI 0 "msp430_general_dst_nonv_operand")
- (ashift:DI (match_operand:DI 1 "general_operand")
- (match_operand:DI 2 "general_operand")))]
- ""
- {
- /* No const_variant for 64-bit shifts. */
- msp430_expand_helper (operands, \"__mspabi_srall\", false);
- DONE;
- }
-)
-
-;;----------
-
-;; unsigned A >> C
-
-(define_expand "lshrhi3"
- [(set (match_operand:HI 0 "msp430_general_dst_nonv_operand")
- (lshiftrt:HI (match_operand:HI 1 "general_operand")
- (match_operand:HI 2 "general_operand")))]
- ""
- {
- if ((GET_CODE (operands[1]) == SUBREG
- && REG_P (XEXP (operands[1], 0)))
- || MEM_P (operands[1]))
- operands[1] = force_reg (HImode, operands[1]);
- 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 if (CONST_INT_P (operands[2])
- && INTVAL (operands[2]) == 1)
- emit_insn (gen_srli_1 (operands[0], operands[1]));
- else
- msp430_expand_helper (operands, \"__mspabi_srli\", !optimize_size);
- DONE;
- }
-)
-
-(define_insn "srli_1"
- [(set (match_operand:HI 0 "msp430_general_dst_nonv_operand" "=rm")
- (lshiftrt:HI (match_operand:HI 1 "general_operand" "0")
- (const_int 1)))]
- ""
- "CLRC { RRC%X0.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")))]
+ "@
+ 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 "ashr<mode>3_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"
- {
- 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 "msp430_general_dst_nonv_operand" "=rm")
- (lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 1)))]
- ""
- "CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0"
-)
-
-(define_insn "srll_2x"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=r")
- (lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
- (const_int 2)))]
+ "@
+ 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 "lshr<mode>3_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"
- "RRUX.W\t%H0 { RRC.W\t%L0 { RRUX.W\t%H0 { RRC.W\t%L0"
-)
-
-(define_expand "lshrsi3"
- [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand")
- (lshiftrt:SI (match_operand:SI 1 "general_operand")
- (match_operand:SI 2 "general_operand")))]
- ""
- "msp430_expand_helper (operands, \"__mspabi_srll\", !optimize_size);
- DONE;"
-)
-
-(define_expand "lshrdi3"
- [(set (match_operand:DI 0 "msp430_general_dst_nonv_operand")
- (ashift:DI (match_operand:DI 1 "general_operand")
- (match_operand:DI 2 "general_operand")))]
- ""
- {
- /* No const_variant for 64-bit shifts. */
- msp430_expand_helper (operands, \"__mspabi_srlll\", false);
- DONE;
- }
+ "@
+ 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")]
)
;;------------------------------------------------------------
[(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)]
- ""
+ [(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
[(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)]
""
".refsym\t__crt0_call_exit"
- )
+ [(set_attr "length" "0")]
+)
;;------------------------------------------------------------
;; Jumps
(match_operand 1 ""))]
""
"CALL%Q0\t%0"
+ [(set_attr "extension" "none")
+ (set_attr "type" "single")]
)
(define_expand "call_value"
(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
"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
(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)))]
)
;;------------------------------------------------------------
(define_expand "cbranch<mode>4"
[(parallel [(set (pc) (if_then_else
(match_operator 0 ""
- [(match_operand:QHI 1 "nonimmediate_operand")
+ [(match_operand:QHI 1 "msp430_general_dst_nonv_operand")
(match_operand:QHI 2 "general_operand")])
(label_ref (match_operand 3 "" ""))
(pc)))
)]
""
"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 1 "msp430_general_dst_nonv_operand" "r,rYs,rm")
(match_operand:PSI 2 "general_operand" "rLs,rYsi,rmi")])
- (label_ref (match_operand 3 "" ""))
+ (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 "nonimmediate_operand" "rYsYx,rm")
+ [(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)))
"@
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 "nonimmediate_operand" "rYsYx,rm")
+ [(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))
]
""
- "*
- /* This is nasty. If we are splitting code between low and high memory
- then we do not want the linker to increase the size of sections by
- relaxing out of range jump instructions. (Since relaxation occurs
- after section placement). So we have to generate pessimal branches
- here. But we only want to do this when really necessary.
-
- FIXME: Do we need code in the other cbranch patterns ? */
- if (msp430_do_not_relax_short_jumps () && get_attr_length (insn) > 6)
- {
- return which_alternative == 0 ?
- \"CMP.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\" :
- \"CMPX.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\";
- }
-
- return which_alternative == 0 ?
- \"CMP.W\t%2, %1 { J%0\t%l3\" :
- \"CMPX.W\t%2, %1 { J%0\t%l3\";
- "
- [(set (attr "length")
- (if_then_else
- (and (ge (minus (match_dup 3) (pc)) (const_int -510))
- (le (minus (match_dup 3) (pc)) (const_int 510)))
- (const_int 6)
- (const_int 10))
- )]
- )
+ "@
+ 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 "general_operand" "r,rYs,rm")])
+ (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 "general_operand" "rYsYx,rm")])
+ (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 "general_operand" "rYsYx,rm")])
+ (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 "*bitbranch<mode>4"
[(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 "" ""))
+ (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 "*bitbranch<mode>4"
[(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 "" ""))
+ (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 "*bitbranch<mode>4"
[(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)
+ (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 "*bitbranch<mode>4"
[(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)
+ (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
[(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 "msp430_bitpos" "i,i"))
+ (match_operand 1 "const_0_to_15_operand" "i,i"))
(const_int 0))
- (label_ref (match_operand 2 "" ""))
+ (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 "*bitbranch<mode>4_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 "msp430_bitpos" "i"))
+ (match_operand 1 "const_0_to_15_operand" "i"))
(const_int 0))
- (label_ref (match_operand 2 "" ""))
+ (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 "*bitbranch<mode>4_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 "msp430_bitpos" "i"))
+ (match_operand 1 "const_0_to_15_operand" "i"))
(const_int 0))
- (pc)
+ (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 "*bitbranch<mode>4_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 "msp430_bitpos" "i"))
+ (match_operand 1 "const_0_to_15_operand" "i"))
(const_int 0))
- (pc)
+ (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
[(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.
[(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.
[(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")
- (and:HI (neg:HI (match_operand:HI 1 "register_operand" "r"))
- (match_operand 2 "immediate_operand" "n")))]
+ [(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")))]
""
- "*
- 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\";
- "
- )
+ "@
+ 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")]
JNE 1b
POP r14
POP r13"
- )
+ [(set_attr "length" "32")]
+)
(define_insn "delay_cycles_32x"
[(unspec_volatile [(match_operand 0 "immediate_operand" "i")
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")
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")
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"
+(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_hwmult_type != MSP430_HWMULT_NONE"
+ "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"
+(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_hwmult_type != MSP430_HWMULT_NONE"
+ "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"
+(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_hwmult_type != MSP430_HWMULT_NONE"
+ "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"
+(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_hwmult_type != MSP430_HWMULT_NONE"
+ "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")]
)