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:
Jozef Lawrynowicz 2020-11-13 15:35:47 +00:00
parent f62dd39823
commit 546c8f9558
5 changed files with 504 additions and 119 deletions

View File

@ -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 */

View File

@ -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)). */

View File

@ -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

View File

@ -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)"))