* defaults.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
(CTZ_DEFINED_VALUE_AT_ZERO): New.
* doc/rtl.texi, doc/tm.texi: Document them.
* combine.c (nonzero_bits) [CLZ, CTZ]: Handle the definedness
of the value at zero properly.
* fold-const.c (tree_expr_nonnegative_p): Likewise.
* simplify-rtx.c (simplify_unary_operation): Likewise.
* config/alpha/alpha.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
(CTZ_DEFINED_VALUE_AT_ZERO): New.
* config/arm/arm.c (TARGET_INIT_BUILTINS): Remove.
(TARGET_EXPAND_BUILTIN): Remove.
(def_builtin, arm_init_builtins, arm_expand_builtin): Remove.
* config/arm/arm.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
(enum arm_builtins): Remove.
* config/arm/arm.md (UNSPEC_CLZ): Remove.
(clzsi2): Rename from clz; use clz instead of unspec.
(ctzsi2): New.
* config/arm/arm-protos.h: Update.
From-SVN: r62453
+2003-02-05 Richard Henderson <rth@redhat.com>
+
+ * defaults.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
+ (CTZ_DEFINED_VALUE_AT_ZERO): New.
+ * doc/rtl.texi, doc/tm.texi: Document them.
+
+ * combine.c (nonzero_bits) [CLZ, CTZ]: Handle the definedness
+ of the value at zero properly.
+ * fold-const.c (tree_expr_nonnegative_p): Likewise.
+ * simplify-rtx.c (simplify_unary_operation): Likewise.
+
+ * config/alpha/alpha.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
+ (CTZ_DEFINED_VALUE_AT_ZERO): New.
+
+ * config/arm/arm.c (TARGET_INIT_BUILTINS): Remove.
+ (TARGET_EXPAND_BUILTIN): Remove.
+ (def_builtin, arm_init_builtins, arm_expand_builtin): Remove.
+ * config/arm/arm.h (CLZ_DEFINED_VALUE_AT_ZERO): New.
+ (enum arm_builtins): Remove.
+ * config/arm/arm.md (UNSPEC_CLZ): Remove.
+ (clzsi2): Rename from clz; use clz instead of unspec.
+ (ctzsi2): New.
+ * config/arm/arm-protos.h: Update.
+
Wed Feb 5 23:12:57 CET 2003 Jan Hubicka <jh@suse.cz>
* i386-protos.h (x86_emit_floatuns): Declare.
break;
case FFS:
- case CLZ:
- case CTZ:
case POPCOUNT:
/* This is at most the number of bits in the mode. */
- nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1;
+ nonzero = ((HOST_WIDE_INT) 2 << (floor_log2 (mode_width))) - 1;
+ break;
+
+ case CLZ:
+ /* If CLZ has a known value at zero, then the nonzero bits are
+ that value, plus the number of bits in the mode minus one. */
+ if (CLZ_DEFINED_VALUE_AT_ZERO (mode, nonzero))
+ nonzero |= ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width))) - 1;
+ else
+ nonzero = -1;
+ break;
+
+ case CTZ:
+ /* If CTZ has a known value at zero, then the nonzero bits are
+ that value, plus the number of bits in the mode minus one. */
+ if (CTZ_DEFINED_VALUE_AT_ZERO (mode, nonzero))
+ nonzero |= ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width))) - 1;
+ else
+ nonzero = -1;
+ break;
break;
case PARITY:
#define STORE_FLAG_VALUE 1
+/* The CIX ctlz and cttz instructions return 64 for zero. */
+#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 64, TARGET_CIX)
+#define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 64, TARGET_CIX)
+
/* Define the value returned by a floating-point comparison instruction. */
#define FLOAT_STORE_FLAG_VALUE(MODE) \
extern void arm_mark_dllimport PARAMS ((tree));
#endif
-extern void arm_init_builtins PARAMS ((void));
-#if defined (TREE_CODE) && defined (RTX_CODE)
-extern rtx arm_expand_builtin PARAMS ((tree, rtx, rtx,
- enum machine_mode, int));
-#endif
-
extern void arm_pr_long_calls PARAMS ((struct cpp_reader *));
extern void arm_pr_no_long_calls PARAMS ((struct cpp_reader *));
extern void arm_pr_long_calls_off PARAMS ((struct cpp_reader *));
#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES
#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes
-#undef TARGET_INIT_BUILTINS
-#define TARGET_INIT_BUILTINS arm_init_builtins
-
-#undef TARGET_EXPAND_BUILTIN
-#define TARGET_EXPAND_BUILTIN arm_expand_builtin
-
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST arm_adjust_cost
return value;
}
-
-#define def_builtin(NAME, TYPE, CODE) \
- builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, NULL_TREE)
-
-void
-arm_init_builtins ()
-{
- tree endlink = void_list_node;
- tree int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
- tree pchar_type_node = build_pointer_type (char_type_node);
-
- tree int_ftype_int, void_ftype_pchar;
-
- /* void func (char *) */
- void_ftype_pchar
- = build_function_type_list (void_type_node, pchar_type_node, NULL_TREE);
-
- /* int func (int) */
- int_ftype_int
- = build_function_type (integer_type_node, int_endlink);
-
- /* Initialize arm V5 builtins. */
- if (arm_arch5)
- def_builtin ("__builtin_arm_clz", int_ftype_int, ARM_BUILTIN_CLZ);
-}
-
-/* Expand an expression EXP that calls a built-in function,
- with result going to TARGET if that's convenient
- (and in mode MODE if that's convenient).
- SUBTARGET may be used as the target for computing one of EXP's operands.
- IGNORE is nonzero if the value is to be ignored. */
-
-rtx
-arm_expand_builtin (exp, target, subtarget, mode, ignore)
- tree exp;
- rtx target;
- rtx subtarget ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- int ignore ATTRIBUTE_UNUSED;
-{
- enum insn_code icode;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
- tree arglist = TREE_OPERAND (exp, 1);
- tree arg0;
- rtx op0, pat;
- enum machine_mode tmode, mode0;
- int fcode = DECL_FUNCTION_CODE (fndecl);
-
- switch (fcode)
- {
- default:
- break;
-
- case ARM_BUILTIN_CLZ:
- icode = CODE_FOR_clz;
- arg0 = TREE_VALUE (arglist);
- op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
- tmode = insn_data[icode].operand[0].mode;
- mode0 = insn_data[icode].operand[1].mode;
-
- if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
- op0 = copy_to_mode_reg (mode0, op0);
- if (target == 0
- || GET_MODE (target) != tmode
- || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
- target = gen_reg_rtx (tmode);
- pat = GEN_FCN (icode) (target, op0);
- if (! pat)
- return 0;
- emit_insn (pat);
- return target;
- }
-
- /* @@@ Should really do something sensible here. */
- return NULL_RTX;
-}
\f
/* Recursively search through all of the blocks in a function
checking to see if any of the variables created in that
#define STORE_FLAG_VALUE 1
+/* The arm5 clz instruction returns 32. */
+#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 32, 1)
\f
/* Gcc puts the pool in the wrong place for ARM, since we can only
#define SPECIAL_MODE_PREDICATES \
"cc_register", "dominant_cc_register",
-enum arm_builtins
-{
- ARM_BUILTIN_CLZ,
- ARM_BUILTIN_MAX
-};
#endif /* ! GCC_ARM_H */
; value to it before trying to dereference it.
(UNSPEC_PRLG_STK 4) ; A special barrier that prevents frame accesses
; being scheduled before the stack adjustment insn.
- (UNSPEC_CLZ 5) ; `clz' instruction, count leading zeros (SImode):
- ; operand 0 is the result,
- ; operand 1 is the parameter.
(UNSPEC_PROLOGUE_USE 6) ; As USE insns are not meaningful after reload,
; this unspec is used to prevent the deletion of
; instructions setting registers for EH handling
;; V5 Instructions,
-(define_insn "clz"
- [(set (match_operand:SI 0 "s_register_operand" "=r")
- (unspec:SI [(match_operand:SI 1 "s_register_operand" "r")]
- UNSPEC_CLZ))]
+(define_insn "clzsi2"
+ [(set (match_operand:SI 0 "s_register_operand" "=r")
+ (clz:SI (match_operand:SI 1 "s_register_operand" "r")))]
"TARGET_ARM && arm_arch5"
"clz\\t%0, %1")
emit_insn (gen_negsi2 (t1, operands[1]));
emit_insn (gen_andsi3 (t2, operands[1], t1));
- emit_insn (gen_clz (t3, t2));
+ emit_insn (gen_clzsi2 (t3, t2));
emit_insn (gen_subsi3 (operands[0], GEN_INT (32), t3));
DONE;
}"
)
+(define_expand "ctzsi2"
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (ctz:SI (match_operand:SI 1 "s_register_operand" "")))]
+ "TARGET_ARM && arm_arch5"
+ "
+ {
+ rtx t1, t2, t3;
+
+ t1 = gen_reg_rtx (SImode);
+ t2 = gen_reg_rtx (SImode);
+ t3 = gen_reg_rtx (SImode);
+
+ emit_insn (gen_negsi2 (t1, operands[1]));
+ emit_insn (gen_andsi3 (t2, operands[1], t1));
+ emit_insn (gen_clzsi2 (t3, t2));
+ emit_insn (gen_subsi3 (operands[0], GEN_INT (31), t3));
+ DONE;
+ }"
+)
+
;; V5E instructions.
(define_insn "prefetch"
#define TARGET_C99_FUNCTIONS 0
#endif
+/* Indicate that CLZ and CTZ are undefined at zero. */
+#ifndef CLZ_DEFINED_VALUE_AT_ZERO
+#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) 0
+#endif
+#ifndef CTZ_DEFINED_VALUE_AT_ZERO
+#define CTZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) 0
+#endif
+
#endif /* ! GCC_DEFAULTS_H */
@item (clz:@var{m} @var{x})
Represents the number of leading 0-bits in @var{x}, represented as an
integer of mode @var{m}, starting at the most significant bit position.
-If @var{x} is zero, the value is undefined. Note that this is one of
+If @var{x} is zero, the value is determined by
+@code{CLZ_DEFINED_VALUE_AT_ZERO}. Note that this is one of
the few expressions that is not invariant under widening. The mode of
@var{x} will usually be an integer mode.
@item (ctz:@var{m} @var{x})
Represents the number of trailing 0-bits in @var{x}, represented as an
integer of mode @var{m}, starting at the least significant bit position.
-If @var{x} is zero, the value is undefined. Except for this case,
+If @var{x} is zero, the value is determined by
+@code{CTZ_DEFINED_VALUE_AT_ZERO}. Except for this case,
@code{ctz(x)} is equivalent to @code{ffs(@var{x}) - 1}. The mode of
@var{x} will usually be an integer mode.
floating-point values. If there are no such operations, do not define
this macro.
+@findex CLZ_DEFINED_VALUE_AT_ZERO
+@findex CTZ_DEFINED_VALUE_AT_ZERO
+@item CLZ_DEFINED_VALUE_AT_ZERO (@var{mode}, @var{value})
+@itemx CTZ_DEFINED_VALUE_AT_ZERO (@var{mode}, @var{value})
+A C expression that evaluates to true if the architecture defines a value
+for @code{clz} or @code{ctz} with a zero operand. If so, @var{value}
+should be set to this value. If this macro is not defined, the value of
+@code{clz} or @code{ctz} is assumed to be undefined.
+
+This macro must be defined if the target's expansion for @code{ffs}
+relies on a particular value to get correct results. Otherwise it
+is not necessary, though it may be used to optimize some corner cases.
+
+Note that regardless of this macro the ``definedness'' of @code{clz}
+and @code{ctz} at zero do @emph{not} extend to the builtin functions
+visible to the user. Thus one may be free to adjust the value at will
+to match the target expansion of these operations without fear of
+breaking the API.
+
@findex Pmode
@item Pmode
An alias for the machine mode for pointers. On most machines, define
{
case ABS_EXPR:
case FFS_EXPR:
- case CLZ_EXPR:
- case CTZ_EXPR:
case POPCOUNT_EXPR:
case PARITY_EXPR:
return 1;
+
+ case CLZ_EXPR:
+ case CTZ_EXPR:
+ /* These are undefined at zero. This is true even if
+ C[LT]Z_DEFINED_VALUE_AT_ZERO is set, since what we're
+ computing here is a user-visible property. */
+ return 0;
+
case INTEGER_CST:
return tree_int_cst_sgn (t) >= 0;
case TRUNC_DIV_EXPR:
case CLZ:
arg0 &= GET_MODE_MASK (mode);
- val = GET_MODE_BITSIZE (mode) - floor_log2 (arg0) - 1;
+ if (arg0 == 0 && CLZ_DEFINED_VALUE_AT_ZERO (mode, val))
+ ;
+ else
+ val = GET_MODE_BITSIZE (mode) - floor_log2 (arg0) - 1;
break;
case CTZ:
arg0 &= GET_MODE_MASK (mode);
- val = arg0 == 0
- ? GET_MODE_BITSIZE (mode)
- : exact_log2 (arg0 & -arg0);
+ if (arg0 == 0)
+ {
+ /* Even if the value at zero is undefined, we have to come
+ up with some replacement. Seems good enough. */
+ if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, val))
+ val = GET_MODE_BITSIZE (mode);
+ }
+ else
+ val = exact_log2 (arg0 & -arg0);
break;
case POPCOUNT: