builtins.c: (expand_builtin_memcmp, expand_builtin_strncmp): s/cmpstrsi/cmpstrnsi

2005-07-12  Adrian Straetling  <straetling@de.ibm.com>

	* builtins.c: (expand_builtin_memcmp, expand_builtin_strncmp):
	s/cmpstrsi/cmpstrnsi
	(expand_builtin_strcmp): Rewrite to support both 'cmpstrsi' and
	'cmpstrnsi'.
	* optabs.c: (prepare_cmp_insn): Add availability of 'cmpstrn'.
	(init_optabs): Initialize cmpstrn_optab.
	* optabs.h: (enum insn_code cmpstrn_optab): Declare.
	* genopinit.c: (optabs[]): Add 'cmpstrn' to initialisation.
	* expr.c: (enum insn_code cmpstrn_optab): Declare.
	* config/i386/i386.md: s/cmpstr/cmpstrn
	* config/c4x/c4x.md: s/cmpstr/cmpstrn
	* doc/md.texi: Update documentation.

From-SVN: r101916
This commit is contained in:
Adrian Straetling 2005-07-12 09:20:21 +00:00 committed by Ulrich Weigand
parent 72f9377893
commit 40c1d5f854
9 changed files with 190 additions and 119 deletions

View file

@ -1,3 +1,18 @@
2005-07-12 Adrian Straetling <straetling@de.ibm.com>
* builtins.c: (expand_builtin_memcmp, expand_builtin_strncmp):
s/cmpstrsi/cmpstrnsi
(expand_builtin_strcmp): Rewrite to support both 'cmpstrsi' and
'cmpstrnsi'.
* optabs.c: (prepare_cmp_insn): Add availability of 'cmpstrn'.
(init_optabs): Initialize cmpstrn_optab.
* optabs.h: (enum insn_code cmpstrn_optab): Declare.
* genopinit.c: (optabs[]): Add 'cmpstrn' to initialisation.
* expr.c: (enum insn_code cmpstrn_optab): Declare.
* config/i386/i386.md: s/cmpstr/cmpstrn
* config/c4x/c4x.md: s/cmpstr/cmpstrn
* doc/md.texi: Update documentation.
2005-07-11 Richard Henderson <rth@redhat.com>
* config/alpha/alpha.c (alpha_gimplify_va_arg_1): Use

View file

