MSP430: Simplify and extend shift instruction patterns

The implementation of define_expand and define_insn patterns to handle
shifts in the MSP430 backend is inconsistent, resulting in missed
opportunities to make best use of the architecture's features.

There's now a single define_expand used as the entry point for all valid
shifts, and the decision to either use a helper function to perform the
shift (often required for the 430 ISA), or fall through to the
define_insn patterns can be made from that expander function.

Shifts by a constant amount have been grouped into one define_insn for
each type of shift, instead of having different define_insn patterns for
shifts by different amounts.

A new target option "-mmax-inline-shift=" has been added to allow tuning
of the number of shift instructions to emit inline, instead of using
a library helper function.

gcc/ChangeLog:

	* config/msp430/constraints.md (K): Change unused constraint to
	constraint to a const_int between 1 and 19.
	(P): New constraint.
	* config/msp430/msp430-protos.h (msp430x_logical_shift_right): Remove.
	(msp430_expand_shift): New.
	(msp430_output_asm_shift_insns): New.
	* config/msp430/msp430.c (msp430_rtx_costs): Remove shift costs.
	(CSH): Remove.
	(msp430_expand_helper): Remove hard-coded generation of some inline
	shift insns.
	(use_helper_for_const_shift): New.
	(msp430_expand_shift): New.
	(msp430_output_asm_shift_insns): New.
	(msp430_print_operand): Add new 'W' operand selector.
	(msp430x_logical_shift_right): Remove.
	* config/msp430/msp430.md (HPSI): New define_mode_iterator.
	(HDI): Likewise.
	(any_shift): New define_code_iterator.
	(shift_insn): New define_code_attr.
	Adjust unnamed insn patterns searched for by combine.
	(ashlhi3): Remove.
	(slli_1): Remove.
	(430x_shift_left): Remove.
	(slll_1): Remove.
	(slll_2): Remove.
	(ashlsi3): Remove.
	(ashldi3): Remove.
	(ashrhi3): Remove.
	(srai_1): Remove.
	(430x_arithmetic_shift_right): Remove.
	(srap_1): Remove.
	(srap_2): Remove.
	(sral_1): Remove.
	(sral_2): Remove.
	(ashrsi3): Remove.
	(ashrdi3): Remove.
	(lshrhi3): Remove.
	(srli_1): Remove.
	(430x_logical_shift_right): Remove.
	(srlp_1): Remove.
	(srll_1): Remove.
	(srll_2x): Remove.
	(lshrsi3): Remove.
	(lshrdi3): Remove.
	(<shift_insn><mode>3): New define_expand.
	(<shift_insn>hi3_430): New define_insn.
	(<shift_insn>si3_const): Likewise.
	(ashl<mode>3_430x): Likewise.
	(ashr<mode>3_430x): Likewise.
	(lshr<mode>3_430x): Likewise.
	(*bitbranch<mode>4_z): Replace renamed predicate msp430_bitpos with
	const_0_to_15_operand.
	* config/msp430/msp430.opt: New option -mmax-inline-shift=.
	* config/msp430/predicates.md (const_1_to_8_operand): New predicate.
	(const_0_to_15_operand): Rename msp430_bitpos predicate.
	(const_1_to_19_operand): New predicate.
	* doc/invoke.texi: Document -mmax-inline-shift=.

libgcc/ChangeLog:

	* config/msp430/slli.S (__gnu_mspabi_sllp): New.
	* config/msp430/srai.S (__gnu_mspabi_srap): New.
	* config/msp430/srli.S (__gnu_mspabi_srlp): New.

gcc/testsuite/ChangeLog:

	* gcc.target/msp430/emulate-srli.c: Fix expected assembler text.
	* gcc.target/msp430/max-inline-shift-430-no-opt.c: New test.
	* gcc.target/msp430/max-inline-shift-430.c: New test.
	* gcc.target/msp430/max-inline-shift-430x.c: New test.
This commit is contained in:
Jozef Lawrynowicz 2020-08-26 20:50:58 +01:00
parent af06acfc8d
commit 703e049aa7
14 changed files with 530 additions and 377 deletions

View File

@ -25,15 +25,16 @@
"Register R13.")
(define_constraint "K"
"Integer constant 1."
"Integer constant 1-19."
(and (match_code "const_int")
(match_test "IN_RANGE (ival, 1, 1)")))
(match_test "IN_RANGE (ival, 1, 19)")))
(define_constraint "L"
"Integer constant -1^20..1^19."
(and (match_code "const_int")
(match_test "IN_RANGE (ival, HOST_WIDE_INT_M1U << 20, 1 << 19)")))
;; Valid shift amount for RRUM, RRAM, RLAM, RRCM.
(define_constraint "M"
"Integer constant 1-4."
(and (match_code "const_int")
@ -49,6 +50,11 @@
(and (match_code "const_int")
(match_test "IN_RANGE (ival, 256, 65535)")))
(define_constraint "P"
"Integer constant 1-16."
(and (match_code "const_int")
(match_test "IN_RANGE (ival, 1, 16)")))
;; We do not allow arbitrary constants, eg symbols or labels,
;; because their address may be above the 16-bit address limit
;; supported by the offset used in the MOVA instruction.

View File

@ -35,7 +35,6 @@ rtx msp430_incoming_return_addr_rtx (void);
void msp430_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, int);
int msp430_initial_elimination_offset (int, int);
bool msp430_is_interrupt_func (void);
const char * msp430x_logical_shift_right (rtx);
const char * msp430_mcu_name (void);
void msp430_output_aligned_decl_common (FILE *, const tree, const char *,
unsigned HOST_WIDE_INT, unsigned,
@ -51,4 +50,9 @@ bool msp430_use_f5_series_hwmult (void);
bool msp430_has_hwmult (void);
bool msp430_op_not_in_high_mem (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);
#endif
#endif /* GCC_MSP430_PROTOS_H */

View File

@ -1064,15 +1064,6 @@ static bool msp430_rtx_costs (rtx x ATTRIBUTE_UNUSED,
return true;
}
break;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (!msp430x)
{
*total = COSTS_N_INSNS (100);
return true;
}
break;
}
return false;
}
@ -2674,32 +2665,6 @@ msp430_init_dwarf_reg_sizes_extra (tree address)
}
}
/* This is a list of MD patterns that implement fixed-count shifts. */
static struct
{
const char *name;
int count;
int need_430x;
rtx (*genfunc)(rtx,rtx);
}
const_shift_helpers[] =
{
#define CSH(N,C,X,G) { "__mspabi_" N, C, X, gen_##G }
CSH ("slli", 1, 1, slli_1),
CSH ("slll", 1, 1, slll_1),
CSH ("slll", 2, 1, slll_2),
CSH ("srai", 1, 0, srai_1),
CSH ("sral", 1, 0, sral_1),
CSH ("sral", 2, 0, sral_2),
CSH ("srll", 1, 0, srll_1),
CSH ("srll", 2, 1, srll_2x),
{ 0, 0, 0, 0 }
#undef CSH
};
/* The MSP430 ABI defines a number of helper functions that should be
used for, for example, 32-bit shifts. This function is called to
emit such a function, using the table above to optimize some
@ -2716,31 +2681,12 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
machine_mode arg0mode = GET_MODE (operands[0]);
machine_mode arg1mode = GET_MODE (operands[1]);
machine_mode arg2mode = GET_MODE (operands[2]);
int have_430x = msp430x ? 1 : 0;
int expand_mpy = strncmp (helper_name, "__mspabi_mpy",
sizeof ("__mspabi_mpy") - 1) == 0;
/* This function has been used incorrectly if CONST_VARIANTS is TRUE for a
hwmpy function. */
gcc_assert (!(expand_mpy && const_variants));
/* Emit size-optimal insns for small shifts we can easily do inline. */
if (CONST_INT_P (operands[2]) && !expand_mpy)
{
int i;
for (i=0; const_shift_helpers[i].name; i++)
{
if (const_shift_helpers[i].need_430x <= have_430x
&& strcmp (helper_name, const_shift_helpers[i].name) == 0
&& INTVAL (operands[2]) == const_shift_helpers[i].count)
{
emit_insn (const_shift_helpers[i].genfunc (operands[0],
operands[1]));
return;
}
}
}
if (arg1mode != VOIDmode && arg2mode != VOIDmode)
/* Modes of arguments must be equal if not constants. */
gcc_assert (arg1mode == arg2mode);
@ -2835,6 +2781,190 @@ msp430_expand_helper (rtx *operands, const char *helper_name,
gen_rtx_REG (arg0mode, 12));
}
/* Return TRUE if the helper function should be used and FALSE if the shifts
insns should be emitted inline. */
static bool
use_helper_for_const_shift (enum rtx_code code, machine_mode mode,
HOST_WIDE_INT amt)
{
const int default_inline_shift = 4;
/* We initialize the option to 65 so we know if the user set it or not. */
int user_set_max_inline = (msp430_max_inline_shift == 65 ? 0 : 1);
int max_inline = (user_set_max_inline ? msp430_max_inline_shift
: default_inline_shift);
/* 32-bit shifts are roughly twice as costly as 16-bit shifts so we adjust
the heuristic accordingly. */
int max_inline_32 = max_inline / 2;
/* Don't use helpers for these modes on 430X, when optimizing for speed, or
when emitting a small number of insns. */
if ((mode == E_QImode || mode == E_HImode || mode == E_PSImode)
&& (msp430x
/* If the user set max_inline then we always obey that number.
Otherwise we always emit the shifts inline at -O2 and above. */
|| amt <= max_inline
|| (!user_set_max_inline
&& (optimize >= 2 && !optimize_size))))
return false;
/* 430 and 430X codegen for SImode shifts is the same.
Set a hard limit of 15 for the number of shifts that will be emitted
inline by default, even at -O2 and above, to prevent code size
explosion. */
if (mode == E_SImode
&& (amt <= max_inline_32
|| (!user_set_max_inline
&& (optimize >= 2 && !optimize_size)
&& amt <= 15)))
return false;
return true;
}
/* For shift operations which will use an mspabi helper function, setup the
call to msp430_expand helper. Return 1 to indicate we have finished with
this insn and invoke "DONE".
Otherwise return 0 to indicate the insn should fallthrough.
Never FAIL. */
int
msp430_expand_shift (enum rtx_code code, machine_mode mode, rtx *operands)
{
/* Always use the helper function when the shift amount is not a
constant. */
if (!CONST_INT_P (operands[2])
|| mode == E_DImode
|| use_helper_for_const_shift (code, mode, INTVAL (operands[2])))
{
const char *helper_name = NULL;
/* The const variants of mspabi shifts have significantly larger code
size than the generic version, so use the generic version if
optimizing for size. */
bool const_variant = !optimize_size;
switch (mode)
{
case E_HImode:
helper_name = (code == ASHIFT ? "__mspabi_slli" :
(code == ASHIFTRT ? "__mspabi_srai" :
(code == LSHIFTRT ? "__mspabi_srli" :
NULL)));
break;
case E_PSImode:
helper_name = (code == ASHIFT ? "__gnu_mspabi_sllp" :
(code == ASHIFTRT ? "__gnu_mspabi_srap" :
(code == LSHIFTRT ? "__gnu_mspabi_srlp" :
NULL)));
/* No const variant for PSImode shifts FIXME. */
const_variant = false;
break;
case E_SImode:
helper_name = (code == ASHIFT ? "__mspabi_slll" :
(code == ASHIFTRT ? "__mspabi_sral" :
(code == LSHIFTRT ? "__mspabi_srll" :
NULL)));
break;
case E_DImode:
helper_name = (code == ASHIFT ? "__mspabi_sllll" :
(code == ASHIFTRT ? "__mspabi_srall" :
(code == LSHIFTRT ? "__mspabi_srlll" :
NULL)));
/* No const variant for DImode shifts. */
const_variant = false;
break;
default:
gcc_unreachable ();
break;
}
gcc_assert (helper_name);
msp430_expand_helper (operands, helper_name, const_variant);
return 1;
}
/* When returning 0, there must be an insn to match the RTL pattern
otherwise there will be an unrecognizeable insn. */
return 0;
}
/* Helper function to emit a sequence of shift instructions. The amount of
shift instructions to emit is in OPERANDS[2].
For 430 we output copies of identical inline shifts for all modes.
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 *
msp430_output_asm_shift_insns (enum rtx_code code, machine_mode mode,
rtx *operands)
{
int i;
int amt;
int max_shift = GET_MODE_BITSIZE (mode) - 1;
gcc_assert (CONST_INT_P (operands[2]));
amt = INTVAL (operands[2]);
if (amt == 0 || amt > max_shift)
{
switch (code)
{
case ASHIFT:
output_asm_insn ("# ignored undefined behaviour left shift "
"of %1 by %2", operands);
break;
case ASHIFTRT:
output_asm_insn ("# ignored undefined behaviour arithmetic right "
"shift of %1 by %2", operands);
break;
case LSHIFTRT:
output_asm_insn ("# ignored undefined behaviour logical right shift "
"of %1 by %2", operands);
break;
default:
gcc_unreachable ();
}
return "";
}
if (code == ASHIFT)
{
if (!msp430x && mode == HImode)
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);
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);
else if (mode == SImode)
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);
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);
/* 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);
#endif
else
gcc_unreachable ();
}
return "";
}
/* Called by cbranch<mode>4 to coerce operands into usable forms. */
void
msp430_fixup_compare_operands (machine_mode my_mode, rtx * operands)
@ -3368,6 +3498,7 @@ msp430_op_not_in_high_mem (rtx op)
O offset of the top of the stack
Q like X but generates an A postfix
R inverse of condition code, unsigned.
W value - 16
X X instruction postfix in large mode
Y value - 4
Z value - 1
@ -3394,6 +3525,11 @@ msp430_print_operand (FILE * file, rtx op, int letter)
/* Print the constant value, less four. */
fprintf (file, "#%ld", INTVAL (op) - 4);
return;
case 'W':
gcc_assert (CONST_INT_P (op));
/* Print the constant value, less 16. */
fprintf (file, "#%ld", INTVAL (op) - 16);
return;
case 'I':
if (GET_CODE (op) == CONST_INT)
{
@ -3711,34 +3847,6 @@ msp430x_extendhisi (rtx * operands)
return "MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0";
}
/* Likewise for logical right shifts. */
const char *
msp430x_logical_shift_right (rtx amount)
{
/* The MSP430X's logical right shift instruction - RRUM - does
not use an extension word, so we cannot encode a repeat count.
Try various alternatives to work around this. If the count
is in a register we are stuck, hence the assert. */
gcc_assert (CONST_INT_P (amount));
if (INTVAL (amount) <= 0
|| INTVAL (amount) >= 16)
return "# nop logical shift.";
if (INTVAL (amount) > 0
&& INTVAL (amount) < 5)
return "rrum.w\t%2, %0"; /* Two bytes. */
if (INTVAL (amount) > 4
&& INTVAL (amount) < 9)
return "rrum.w\t#4, %0 { rrum.w\t%Y2, %0 "; /* Four bytes. */
/* First we logically shift right by one. Now we know
that the top bit is zero and we can use the arithmetic
right shift instruction to perform the rest of the shift. */
return "rrum.w\t#1, %0 { rpt\t%Z2 { rrax.w\t%0"; /* Six bytes. */
}
/* Stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)). */
#undef TARGET_CAN_CHANGE_MODE_CLASS

View File

@ -65,6 +65,15 @@
(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:
;;
@ -689,31 +698,42 @@
MOV%X1.B\t%1, %0"
)
;; 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")
(ashift:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "rm"))
(match_operand:HI 2 "immediate_operand" "M")))]
[(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"
"MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
"@
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"
)
;; We are taking a char and shifting it and putting the result in 2 registers.
;; the high register will always be for 0 shift counts < 8.
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (zero_extend:SI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))
(match_operand:HI 2 "immediate_operand" "M")))]
[(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"
"MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
"@
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"
)
;; Same as above but with a NOP sign_extend round the subreg
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0)))
(match_operand:HI 2 "immediate_operand" "M")))]
[(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"
"MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0"
"@
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"
)
(define_insn ""
@ -724,11 +744,14 @@
)
(define_insn ""
[(set (match_operand:PSI 0 "register_operand" "=r")
(ashift:PSI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))
(match_operand:HI 2 "immediate_operand" "M")))]
[(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"
"MOV%X1.B %1, %0 { RLAM.W %2, %0"
"@
RLAM.W %2, %0
MOV%X1.B %1, %0 { RLAM.W %2, %0
MOV%X1.B %1, %0 { RPT %2 { RLAX.A %0"
)
;; END msp430 pointer manipulation combine insn patterns
@ -840,287 +863,75 @@
;; 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
;; 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"
"* return msp430_output_asm_shift_insns (<CODE>, HImode, operands);"
)
(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")))]
;; 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")))]
""
"* return msp430_output_asm_shift_insns (<CODE>, SImode, operands);"
)
(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"
"*
if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 5)
return \"RLAM.W\t%2, %0\";
else if (INTVAL (operands[2]) >= 5 && INTVAL (operands[2]) < 16)
return \"RPT\t%2 { RLAX.W\t%0\";
return \"# nop left shift\";
"
"@
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"
)
(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;
}
)
;;----------
;; 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")))]
""
{
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")))]
(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"
"*
if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 5)
return \"RRAM.W\t%2, %0\";
else if (INTVAL (operands[2]) >= 5 && INTVAL (operands[2]) < 16)
return \"RPT\t%2 { RRAX.W\t%0\";
return \"# nop arith right shift\";
"
"@
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"
)
(define_insn "srap_1"
[(set (match_operand:PSI 0 "register_operand" "=r")
(ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
(const_int 1)))]
(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"
"RRAM.A #1,%0"
)
(define_insn "srap_2"
[(set (match_operand:PSI 0 "register_operand" "=r")
(ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0")
(const_int 2)))]
"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")))]
"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)))]
"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"
)
;;------------------------------------------------------------
@ -1427,7 +1238,7 @@
[(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 "" ""))
(pc)))
@ -1443,7 +1254,7 @@
[(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 "" ""))
(pc)))
@ -1457,7 +1268,7 @@
[(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)
(label_ref (match_operand 2 "" ""))))
@ -1471,7 +1282,7 @@
[(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)
(label_ref (match_operand 2 "" ""))))

