diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 75ee0503786..0615327dd8c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,9 +1,29 @@ +2001-08-21 Zack Weinberg + + * stmt.c: Don't include insn-codes.h. + (expand_end_case): Machine specific logic moved to expr.c. + No need to worry about __builtin_classify_type. + (check_for_full_enumeration_handling, emit_case_nodes): + Kill #if 0 blocks. + + * builtins.o (expand_builtin_classify_type): Split up so code + can be shared with fold_builtin_classify_type. + (type_to_class, fold_builtin_classify_type): New functions. + (fold_builtins): Handle __builtin_classify_type. + + * expr.c (do_tablejump): Now static. + (case_values_threshold, try_casesi, try_tablejump): New; + code mostly from stmt.c (expand_end_case). + (expr.h): Update prototypes. + + * Makefile.in (stmt.o): Update dependencies. + 2001-08-21 Will Cohen - * configure/alpha/alpha.h (CONDITIONAL_REGISTER_USAGE): Added local - declaration of variable i. - * configure/rs6000/rs6000.h (CONDITIONAL_REGISTER_USAGE): Added local - declaration of variable i. + * configure/alpha/alpha.h (CONDITIONAL_REGISTER_USAGE): Added local + declaration of variable i. + * configure/rs6000/rs6000.h (CONDITIONAL_REGISTER_USAGE): Added local + declaration of variable i. 2001-08-21 Richard Henderson diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a5253e53926..478a10c6d2f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1383,7 +1383,7 @@ function.o : function.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ function.h insn-codes.h $(EXPR_H) libfuncs.h $(REGS_H) hard-reg-set.h \ insn-config.h $(RECOG_H) output.h toplev.h except.h hash.h $(GGC_H) $(TM_P_H) stmt.o : stmt.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h function.h \ - insn-config.h insn-codes.h hard-reg-set.h $(EXPR_H) libfuncs.h except.h \ + insn-config.h hard-reg-set.h $(EXPR_H) libfuncs.h except.h \ $(LOOP_H) $(RECOG_H) toplev.h output.h varray.h $(GGC_H) $(TM_P_H) except.o : except.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ except.h function.h $(EXPR_H) libfuncs.h integrate.h \ diff --git a/gcc/builtins.c b/gcc/builtins.c index b2f4a6bf737..2d92e007258 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -91,6 +91,7 @@ static rtx expand_builtin_apply_args PARAMS ((void)); static rtx expand_builtin_apply_args_1 PARAMS ((void)); static rtx expand_builtin_apply PARAMS ((rtx, rtx, rtx)); static void expand_builtin_return PARAMS ((rtx)); +static int type_to_class PARAMS ((tree)); static rtx expand_builtin_classify_type PARAMS ((tree)); static rtx expand_builtin_mathfn PARAMS ((tree, rtx, rtx)); static rtx expand_builtin_constant_p PARAMS ((tree)); @@ -142,6 +143,7 @@ static rtx expand_builtin_fputs PARAMS ((tree, int)); static tree stabilize_va_list PARAMS ((tree, int)); static rtx expand_builtin_expect PARAMS ((tree, rtx)); static tree fold_builtin_constant_p PARAMS ((tree)); +static tree fold_builtin_classify_type PARAMS ((tree)); static tree build_function_call_expr PARAMS ((tree, tree)); static int validate_arglist PARAMS ((tree, ...)); @@ -1267,6 +1269,37 @@ expand_builtin_return (result) expand_null_return (); } +/* Used by expand_builtin_classify_type and fold_builtin_classify_type. */ +static enum type_class +type_to_class (type) + tree type; +{ + switch (TREE_CODE (type)) + { + case VOID_TYPE: return void_type_class; + case INTEGER_TYPE: return integer_type_class; + case CHAR_TYPE: return char_type_class; + case ENUMERAL_TYPE: return enumeral_type_class; + case BOOLEAN_TYPE: return boolean_type_class; + case POINTER_TYPE: return pointer_type_class; + case REFERENCE_TYPE: return reference_type_class; + case OFFSET_TYPE: return offset_type_class; + case REAL_TYPE: return real_type_class; + case COMPLEX_TYPE: return complex_type_class; + case FUNCTION_TYPE: return function_type_class; + case METHOD_TYPE: return method_type_class; + case RECORD_TYPE: return record_type_class; + case UNION_TYPE: + case QUAL_UNION_TYPE: return union_type_class; + case ARRAY_TYPE: return (TYPE_STRING_FLAG (type) + ? string_type_class : array_type_class); + case SET_TYPE: return set_type_class; + case FILE_TYPE: return file_type_class; + case LANG_TYPE: return lang_type_class; + default: return no_type_class; + } +} + /* Expand a call to __builtin_classify_type with arguments found in ARGLIST. */ static rtx @@ -1274,51 +1307,7 @@ expand_builtin_classify_type (arglist) tree arglist; { if (arglist != 0) - { - tree type = TREE_TYPE (TREE_VALUE (arglist)); - enum tree_code code = TREE_CODE (type); - if (code == VOID_TYPE) - return GEN_INT (void_type_class); - if (code == INTEGER_TYPE) - return GEN_INT (integer_type_class); - if (code == CHAR_TYPE) - return GEN_INT (char_type_class); - if (code == ENUMERAL_TYPE) - return GEN_INT (enumeral_type_class); - if (code == BOOLEAN_TYPE) - return GEN_INT (boolean_type_class); - if (code == POINTER_TYPE) - return GEN_INT (pointer_type_class); - if (code == REFERENCE_TYPE) - return GEN_INT (reference_type_class); - if (code == OFFSET_TYPE) - return GEN_INT (offset_type_class); - if (code == REAL_TYPE) - return GEN_INT (real_type_class); - if (code == COMPLEX_TYPE) - return GEN_INT (complex_type_class); - if (code == FUNCTION_TYPE) - return GEN_INT (function_type_class); - if (code == METHOD_TYPE) - return GEN_INT (method_type_class); - if (code == RECORD_TYPE) - return GEN_INT (record_type_class); - if (code == UNION_TYPE || code == QUAL_UNION_TYPE) - return GEN_INT (union_type_class); - if (code == ARRAY_TYPE) - { - if (TYPE_STRING_FLAG (type)) - return GEN_INT (string_type_class); - else - return GEN_INT (array_type_class); - } - if (code == SET_TYPE) - return GEN_INT (set_type_class); - if (code == FILE_TYPE) - return GEN_INT (file_type_class); - if (code == LANG_TYPE) - return GEN_INT (lang_type_class); - } + return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist)))); return GEN_INT (no_type_class); } @@ -3806,6 +3795,17 @@ fold_builtin_constant_p (arglist) return 0; } +/* Fold a call to __builtin_classify_type. */ +static tree +fold_builtin_classify_type (arglist) + tree arglist; +{ + if (arglist == 0) + return build_int_2 (no_type_class, 0); + + return build_int_2 (type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0); +} + /* Used by constant folding to eliminate some builtin calls early. EXP is the CALL_EXPR of a call to a builtin function. */ @@ -3825,6 +3825,9 @@ fold_builtin (exp) case BUILT_IN_CONSTANT_P: return fold_builtin_constant_p (arglist); + case BUILT_IN_CLASSIFY_TYPE: + return fold_builtin_classify_type (arglist); + case BUILT_IN_STRLEN: if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) { diff --git a/gcc/expr.c b/gcc/expr.c index ed7f33fcf1f..ffb46cb59c1 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -177,6 +177,7 @@ static rtx do_store_flag PARAMS ((tree, rtx, enum machine_mode, int)); #ifdef PUSH_ROUNDING static void emit_single_push_insn PARAMS ((enum machine_mode, rtx, tree)); #endif +static void do_tablejump PARAMS ((rtx, enum machine_mode, rtx, rtx, rtx)); /* Record for each mode whether we can move a register directly to or from an object of that mode in memory. If we can't, we won't try @@ -10717,11 +10718,112 @@ do_store_flag (exp, target, mode, only_cheap) return target; } -/* Generate a tablejump instruction (used for switch statements). */ -#ifdef HAVE_tablejump +/* Stubs in case we haven't got a casesi insn. */ +#ifndef HAVE_casesi +# define HAVE_casesi 0 +# define gen_casesi(a, b, c, d, e) (0) +# define CODE_FOR_casesi CODE_FOR_nothing +#endif -/* INDEX is the value being switched on, with the lowest value +/* If the machine does not have a case insn that compares the bounds, + this means extra overhead for dispatch tables, which raises the + threshold for using them. */ +#ifndef CASE_VALUES_THRESHOLD +#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5) +#endif /* CASE_VALUES_THRESHOLD */ + +unsigned int +case_values_threshold () +{ + return CASE_VALUES_THRESHOLD; +} + +/* Attempt to generate a casesi instruction. Returns 1 if successful, + 0 otherwise (i.e. if there is no casesi instruction). */ +int +try_casesi (index_type, index_expr, minval, range, + table_label, default_label) + tree index_type, index_expr, minval, range; + rtx table_label ATTRIBUTE_UNUSED; + rtx default_label; +{ + enum machine_mode index_mode = SImode; + int index_bits = GET_MODE_BITSIZE (index_mode); + rtx op1, op2, index; + enum machine_mode op_mode; + + if (! HAVE_casesi) + return 0; + + /* Convert the index to SImode. */ + if (GET_MODE_BITSIZE (TYPE_MODE (index_type)) > GET_MODE_BITSIZE (index_mode)) + { + enum machine_mode omode = TYPE_MODE (index_type); + rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0); + + /* We must handle the endpoints in the original mode. */ + index_expr = build (MINUS_EXPR, index_type, + index_expr, minval); + minval = integer_zero_node; + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + emit_cmp_and_jump_insns (rangertx, index, LTU, NULL_RTX, + omode, 1, 0, default_label); + /* Now we can safely truncate. */ + index = convert_to_mode (index_mode, index, 0); + } + else + { + if (TYPE_MODE (index_type) != index_mode) + { + index_expr = convert (type_for_size (index_bits, 0), + index_expr); + index_type = TREE_TYPE (index_expr); + } + + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + } + emit_queue (); + index = protect_from_queue (index, 0); + do_pending_stack_adjust (); + + op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode; + if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate) + (index, op_mode)) + index = copy_to_mode_reg (op_mode, index); + + op1 = expand_expr (minval, NULL_RTX, VOIDmode, 0); + + op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode; + op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)), + op1, TREE_UNSIGNED (TREE_TYPE (minval))); + if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate) + (op1, op_mode)) + op1 = copy_to_mode_reg (op_mode, op1); + + op2 = expand_expr (range, NULL_RTX, VOIDmode, 0); + + op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode; + op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)), + op2, TREE_UNSIGNED (TREE_TYPE (range))); + if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate) + (op2, op_mode)) + op2 = copy_to_mode_reg (op_mode, op2); + + emit_jump_insn (gen_casesi (index, op1, op2, + table_label, default_label)); + return 1; +} + +/* Attempt to generate a tablejump instruction; same concept. */ +#ifndef HAVE_tablejump +#define HAVE_tablejump 0 +#define gen_tablejump(x, y) (0) +#endif + +/* Subroutine of the next function. + + INDEX is the value being switched on, with the lowest value in the table already subtracted. MODE is its expected mode (needed if INDEX is constant). RANGE is the length of the jump table. @@ -10730,7 +10832,7 @@ do_store_flag (exp, target, mode, only_cheap) DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the index value is out of range. */ -void +static void do_tablejump (index, mode, range, table_label, default_label) rtx index, range, table_label, default_label; enum machine_mode mode; @@ -10792,4 +10894,31 @@ do_tablejump (index, mode, range, table_label, default_label) emit_barrier (); } -#endif /* HAVE_tablejump */ +int +try_tablejump (index_type, index_expr, minval, range, + table_label, default_label) + tree index_type, index_expr, minval, range; + rtx table_label, default_label; +{ + rtx index; + + if (! HAVE_tablejump) + return 0; + + index_expr = fold (build (MINUS_EXPR, index_type, + convert (index_type, index_expr), + convert (index_type, minval))); + index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); + emit_queue (); + index = protect_from_queue (index, 0); + do_pending_stack_adjust (); + + do_tablejump (index, TYPE_MODE (index_type), + convert_modes (TYPE_MODE (index_type), + TYPE_MODE (TREE_TYPE (range)), + expand_expr (range, NULL_RTX, + VOIDmode, 0), + TREE_UNSIGNED (TREE_TYPE (range))), + table_label, default_label); + return 1; +} diff --git a/gcc/expr.h b/gcc/expr.h index 2ea167ec3d6..22e537d62fc 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -540,8 +540,14 @@ extern void do_compare_rtx_and_jump PARAMS ((rtx, rtx, enum rtx_code, int, enum machine_mode, rtx, unsigned int, rtx, rtx)); -/* Generate a tablejump instruction (used for switch statements). */ -extern void do_tablejump PARAMS ((rtx, enum machine_mode, rtx, rtx, rtx)); +/* Two different ways of generating switch statements. */ +extern int try_casesi PARAMS ((tree, tree, tree, tree, rtx, rtx)); +extern int try_tablejump PARAMS ((tree, tree, tree, tree, rtx, rtx)); + +/* Smallest number of adjacent cases before we use a jump table. + XXX Should be a target hook. */ +extern unsigned int case_values_threshold PARAMS ((void)); + #ifdef TREE_CODE /* rtl.h and tree.h were included. */ diff --git a/gcc/stmt.c b/gcc/stmt.c index 5ef7ba90c7c..d4b4e0400cd 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -43,7 +43,6 @@ Boston, MA 02111-1307, USA. */ #include "except.h" #include "function.h" #include "insn-config.h" -#include "insn-codes.h" #include "expr.h" #include "libfuncs.h" #include "hard-reg-set.h" @@ -5021,10 +5020,6 @@ check_for_full_enumeration_handling (type) { register struct case_node *n; register tree chain; -#if 0 /* variable used by 'if 0'ed code below. */ - register struct case_node **l; - int all_values = 1; -#endif /* True iff the selector type is a numbered set mode. */ int sparseness = 0; @@ -5122,28 +5117,6 @@ check_for_full_enumeration_handling (type) } } } - -#if 0 - /* ??? This optimization is disabled because it causes valid programs to - fail. ANSI C does not guarantee that an expression with enum type - will have a value that is the same as one of the enumeration literals. */ - - /* If all values were found as case labels, make one of them the default - label. Thus, this switch will never fall through. We arbitrarily pick - the last one to make the default since this is likely the most - efficient choice. */ - - if (all_values) - { - for (l = &case_stack->data.case_stmt.case_list; - (*l)->right != 0; - l = &(*l)->right) - ; - - case_stack->data.case_stmt.default_label = (*l)->code_label; - *l = 0; - } -#endif /* 0 */ } /* Free CN, and its children. */ @@ -5161,6 +5134,7 @@ free_case_nodes (cn) } + /* Terminate a case (Pascal) or switch (C) statement in which ORIG_INDEX is the expression to be tested. Generate the code to test it and jump to the right place. */ @@ -5289,18 +5263,7 @@ expand_end_case (orig_index) If the switch-index is a constant, do it this way because we can optimize it. */ -#ifndef CASE_VALUES_THRESHOLD -#ifdef HAVE_casesi -#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5) -#else - /* If machine does not have a case insn that compares the - bounds, this means extra overhead for dispatch tables - which raises the threshold for using them. */ -#define CASE_VALUES_THRESHOLD 5 -#endif /* HAVE_casesi */ -#endif /* CASE_VALUES_THRESHOLD */ - - else if (count < CASE_VALUES_THRESHOLD + else if (count < case_values_threshold () || compare_tree_int (range, 10 * count) > 0 /* RANGE may be signed, and really large ranges will show up as negative numbers. */ @@ -5309,12 +5272,6 @@ expand_end_case (orig_index) || flag_pic #endif || TREE_CODE (index_expr) == INTEGER_CST - /* These will reduce to a constant. */ - || (TREE_CODE (index_expr) == CALL_EXPR - && TREE_CODE (TREE_OPERAND (index_expr, 0)) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == FUNCTION_DECL - && DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_NORMAL - && DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_CLASSIFY_TYPE) || (TREE_CODE (index_expr) == COMPOUND_EXPR && TREE_CODE (TREE_OPERAND (index_expr, 1)) == INTEGER_CST)) { @@ -5399,100 +5356,15 @@ expand_end_case (orig_index) } else { - int win = 0; -#ifdef HAVE_casesi - if (HAVE_casesi) - { - enum machine_mode index_mode = SImode; - int index_bits = GET_MODE_BITSIZE (index_mode); - rtx op1, op2; - enum machine_mode op_mode; - - /* Convert the index to SImode. */ - if (GET_MODE_BITSIZE (TYPE_MODE (index_type)) - > GET_MODE_BITSIZE (index_mode)) - { - enum machine_mode omode = TYPE_MODE (index_type); - rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0); - - /* We must handle the endpoints in the original mode. */ - index_expr = build (MINUS_EXPR, index_type, - index_expr, minval); - minval = integer_zero_node; - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - emit_cmp_and_jump_insns (rangertx, index, LTU, NULL_RTX, - omode, 1, 0, default_label); - /* Now we can safely truncate. */ - index = convert_to_mode (index_mode, index, 0); - } - else - { - if (TYPE_MODE (index_type) != index_mode) - { - index_expr = convert (type_for_size (index_bits, 0), - index_expr); - index_type = TREE_TYPE (index_expr); - } - - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - } - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode; - if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate) - (index, op_mode)) - index = copy_to_mode_reg (op_mode, index); - - op1 = expand_expr (minval, NULL_RTX, VOIDmode, 0); - - op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode; - op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)), - op1, TREE_UNSIGNED (TREE_TYPE (minval))); - if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate) - (op1, op_mode)) - op1 = copy_to_mode_reg (op_mode, op1); - - op2 = expand_expr (range, NULL_RTX, VOIDmode, 0); - - op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode; - op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)), - op2, TREE_UNSIGNED (TREE_TYPE (range))); - if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate) - (op2, op_mode)) - op2 = copy_to_mode_reg (op_mode, op2); - - emit_jump_insn (gen_casesi (index, op1, op2, - table_label, default_label)); - win = 1; - } -#endif -#ifdef HAVE_tablejump - if (! win && HAVE_tablejump) + if (! try_casesi (index_type, index_expr, minval, range, + table_label, default_label)) { index_type = thiscase->data.case_stmt.nominal_type; - index_expr = fold (build (MINUS_EXPR, index_type, - convert (index_type, index_expr), - convert (index_type, minval))); - index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0); - emit_queue (); - index = protect_from_queue (index, 0); - do_pending_stack_adjust (); - - do_tablejump (index, TYPE_MODE (index_type), - convert_modes (TYPE_MODE (index_type), - TYPE_MODE (TREE_TYPE (range)), - expand_expr (range, NULL_RTX, - VOIDmode, 0), - TREE_UNSIGNED (TREE_TYPE (range))), - table_label, default_label); - win = 1; + if (! try_tablejump (index_type, index_expr, minval, range, + table_label, default_label)) + abort (); } -#endif - if (! win) - abort (); - + /* Get table of labels to jump to, in order of case index. */ ncases = TREE_INT_CST_LOW (range) + 1; @@ -6133,20 +6005,6 @@ emit_case_nodes (index, node, default_label, index_type) else if (node->right == 0 && node->left != 0) { /* Just one subtree, on the left. */ - -#if 0 /* The following code and comment were formerly part - of the condition here, but they didn't work - and I don't understand what the idea was. -- rms. */ - /* If our "most probable entry" is less probable - than the default label, emit a jump to - the default label using condition codes - already lying around. With no right branch, - a branch-greater-than will get us to the default - label correctly. */ - if (use_cost_table - && COST_TABLE (TREE_INT_CST_LOW (node->high)) < 12) - ; -#endif /* 0 */ if (node->left->left || node->left->right || !tree_int_cst_equal (node->left->low, node->left->high)) {