@ -3449,7 +3449,7 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi
{
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
@ -3469,9 +3469,9 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
else
#endif
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
#ifdef HAVE_cmpstrnsi
if (HAVE_cmpstrnsi)
insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
else
#endif
return 0;
@ -3504,10 +3504,10 @@ expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
GEN_INT (MIN (arg1_align, arg2_align)));
else
#endif
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
#ifdef HAVE_cmpstrnsi
if (HAVE_cmpstrnsi)
insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
else
#endif
gcc_unreachable ();
@ -3558,103 +3558,134 @@ expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
#if defined HAVE_cmpstrsi || defined HAVE_cmpstrnsi
if (cmpstr_optab[SImode] != CODE_FOR_nothing
|| cmpstrn_optab[SImode] != CODE_FOR_nothing)
{
rtx arg1_rtx, arg2_rtx;
rtx result, insn = NULL_RTX;
tree fndecl, fn;
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
int arg2_align
= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
/* If we don't have POINTER_TYPE, call the function. */
if (arg1_align == 0 || arg2_align == 0)
return 0;
/* Stabilize the arguments in case gen_cmpstr(n)si fail. */
arg1 = builtin_save_expr (arg1);
arg2 = builtin_save_expr (arg2);
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
{
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
tree fndecl, fn;
/* Try to call cmpstrsi. */
if (HAVE_cmpstrsi)
{
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
int arg2_align
= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
&& REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
len1 = c_strlen (arg1, 1);
len2 = c_strlen (arg2, 1);
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
}
#endif
#if HAVE_cmpstrnsi
/* Try to determine at least one length and call cmpstrnsi. */
if (!insn && HAVE_cmpstrnsi)
{
tree len;
rtx arg3_rtx;
if (len1)
len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
if (len2)
len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
tree len1 = c_strlen (arg1, 1);
tree len2 = c_strlen (arg2, 1);
/* If we don't have a constant length for the first, use the length
of the second, if we know it. We don't require a constant for
this case; some cost analysis could be done if both are available
but neither is constant. For now, assume they're equally cheap,
unless one has side effects. If both strings have constant lengths,
use the smaller. */
if (len1)
len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
if (len2)
len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
if (!len1)
len = len2;
else if (!len2)
len = len1;
else if (TREE_SIDE_EFFECTS (len1))
len = len2;
else if (TREE_SIDE_EFFECTS (len2))
len = len1;
else if (TREE_CODE (len1) != INTEGER_CST)
len = len2;
else if (TREE_CODE (len2) != INTEGER_CST)
len = len1;
else if (tree_int_cst_lt (len1, len2))
len = len1;
else
len = len2;
/* If we don't have a constant length for the first, use the length
of the second, if we know it. We don't require a constant for
this case; some cost analysis could be done if both are available
but neither is constant. For now, assume they're equally cheap,
unless one has side effects. If both strings have constant lengths,
use the smaller. */
/* If both arguments have side effects, we cannot optimize. */
if (!len || TREE_SIDE_EFFECTS (len))
return 0;
if (!len1)
len = len2;
else if (!len2)
len = len1;
else if (TREE_SIDE_EFFECTS (len1))
len = len2;
else if (TREE_SIDE_EFFECTS (len2))
len = len1;
else if (TREE_CODE (len1) != INTEGER_CST)
len = len2;
else if (TREE_CODE (len2) != INTEGER_CST)
len = len1;
else if (tree_int_cst_lt (len1, len2))
len = len1;
else
len = len2;
/* If we don't have POINTER_TYPE, call the function. */
if (arg1_align == 0 || arg2_align == 0)
return 0;
/* If both arguments have side effects, we cannot optimize. */
if (!len || TREE_SIDE_EFFECTS (len))
return 0;
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
&& REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
/* Stabilize the arguments in case gen_cmpstrnsi fails. */
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
/* Stabilize the arguments in case gen_cmpstrsi fails. */
arg1 = builtin_save_expr (arg1);
arg2 = builtin_save_expr (arg2);
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
&& REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
if (insn)
{
emit_insn (insn);
insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
}
#endif
/* Return the value in the proper mode for this function. */
mode = TYPE_MODE (TREE_TYPE (exp));
if (GET_MODE (result) == mode)
return result;
if (target == 0)
return convert_to_mode (mode, result, 0);
convert_move (target, result, 0);
return target;
}
if (insn)
{
emit_insn (insn);
/* Expand the library call ourselves using a stabilized argument
list to avoid re-evaluating the function's arguments twice. */
arglist = build_tree_list (NULL_TREE, arg2);
arglist = tree_cons (NULL_TREE, arg1, arglist);
fndecl = get_callee_fndecl (exp);
fn = build_function_call_expr (fndecl, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_call (fn, target, target == const0_rtx);
}
/* Return the value in the proper mode for this function. */
mode = TYPE_MODE (TREE_TYPE (exp));
if (GET_MODE (result) == mode)
return result;
if (target == 0)
return convert_to_mode (mode, result, 0);
convert_move (target, result, 0);
return target;
}
/* Expand the library call ourselves using a stabilized argument
list to avoid re-evaluating the function's arguments twice. */
arglist = build_tree_list (NULL_TREE, arg2);
arglist = tree_cons (NULL_TREE, arg1, arglist);
fndecl = get_callee_fndecl (exp);
fn = build_function_call_expr (fndecl, arglist);
if (TREE_CODE (fn) == CALL_EXPR)
CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
}
@ -3679,10 +3710,10 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
}
/* If c_strlen can determine an expression for one of the string
lengths, and it doesn't have side effects, then emit cmpstrsi
lengths, and it doesn't have side effects, then emit cmpstrnsi
using length MIN(strlen(string)+1, arg3). */
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
#ifdef HAVE_cmpstrnsi
if (HAVE_cmpstrnsi)
{
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
@ -3697,7 +3728,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
int arg2_align
= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
= insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
len1 = c_strlen (arg1, 1);
len2 = c_strlen (arg2, 1);
@ -3750,7 +3781,7 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
/* Stabilize the arguments in case gen_cmpstrsi fails. */
/* Stabilize the arguments in case gen_cmpstrnsi fails. */
arg1 = builtin_save_expr (arg1);
arg2 = builtin_save_expr (arg2);
len = builtin_save_expr (len);
@ -3758,8 +3789,8 @@ expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
if (insn)
{
emit_insn (insn);

View file

@ -5714,7 +5714,7 @@
}")
(define_insn "*cmpstrqi"
(define_insn "*cmpstrnqi"
[(set (match_operand:QI 0 "ext_reg_operand" "=d")
(compare:QI (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a"))
(mem:BLK (match_operand:QI 2 "addr_reg_operand" "+a"))))
@ -5731,7 +5731,7 @@
return \"\";
}")
(define_expand "cmpstrqi"
(define_expand "cmpstrnqi"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(compare:QI (match_operand:BLK 1 "general_operand" "")
(match_operand:BLK 2 "general_operand" "")))

View file

@ -17446,7 +17446,7 @@
(set_attr "memory" "store")
(set_attr "mode" "QI")])
(define_expand "cmpstrsi"
(define_expand "cmpstrnsi"
[(set (match_operand:SI 0 "register_operand" "")
(compare:SI (match_operand:BLK 1 "general_operand" "")
(match_operand:BLK 2 "general_operand" "")))
@ -17487,8 +17487,8 @@
emit_move_insn (operands[0], const0_rtx);
DONE;
}
emit_insn (gen_cmpstrqi_nz_1 (addr1, addr2, countreg, align,
operands[1], operands[2]));
emit_insn (gen_cmpstrnqi_nz_1 (addr1, addr2, countreg, align,
operands[1], operands[2]));
}
else
{
@ -17496,8 +17496,8 @@
emit_insn (gen_cmpdi_1_rex64 (countreg, countreg));
else
emit_insn (gen_cmpsi_1 (countreg, countreg));
emit_insn (gen_cmpstrqi_1 (addr1, addr2, countreg, align,
operands[1], operands[2]));
emit_insn (gen_cmpstrnqi_1 (addr1, addr2, countreg, align,
operands[1], operands[2]));
}
outlow = gen_lowpart (QImode, out);
@ -17528,7 +17528,7 @@
;; memcmp recognizers. The `cmpsb' opcode does nothing if the count is
;; zero. Emit extra code to make sure that a zero-length compare is EQ.
(define_expand "cmpstrqi_nz_1"
(define_expand "cmpstrnqi_nz_1"
[(parallel [(set (reg:CC FLAGS_REG)
(compare:CC (match_operand 4 "memory_operand" "")
(match_operand 5 "memory_operand" "")))
@ -17541,7 +17541,7 @@
""
"")
(define_insn "*cmpstrqi_nz_1"
(define_insn "*cmpstrnqi_nz_1"
[(set (reg:CC FLAGS_REG)
(compare:CC (mem:BLK (match_operand:SI 4 "register_operand" "0"))
(mem:BLK (match_operand:SI 5 "register_operand" "1"))))
@ -17557,7 +17557,7 @@
(set_attr "mode" "QI")
(set_attr "prefix_rep" "1")])
(define_insn "*cmpstrqi_nz_rex_1"
(define_insn "*cmpstrnqi_nz_rex_1"
[(set (reg:CC FLAGS_REG)
(compare:CC (mem:BLK (match_operand:DI 4 "register_operand" "0"))
(mem:BLK (match_operand:DI 5 "register_operand" "1"))))
@ -17575,7 +17575,7 @@
;; The same, but the count is not known to not be zero.
(define_expand "cmpstrqi_1"
(define_expand "cmpstrnqi_1"
[(parallel [(set (reg:CC FLAGS_REG)
(if_then_else:CC (ne (match_operand 2 "register_operand" "")
(const_int 0))
@ -17591,7 +17591,7 @@
""
"")
(define_insn "*cmpstrqi_1"
(define_insn "*cmpstrnqi_1"
[(set (reg:CC FLAGS_REG)
(if_then_else:CC (ne (match_operand:SI 6 "register_operand" "2")
(const_int 0))
@ -17610,7 +17610,7 @@
(set_attr "mode" "QI")
(set_attr "prefix_rep" "1")])
(define_insn "*cmpstrqi_rex_1"
(define_insn "*cmpstrnqi_rex_1"
[(set (reg:CC FLAGS_REG)
(if_then_else:CC (ne (match_operand:DI 6 "register_operand" "2")
(const_int 0))
@ -17693,9 +17693,9 @@
(set_attr "mode" "QI")
(set_attr "prefix_rep" "1")])
;; Peephole optimizations to clean up after cmpstr*. This should be
;; Peephole optimizations to clean up after cmpstrn*. This should be
;; handled in combine, but it is not currently up to the task.
;; When used for their truth value, the cmpstr* expanders generate
;; When used for their truth value, the cmpstrn* expanders generate
;; code like this:
;;
;; repz cmpsb
@ -17706,7 +17706,7 @@
;;
;; The intermediate three instructions are unnecessary.
;; This one handles cmpstr*_nz_1...
;; This one handles cmpstrn*_nz_1...
(define_peephole2
[(parallel[
(set (reg:CC FLAGS_REG)
@ -17738,7 +17738,7 @@
(clobber (match_dup 2))])]
"")
;; ...and this one handles cmpstr*_1.
;; ...and this one handles cmpstrn*_1.
(define_peephole2
[(parallel[
(set (reg:CC FLAGS_REG)

View file

@ -3269,8 +3269,8 @@ operand.
The use for multiple @code{setmem@var{m}} is as for @code{movmem@var{m}}.
@cindex @code{cmpstr@var{m}} instruction pattern
@item @samp{cmpstr@var{m}}
@cindex @code{cmpstrn@var{m}} instruction pattern
@item @samp{cmpstrn@var{m}}
String compare instruction, with five operands. Operand 0 is the output;
it has mode @var{m}. The remaining four operands are like the operands
of @samp{movmem@var{m}}. The two memory blocks specified are compared
@ -3281,6 +3281,25 @@ that may access an invalid page or segment and cause a fault. The
effect of the instruction is to store a value in operand 0 whose sign
indicates the result of the comparison.
@cindex @code{cmpstr@var{m}} instruction pattern
@item @samp{cmpstr@var{m}}
String compare instruction, without known maximum length. Operand 0 is the
output; it has mode @var{m}. The second and third operand are the blocks of
memory to be compared; both are @code{mem:BLK} with an address in mode
@code{Pmode}.
The fourth operand is the known shared alignment of the source and
destination, in the form of a @code{const_int} rtx. Thus, if the
compiler knows that both source and destination are word-aligned,
it may provide the value 4 for this operand.
The two memory blocks specified are compared byte by byte in lexicographic
order starting at the beginning of each string. The instruction is not allowed
to prefetch more than one byte at a time since either string may end in the
first byte and reading past that may access an invalid page or segment and
cause a fault. The effect of the instruction is to store a value in operand 0
whose sign indicates the result of the comparison.
@cindex @code{cmpmem@var{m}} instruction pattern
@item @samp{cmpmem@var{m}}
Block compare instruction, with five operands like the operands

View file

@ -202,9 +202,10 @@ enum insn_code movmem_optab[NUM_MACHINE_MODES];
/* This array records the insn_code of insns to perform block sets. */
enum insn_code setmem_optab[NUM_MACHINE_MODES];
/* These arrays record the insn_code of two different kinds of insns
/* These arrays record the insn_code of three different kinds of insns
to perform block compares. */
enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
enum insn_code cmpstrn_optab[NUM_MACHINE_MODES];
enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
/* Synchronization primitives. */

View file

@ -169,6 +169,7 @@ static const char * const optabs[] =
"reload_out_optab[$A] = CODE_FOR_$(reload_out$a$)",
"movmem_optab[$A] = CODE_FOR_$(movmem$a$)",
"cmpstr_optab[$A] = CODE_FOR_$(cmpstr$a$)",
"cmpstrn_optab[$A] = CODE_FOR_$(cmpstrn$a$)",
"cmpmem_optab[$A] = CODE_FOR_$(cmpmem$a$)",
"setmem_optab[$A] = CODE_FOR_$(setmem$a$)",
"sync_add_optab[$A] = CODE_FOR_$(sync_add$I$a$)",

View file

@ -3425,6 +3425,8 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
cmp_code = cmpmem_optab[cmp_mode];
if (cmp_code == CODE_FOR_nothing)
cmp_code = cmpstr_optab[cmp_mode];
if (cmp_code == CODE_FOR_nothing)
cmp_code = cmpstrn_optab[cmp_mode];
if (cmp_code == CODE_FOR_nothing)
continue;
@ -5090,6 +5092,7 @@ init_optabs (void)
{
movmem_optab[i] = CODE_FOR_nothing;
cmpstr_optab[i] = CODE_FOR_nothing;
cmpstrn_optab[i] = CODE_FOR_nothing;
cmpmem_optab[i] = CODE_FOR_nothing;
setmem_optab[i] = CODE_FOR_nothing;

View file

@ -450,6 +450,7 @@ extern enum insn_code setmem_optab[NUM_MACHINE_MODES];
/* These arrays record the insn_code of two different kinds of insns
to perform block compares. */
extern enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
extern enum insn_code cmpstrn_optab[NUM_MACHINE_MODES];
extern enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
/* Synchronization primitives. This first set is atomic operation for