View File

@ -109,3 +109,9 @@ mdevices-csv-loc=
Target Joined Var(msp430_devices_csv_loc) RejectNegative Report
The path to devices.csv. The GCC driver can normally locate devices.csv itself
and pass this option to the compiler, so the user shouldn't need to pass this.
mmax-inline-shift=
Target RejectNegative Joined UInteger IntegerRange(0,65) Var(msp430_max_inline_shift) Init(65) Report
For shift operations by a constant amount, which require an individual instruction to shift by one
position, set the maximum number of inline shift instructions (maximum value 64) to emit instead of using the corresponding __mspabi helper function.
The default value is 4.

View File

@ -113,12 +113,21 @@
(ior (match_code "reg,mem")
(match_operand 0 "immediate_operand"))))
; TRUE for constants which are bit positions for zero_extract
(define_predicate "msp430_bitpos"
(define_predicate "const_1_to_8_operand"
(and (match_code "const_int")
(match_test (" INTVAL (op) >= 1
&& INTVAL (op) <= 8 "))))
(define_predicate "const_0_to_15_operand"
(and (match_code "const_int")
(match_test (" INTVAL (op) >= 0
&& INTVAL (op) <= 15 "))))
(define_predicate "const_1_to_19_operand"
(and (match_code "const_int")
(match_test (" INTVAL (op) >= 1
&& INTVAL (op) <= 19 "))))
(define_predicate "msp430_symbol_operand"
(match_code "symbol_ref")
)

View File

@ -1070,7 +1070,7 @@ Objective-C and Objective-C++ Dialects}.
-mwarn-mcu @gol
-mcode-region= -mdata-region= @gol
-msilicon-errata= -msilicon-errata-warn= @gol
-mhwmult= -minrt -mtiny-printf}
-mhwmult= -minrt -mtiny-printf -mmax-inline-shift=}
@emph{NDS32 Options}
@gccoptlist{-mbig-endian -mlittle-endian @gol
@ -24816,6 +24816,19 @@ buffered before it is sent to write.
This option requires Newlib Nano IO, so GCC must be configured with
@samp{--enable-newlib-nano-formatted-io}.
@item -mmax-inline-shift=
@opindex mmax-inline-shift=
This option takes an integer between 0 and 64 inclusive, and sets
the maximum number of inline shift instructions which should be emitted to
perform a shift operation by a constant amount. When this value needs to be
exceeded, an mspabi helper function is used instead. The default value is 4.
This only affects cases where a shift by multiple positions cannot be
completed with a single instruction (e.g. all shifts >1 on the 430 ISA).
Shifts of a 32-bit value are at least twice as costly, so the value passed for
this option is divided by 2 and the resulting value used instead.
@item -mcode-region=
@itemx -mdata-region=
@opindex mcode-region

View File

@ -2,7 +2,7 @@
/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" } { "" } } */
/* { dg-options "-Os" } */
/* { dg-final { scan-assembler-not "mspabi_srli" } } */
/* { dg-final { scan-assembler "rrum" } } */
/* { dg-final { scan-assembler "RRUM" } } */
/* Ensure that HImode shifts with source operand in memory are emulated with a
rotate instructions. */

View File

@ -0,0 +1,52 @@
/* { dg-do compile } */
/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430x" "-mlarge" } { "" } } */
/* { dg-options "-mcpu=msp430" } */
/* { dg-final { scan-assembler-not "__mspabi_slli_4" } } */
/* { dg-final { scan-assembler-not "__mspabi_sral_2" } } */
/* { dg-final { scan-assembler "__mspabi_slli_5" } } */
/* { dg-final { scan-assembler "__mspabi_sral_3" } } */
/* Test the default value of 4 for -mmax-inline-shift has been observed. */
volatile int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
volatile long l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15;
void
ashift (void)
{
a1 <<= 1;
a2 <<= 2;
a3 <<= 3;
a4 <<= 4;
a5 <<= 5;
a6 <<= 6;
a7 <<= 7;
a8 <<= 8;
a9 <<= 9;
a10 <<= 10;
a11 <<= 11;
a12 <<= 12;
a13 <<= 13;
a14 <<= 14;
a15 <<= 15;
}
void
ashiftrt (void)
{
l1 >>= 1;
l2 >>= 2;
l3 >>= 3;
l4 >>= 4;
l5 >>= 5;
l6 >>= 6;
l7 >>= 7;
l8 >>= 8;
l9 >>= 9;
l10 >>= 10;
l11 >>= 11;
l12 >>= 12;
l13 >>= 13;
l14 >>= 14;
l15 >>= 15;
}

View File

@ -0,0 +1,50 @@
/* { dg-do compile } */
/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430x" "-mlarge" } { "" } } */
/* { dg-options "-mcpu=msp430 -mmax-inline-shift=10" } */
/* { dg-final { scan-assembler-not "__mspabi_slli_10" } } */
/* { dg-final { scan-assembler-not "__mspabi_sral_5" } } */
/* { dg-final { scan-assembler "__mspabi_slli_11" } } */
/* { dg-final { scan-assembler "__mspabi_sral_6" } } */
volatile int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
volatile long l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15;
void
ashift (void)
{
a1 <<= 1;
a2 <<= 2;
a3 <<= 3;
a4 <<= 4;
a5 <<= 5;
a6 <<= 6;
a7 <<= 7;
a8 <<= 8;
a9 <<= 9;
a10 <<= 10;
a11 <<= 11;
a12 <<= 12;
a13 <<= 13;
a14 <<= 14;
a15 <<= 15;
}
void
ashiftrt (void)
{
l1 >>= 1;
l2 >>= 2;
l3 >>= 3;
l4 >>= 4;
l5 >>= 5;
l6 >>= 6;
l7 >>= 7;
l8 >>= 8;
l9 >>= 9;
l10 >>= 10;
l11 >>= 11;
l12 >>= 12;
l13 >>= 13;
l14 >>= 14;
l15 >>= 15;
}

View File

@ -0,0 +1,48 @@
/* { dg-do compile } */
/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" } { "" } } */
/* { dg-options "-mcpu=msp430x -mmax-inline-shift=10" } */
/* { dg-final { scan-assembler-not "__mspabi_slli" } } */
/* { dg-final { scan-assembler "__mspabi_sral_6" } } */
volatile int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15;
volatile long l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15;
void
ashift (void)
{
a1 <<= 1;
a2 <<= 2;
a3 <<= 3;
a4 <<= 4;
a5 <<= 5;
a6 <<= 6;
a7 <<= 7;
a8 <<= 8;
a9 <<= 9;
a10 <<= 10;
a11 <<= 11;
a12 <<= 12;
a13 <<= 13;
a14 <<= 14;
a15 <<= 15;
}
void
ashiftrt (void)
{
l1 >>= 1;
l2 >>= 2;
l3 >>= 3;
l4 >>= 4;
l5 >>= 5;
l6 >>= 6;
l7 >>= 7;
l8 >>= 8;
l9 >>= 9;
l10 >>= 10;
l11 >>= 11;
l12 >>= 12;
l13 >>= 13;
l14 >>= 14;
l15 >>= 15;
}

View File

@ -65,6 +65,21 @@ __mspabi_slli:
RET
#endif
#ifdef __MSP430X__
.section .text.__gnu_mspabi_sllp
1: ADDA #-1,R13
ADDA R12,R12
.global __gnu_mspabi_sllp
__gnu_mspabi_sllp:
CMP #0,R13
JNZ 1b
#ifdef __MSP430X_LARGE__
RETA
#else
RET
#endif /* __MSP430X_LARGE__ */
#endif /* __MSP430X__ */
/* Logical Left Shift - R12:R13 -> R12:R13. */
.section .text.__mspabi_slll_n

View File

@ -64,6 +64,21 @@ __mspabi_srai:
RET
#endif
#ifdef __MSP430X__
.section .text.__gnu_mspabi_srap
1: ADDA #-1,R13
RRAX.A R12,R12
.global __gnu_mspabi_srap
__gnu_mspabi_srap:
CMP #0,R13
JNZ 1b
#ifdef __MSP430X_LARGE__
RETA
#else
RET
#endif /* __MSP430X_LARGE__ */
#endif /* __MSP430X__ */
/* Arithmetic Right Shift - R12:R13 -> R12:R13. */
.section .text.__mspabi_sral_n

View File

@ -66,6 +66,22 @@ __mspabi_srli:
RET
#endif
#ifdef __MSP430X__
.section .text.__gnu_mspabi_srlp
1: ADDA #-1,R13
CLRC
RRCX.A R12,R12
.global __gnu_mspabi_srlp
__gnu_mspabi_srlp:
CMP #0,R13
JNZ 1b
#ifdef __MSP430X_LARGE__
RETA
#else
RET
#endif /* __MSP430X_LARGE__ */
#endif /* __MSP430X__ */
/* Logical Right Shift - R12:R13 -> R12:R13. */
.section .text.__mspabi_srll_n