diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4c27343b5630..3b9e6c0f49f0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2003-02-05 Richard Henderson + + * 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 * i386-protos.h (x86_emit_floatuns): Declare. diff --git a/gcc/combine.c b/gcc/combine.c index aeb5c2f1b98d..6566823366c9 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -8547,11 +8547,28 @@ nonzero_bits (x, mode) 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: diff --git a/gcc/config/alpha/alpha.h b/gcc/config/alpha/alpha.h index 8824f12f0f78..2c79fa8e935b 100644 --- a/gcc/config/alpha/alpha.h +++ b/gcc/config/alpha/alpha.h @@ -1493,6 +1493,10 @@ do { \ #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) \ diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 6fce0ce391a2..8923dd02452e 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -209,12 +209,6 @@ extern void arm_mark_dllexport PARAMS ((tree)); 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 *)); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index fe306fd63910..e59f461fbeaf 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -187,12 +187,6 @@ static int arm_address_cost PARAMS ((rtx)); #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 @@ -9981,82 +9975,6 @@ arm_debugger_arg_offset (value, addr) 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; -} /* Recursively search through all of the blocks in a function checking to see if any of the variables created in that diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index d389eb027def..802e19986c75 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -2146,6 +2146,8 @@ extern int making_const_table; #define STORE_FLAG_VALUE 1 +/* The arm5 clz instruction returns 32. */ +#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 32, 1) /* Gcc puts the pool in the wrong place for ARM, since we can only @@ -2456,9 +2458,4 @@ extern int making_const_table; #define SPECIAL_MODE_PREDICATES \ "cc_register", "dominant_cc_register", -enum arm_builtins -{ - ARM_BUILTIN_CLZ, - ARM_BUILTIN_MAX -}; #endif /* ! GCC_ARM_H */ diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index 001ea4ba2c27..b35041a4b74a 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -58,9 +58,6 @@ ; 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 @@ -8851,10 +8848,9 @@ ;; 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") @@ -8872,12 +8868,32 @@ 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" diff --git a/gcc/defaults.h b/gcc/defaults.h index 2dc9d749858f..ec504a3b5fa6 100644 --- a/gcc/defaults.h +++ b/gcc/defaults.h @@ -644,4 +644,12 @@ You Lose! You must define PREFERRED_DEBUGGING_TYPE! #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 */ diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi index 16985176ed22..7e696fac38b8 100644 --- a/gcc/doc/rtl.texi +++ b/gcc/doc/rtl.texi @@ -1850,7 +1850,8 @@ valid. @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. @@ -1858,7 +1859,8 @@ the few expressions that is not invariant under widening. The mode of @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. diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index b5cb2404429b..0abf394f29cd 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -8800,6 +8800,25 @@ Define this macro on machine that have comparison operations that return 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 diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 55f4ce46c41f..0e301a726902 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -7436,11 +7436,17 @@ tree_expr_nonnegative_p (t) { 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: diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index 19d664b14fb1..0298b79e8ce0 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -449,14 +449,23 @@ simplify_unary_operation (code, mode, op, op_mode) 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: