MSP430: Add defaulting to the insn length attribute
The length of MSP430 instructions is mostly just a function of the type and number of operands. Setting the "type" attribute on all insns describes the number of operands, and the position of the source and destination operands. In most cases, defaulting in the "length" and "extension" attribute definitions can then be used to calculate the total length of the instruction by using the value of the "type" attribute to examine the operands. gcc/ChangeLog: * config/msp430/msp430-protos.h (msp430x_extendhisi): Return int instead of char *. (msp430_output_asm_shift_insns): Likewise. Add new return_length argument. (msp430x_insn_required): Add prototype. * config/msp430/msp430.c (msp430_output_asm_shift_insns): Return the total length, in bytes, of the emitted instructions. (msp430x_insn_required): New function. (msp430x_extendhisi): Return the total length, in bytes, of the emitted instructions. * config/msp430/msp430.h (ADJUST_INSN_LENGTH): Define. * config/msp430/msp430.md: New define_attr "type". New define_attr "extension". New define_attr "length_multiplier". New define_attr "extra_length". Rewrite define_attr "length". Set type, extension, length, length_multiplier or extra_length insn attributes on all insns, as appropriate. (andneghi3): Rewrite using constraints instead of C code to decide output insns. * config/msp430/predicates.md (msp430_cheap_operand): New predicate. (msp430_high_memory_operand): New predicate.
This commit is contained in:
parent
f62dd39823
commit
546c8f9558
5 changed files with 504 additions and 119 deletions
|
@ -26,7 +26,7 @@ void msp430_expand_eh_return (rtx);
|
|||
void msp430_expand_epilogue (int);
|
||||
void msp430_expand_helper (rtx *operands, const char *, bool);
|
||||
void msp430_expand_prologue (void);
|
||||
const char * msp430x_extendhisi (rtx *);
|
||||
int msp430x_extendhisi (rtx *, bool);
|
||||
void msp430_fixup_compare_operands (machine_mode, rtx *);
|
||||
int msp430_hard_regno_nregs_has_padding (int, machine_mode);
|
||||
int msp430_hard_regno_nregs_with_padding (int, machine_mode);
|
||||
|
@ -49,10 +49,11 @@ rtx msp430_subreg (machine_mode, rtx, machine_mode, int);
|
|||
bool msp430_use_f5_series_hwmult (void);
|
||||
bool msp430_has_hwmult (void);
|
||||
bool msp430_op_not_in_high_mem (rtx op);
|
||||
bool msp430x_insn_required (rtx op);
|
||||
|
||||
#ifdef RTX_CODE
|
||||
int msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands);
|
||||
const char * msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode, rtx *operands);
|
||||
int msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode, rtx *operands, bool);
|
||||
#endif
|
||||
|
||||
#endif /* GCC_MSP430_PROTOS_H */
|
||||
|
|
|
@ -3520,18 +3520,22 @@ msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
|
|||
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:
|
||||
|
@ -3549,17 +3553,28 @@ msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
|
|||
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 ();
|
||||
|
@ -3567,33 +3582,61 @@ msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
|
|||
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. */
|
||||
|
@ -4115,6 +4158,20 @@ msp430_op_not_in_high_mem (rtx op)
|
|||
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
|
||||
|
||||
|
@ -4455,35 +4512,52 @@ msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
|
|||
|
||||
/* 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";
|
||||
{
|
||||
/* 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:
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
/* 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)). */
|
||||
|
|
|
@ -530,3 +530,13 @@ void msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
|
|||
|
||||
|
||||
#define SYMBOL_FLAG_LOW_MEM (SYMBOL_FLAG_MACH_DEP << 0)
|
||||
|
||||
#define ADJUST_INSN_LENGTH(insn, length) \
|
||||
do \
|
||||
{ \
|
||||
if (recog_memoized (insn) >= 0) \
|
||||
{ \
|
||||
length += get_attr_extra_length (insn); \
|
||||
length *= get_attr_length_multiplier (insn); \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -131,3 +131,16 @@
|
|||
(define_predicate "msp430_symbol_operand"
|
||||
(match_code "symbol_ref")
|
||||
)
|
||||
|
||||
; Used in length attribute tests - if a source operand is a reg,
|
||||
; (mem (post_inc)), or (mem (reg)) then it is cheap compared to other operand
|
||||
; types.
|
||||
(define_predicate "msp430_cheap_operand"
|
||||
(ior (match_code "reg")
|
||||
(and (match_code "mem")
|
||||
(ior (match_code "reg" "0")
|
||||
(match_code "post_inc" "0")))))
|
||||
|
||||
; Used for insn attributes only. For insn patterns themselves, use constraints.
|
||||
(define_predicate "msp430_high_memory_operand"
|
||||
(match_test "msp430x_insn_required (op)"))
|
||||
|
|
Loading…
Add table
Reference in a new issue