For 430X it is inneficient to do so for any modes except SI and DI, since we
can make use of R*M insns or RPT with 430X insns, so this function is only
used for SImode in that case. */
-const char *
+int
msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
- rtx *operands)
+ rtx *operands, bool return_length)
{
int i;
int amt;
int max_shift = GET_MODE_BITSIZE (mode) - 1;
+ int length = 0;
+
gcc_assert (CONST_INT_P (operands[2]));
amt = INTVAL (operands[2]);
if (amt == 0 || amt > max_shift)
{
+ if (return_length)
+ return 0;
switch (code)
{
case ASHIFT:
default:
gcc_unreachable ();
}
- return "";
+ return 0;
}
if (code == ASHIFT)
{
if (!msp430x && mode == HImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("RLA.W\t%0", operands);
+ {
+ if (return_length)
+ length = 2 + (MEM_P (operands[0]) ? 2 : 0);
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("RLA.W\t%0", operands);
+ }
else if (mode == SImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("RLA%X0.W\t%L0 { RLC%X0.W\t%H0", operands);
+ {
+ if (return_length)
+ length = 4 + (MEM_P (operands[0]) ? 4 : 0)
+ + (4 * msp430x_insn_required (operands[0]));
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("RLA%X0.W\t%L0 { RLC%X0.W\t%H0", operands);
+ }
else
/* Catch unhandled cases. */
gcc_unreachable ();
else if (code == ASHIFTRT)
{
if (!msp430x && mode == HImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("RRA.W\t%0", operands);
+ {
+ if (return_length)
+ length = 2 + (MEM_P (operands[0]) ? 2 : 0);
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("RRA.W\t%0", operands);
+ }
else if (mode == SImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("RRA%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+ {
+ if (return_length)
+ length = 4 + (MEM_P (operands[0]) ? 4 : 0)
+ + (4 * msp430x_insn_required (operands[0]));
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("RRA%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+ }
else
gcc_unreachable ();
}
else if (code == LSHIFTRT)
{
if (!msp430x && mode == HImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("CLRC { RRC.W\t%0", operands);
+ {
+ if (return_length)
+ length = 4 + (MEM_P (operands[0]) ? 2 : 0);
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("CLRC { RRC.W\t%0", operands);
+ }
else if (mode == SImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0", operands);
+ {
+ if (return_length)
+ length = 6 + (MEM_P (operands[0]) ? 4 : 0)
+ + (4 * msp430x_insn_required (operands[0]));
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0",
+ operands);
+ }
/* FIXME: Why doesn't "RRUX.W\t%H0 { RRC%X0.W\t%L0" work for msp430x?
It causes execution timeouts e.g. pr41963.c. */
#if 0
else if (msp430x && mode == SImode)
- for (i = 0; i < amt; i++)
- output_asm_insn ("RRUX.W\t%H0 { RRC%X0.W\t%L0", operands);
+ {
+ if (return_length)
+ length = 2;
+ else
+ for (i = 0; i < amt; i++)
+ output_asm_insn ("RRUX.W\t%H0 { RRC%X0.W\t%L0", operands);
+ }
#endif
else
gcc_unreachable ();
}
- return "";
+ return length * amt;
}
/* Called by cbranch<mode>4 to coerce operands into usable forms. */
return false;
}
+/* Based on the operand OP, is a 430X insn required to handle it?
+ There are only 3 conditions for which a 430X insn is required:
+ - PSImode operand
+ - memory reference to a symbol which could be in upper memory
+ (so its address is > 0xFFFF)
+ - absolute address which has VOIDmode, i.e. (mem:HI (const_int))
+ Use a 430 insn if none of these conditions are true. */
+bool
+msp430x_insn_required (rtx op)
+{
+ return (GET_MODE (op) == PSImode
+ || !msp430_op_not_in_high_mem (op));
+}
+
#undef TARGET_PRINT_OPERAND
#define TARGET_PRINT_OPERAND msp430_print_operand
/* Generate a sequence of instructions to sign-extend an HI
value into an SI value. Handles the tricky case where
- we are overwriting the destination. */
-
-const char *
-msp430x_extendhisi (rtx * operands)
+ we are overwriting the destination.
+ Return the number of bytes used by the emitted instructions.
+ If RETURN_LENGTH is true then do not emit the assembly instruction
+ sequence. */
+int
+msp430x_extendhisi (rtx * operands, bool return_length)
{
if (REGNO (operands[0]) == REGNO (operands[1]))
- /* Low word of dest == source word. 8-byte sequence. */
- return "BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0";
-
- if (! msp430x)
- /* Note: This sequence is approximately the same length as invoking a helper
- function to perform the sign-extension, as in:
-
- MOV.W %1, %L0
- MOV.W %1, r12
- CALL __mspabi_srai_15
- MOV.W r12, %H0
-
- but this version does not involve any function calls or using argument
- registers, so it reduces register pressure. 10-byte sequence. */
- return "MOV.W\t%1, %L0 { BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 "
- "{ INV.W\t%H0, %H0";
-
- if (REGNO (operands[0]) + 1 == REGNO (operands[1]))
- /* High word of dest == source word. 6-byte sequence. */
- return "MOV.W\t%1, %L0 { RPT\t#15 { RRAX.W\t%H0";
+ {
+ /* Low word of dest == source word. */
+ if (!return_length)
+ output_asm_insn ("BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0",
+ operands);
+ return 8;
+ }
+ else if (! msp430x)
+ {
+ /* Note: This sequence is approximately the same length as invoking a
+ helper function to perform the sign-extension, as in:
+
+ MOV.W %1, %L0
+ MOV.W %1, r12
+ CALL __mspabi_srai_15
+ MOV.W r12, %H0
+
+ but this version does not involve any function calls or using argument
+ registers, so it reduces register pressure. */
+ if (!return_length)
+ output_asm_insn ("MOV.W\t%1, %L0 { BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0",
+ operands);
+ return 10;
+ }
+ else if (REGNO (operands[0]) + 1 == REGNO (operands[1]))
+ {
+ /* High word of dest == source word. */
+ if (!return_length)
+ output_asm_insn ("MOV.W\t%1, %L0 { RPT\t#15 { RRAX.W\t%H0",
+ operands);
+ return 6;
+ }
- /* No overlap between dest and source. 8-byte sequence. */
- return "MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0";
+ /* No overlap between dest and source. */
+ if (!return_length)
+ output_asm_insn ("MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0",
+ operands);
+ return 8;
}
/* Stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)). */
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")
(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"
"@
SXT\t%0
SXTX.A\t%0"
+ [(set_attr "type" "single")
+ (set_attr "extension" "none,x")]
)
;; ------------------------
MOV.B\t%1, %0
MOV%X1.B\t%1, %0
AND%X0\t#0xff, %0"
+ [(set_attr "type" "double")]
)
(define_insn "zero_extendqipsi2"
"@
MOV.B\t%1, %0
MOV%X1.B\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_insn "zero_extendqisi2"
"@
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"
MOV.W\t%1, %0
MOV%X1\t%1, %0
MOVX.A\t%1, %0"
+ [(set_attr "type" "double")]
)
(define_insn "zero_extendhisi2"
"@
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"
"@
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
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
(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 ""
"@
MOV.B\t%1, %0
MOV%X1.B\t%1, %0"
+ [(set_attr "type" "double")]
)
;; The next three insns emit identical assembly code.
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 ""
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
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 ""
(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 ""
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
(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
(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"
+ [(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")]
)
;;------------------------------------------------------------
(any_shift:HI (match_operand:HI 1 "general_operand" "0")
(match_operand:HI 2 "const_int_operand" "n")))]
"!msp430x"
- "* return msp430_output_asm_shift_insns (<CODE>, HImode, operands);"
+ "* 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")]
)
;; All 430 and 430X SImode constant shifts
(any_shift:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "const_int_operand" "n")))]
""
- "* return msp430_output_asm_shift_insns (<CODE>, SImode, operands);"
+ "* 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 "ashl<mode>3_430x"
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"
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"
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"
[(set (pc)
- (unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER))
+ (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)))]
)
;;------------------------------------------------------------
)]
""
"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 "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
"@
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
"@
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"
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
"@
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
"@
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
(const_int 1)
(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
(const_int 1)
(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
(const_int 1)
(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
(const_int 1)
(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 "general_operand" "rm"))
- (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%X1.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")]
+)
; 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
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"
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"
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"
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")]
)