This adds an argument to direct_internal_fn_supported_p that says whether the function is going to be used in code optimised for size or speed. It also has a "both" option for cases where we want code that is optimised for both size and speed.
At the moment the match-and-simplify code doesn't have direct access to the target block, so for now I've used "both" there. Tested as described in the covering note. gcc/ * coretypes.h (optimization_type): New enum. * predict.h (function_optimization_type): Declare. (bb_optimization_type): Likewise. * predict.c (function_optimization_type): New function. (bb_optimization_type): Likewise. * optabs-query.h (optab_supported_p): Declare. * optabs-query.c (optab_supported_p): New function. * internal-fn.h (direct_internal_fn_supported_p): Take an optimization_type argument. * internal-fn.c (direct_optab_supported_p): Likewise. (multi_vector_optab_supported_p): Likewise. (direct_internal_fn_supported_p): Likewise. * builtins.c (replacement_internal_fn): Update call to direct_internal_fn_supported_p. * gimple-match-head.c (build_call_internal): Likewise. * tree-vect-patterns.c (vect_recog_pow_pattern): Likewise. * tree-vect-stmts.c (vectorizable_internal_function): Likewise. * tree.c (maybe_build_call_expr_loc): Likewise. diff --git a/gcc/builtins.c b/gcc/builtins.c index df5c493..7c614e6 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -1962,7 +1962,8 @@ replacement_internal_fn (gcall *call) if (ifn != IFN_LAST) { tree_pair types = direct_internal_fn_types (ifn, call); - if (direct_internal_fn_supported_p (ifn, types)) + optimization_type opt_type = bb_optimization_type (gimple_bb (call)); + if (direct_internal_fn_supported_p (ifn, types, opt_type)) return ifn; } } diff --git a/gcc/coretypes.h b/gcc/coretypes.h index d4a75db..5a9fb63 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -200,6 +200,18 @@ enum node_frequency { NODE_FREQUENCY_HOT }; +/* Ways of optimizing code. */ +enum optimization_type { + /* Prioritize speed over size. */ + OPTIMIZE_FOR_SPEED, + + /* Only do things that are good for both size and speed. */ + OPTIMIZE_FOR_BOTH, + + /* Prioritize size over speed. */ + OPTIMIZE_FOR_SIZE +}; + /* Possible initialization status of a variable. When requested by the user, this information is tracked and recorded in the DWARF debug information, along with the variable's location. */ diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c index bdc1f98..049ebab 100644 --- a/gcc/gimple-match-head.c +++ b/gcc/gimple-match-head.c @@ -261,7 +261,7 @@ build_call_internal (internal_fn fn, tree type, unsigned int nargs, tree *ops) if (direct_internal_fn_p (fn)) { tree_pair types = direct_internal_fn_types (fn, type, ops); - if (!direct_internal_fn_supported_p (fn, types)) + if (!direct_internal_fn_supported_p (fn, types, OPTIMIZE_FOR_BOTH)) return NULL; } return gimple_build_call_internal (fn, nargs, ops[0], ops[1], ops[2]); diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index b15657f..c6d2c9a 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -2214,23 +2214,28 @@ direct_internal_fn_types (internal_fn fn, gcall *call) } /* Return true if OPTAB is supported for TYPES (whose modes should be - the same). Used for simple direct optabs. */ + the same) when the optimization type is OPT_TYPE. Used for simple + direct optabs. */ static bool -direct_optab_supported_p (direct_optab optab, tree_pair types) +direct_optab_supported_p (direct_optab optab, tree_pair types, + optimization_type opt_type) { machine_mode mode = TYPE_MODE (types.first); gcc_checking_assert (mode == TYPE_MODE (types.second)); - return direct_optab_handler (optab, mode) != CODE_FOR_nothing; + insn_code icode = direct_optab_handler (optab, mode); + return optab_supported_p (icode, opt_type); } /* Return true if load/store lanes optab OPTAB is supported for - array type TYPES.first. */ + array type TYPES.first when the optimization type is OPT_TYPE. */ static bool -multi_vector_optab_supported_p (convert_optab optab, tree_pair types) +multi_vector_optab_supported_p (convert_optab optab, tree_pair types, + optimization_type opt_type) { - return get_multi_vector_move (types.first, optab) != CODE_FOR_nothing; + insn_code icode = get_multi_vector_move (types.first, optab); + return optab_supported_p (icode, opt_type); } #define direct_unary_optab_supported_p direct_optab_supported_p @@ -2240,12 +2245,14 @@ multi_vector_optab_supported_p (convert_optab optab, tree_pair types) #define direct_mask_store_optab_supported_p direct_optab_supported_p #define direct_store_lanes_optab_supported_p multi_vector_optab_supported_p -/* Return true if FN is supported for the types in TYPES. The types - are those associated with the "type0" and "type1" fields of FN's - direct_internal_fn_info structure. */ +/* Return true if FN is supported for the types in TYPES when the + optimization type is OPT_TYPE. The types are those associated with + the "type0" and "type1" fields of FN's direct_internal_fn_info + structure. */ bool -direct_internal_fn_supported_p (internal_fn fn, tree_pair types) +direct_internal_fn_supported_p (internal_fn fn, tree_pair types, + optimization_type opt_type) { switch (fn) { @@ -2253,7 +2260,8 @@ direct_internal_fn_supported_p (internal_fn fn, tree_pair types) case IFN_##CODE: break; #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \ case IFN_##CODE: \ - return direct_##TYPE##_optab_supported_p (OPTAB##_optab, types); + return direct_##TYPE##_optab_supported_p (OPTAB##_optab, types, \ + opt_type); #include "internal-fn.def" case IFN_LAST: @@ -2262,16 +2270,17 @@ direct_internal_fn_supported_p (internal_fn fn, tree_pair types) gcc_unreachable (); } -/* Return true if FN is supported for type TYPE. The caller knows that - the "type0" and "type1" fields of FN's direct_internal_fn_info - structure are the same. */ +/* Return true if FN is supported for type TYPE when the optimization + type is OPT_TYPE. The caller knows that the "type0" and "type1" + fields of FN's direct_internal_fn_info structure are the same. */ bool -direct_internal_fn_supported_p (internal_fn fn, tree type) +direct_internal_fn_supported_p (internal_fn fn, tree type, + optimization_type opt_type) { const direct_internal_fn_info &info = direct_internal_fn (fn); gcc_checking_assert (info.type0 == info.type1); - return direct_internal_fn_supported_p (fn, tree_pair (type, type)); + return direct_internal_fn_supported_p (fn, tree_pair (type, type), opt_type); } /* Return true if IFN_SET_EDOM is supported. */ diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h index aea6abd..ef27f09 100644 --- a/gcc/internal-fn.h +++ b/gcc/internal-fn.h @@ -166,8 +166,10 @@ direct_internal_fn (internal_fn fn) extern tree_pair direct_internal_fn_types (internal_fn, tree, tree *); extern tree_pair direct_internal_fn_types (internal_fn, gcall *); -extern bool direct_internal_fn_supported_p (internal_fn, tree_pair); -extern bool direct_internal_fn_supported_p (internal_fn, tree); +extern bool direct_internal_fn_supported_p (internal_fn, tree_pair, + optimization_type); +extern bool direct_internal_fn_supported_p (internal_fn, tree, + optimization_type); extern bool set_edom_supported_p (void); extern void expand_internal_call (gcall *); diff --git a/gcc/optabs-query.c b/gcc/optabs-query.c index c20597c..a059aa2 100644 --- a/gcc/optabs-query.c +++ b/gcc/optabs-query.c @@ -35,6 +35,36 @@ struct target_optabs *this_fn_optabs = &default_target_optabs; struct target_optabs *this_target_optabs = &default_target_optabs; #endif +/* Return true if ICODE is a valid instruction number and the instruction + is supported for optimization type OPT_TYPE. We allow CODE_FOR_nothing + to be passed so that callers can use this function instead of, rather + than as well as, an explicit check for CODE_nothing. */ + +bool +optab_supported_p (insn_code icode, optimization_type opt_type) +{ + if (icode == CODE_FOR_nothing) + return false; + + switch (opt_type) + { + case OPTIMIZE_FOR_SPEED: + return (get_bool_attr_mask (icode, BA_PREFERRED_FOR_SPEED) + == get_bool_attr_mask (icode, BA_ENABLED)); + + case OPTIMIZE_FOR_SIZE: + return (get_bool_attr_mask (icode, BA_PREFERRED_FOR_SIZE) + == get_bool_attr_mask (icode, BA_ENABLED)); + + case OPTIMIZE_FOR_BOTH: + /* Require the choice to be good for both size and speed. */ + return ((get_bool_attr_mask (icode, BA_PREFERRED_FOR_SIZE) + & get_bool_attr_mask (icode, BA_PREFERRED_FOR_SPEED)) + == get_bool_attr_mask (icode, BA_ENABLED)); + } + gcc_unreachable (); +} + /* Enumerates the possible types of structure operand to an extraction_insn. */ enum extraction_type { ET_unaligned_mem, ET_reg }; diff --git a/gcc/optabs-query.h b/gcc/optabs-query.h index 48bcf7c..1898b57 100644 --- a/gcc/optabs-query.h +++ b/gcc/optabs-query.h @@ -136,6 +136,7 @@ bool get_best_mem_extraction_insn (extraction_insn *, enum extraction_pattern, HOST_WIDE_INT, HOST_WIDE_INT, machine_mode); +bool optab_supported_p (insn_code, optimization_type); enum insn_code can_extend_p (machine_mode, machine_mode, int); enum insn_code can_float_p (machine_mode, machine_mode, int); enum insn_code can_fix_p (machine_mode, machine_mode, int, bool *); diff --git a/gcc/predict.c b/gcc/predict.c index 7e0f848..d5c40de 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -269,6 +269,16 @@ optimize_function_for_speed_p (struct function *fun) return !optimize_function_for_size_p (fun); } +/* Return the optimization type that should be used for the function FUN. */ + +optimization_type +function_optimization_type (struct function *fun) +{ + return (optimize_function_for_speed_p (fun) + ? OPTIMIZE_FOR_SPEED + : OPTIMIZE_FOR_SIZE); +} + /* Return TRUE when BB should be optimized for size. */ bool @@ -286,6 +296,16 @@ optimize_bb_for_speed_p (const_basic_block bb) return !optimize_bb_for_size_p (bb); } +/* Return the optimization type that should be used for block BB. */ + +optimization_type +bb_optimization_type (const_basic_block bb) +{ + return (optimize_bb_for_speed_p (bb) + ? OPTIMIZE_FOR_SPEED + : OPTIMIZE_FOR_SIZE); +} + /* Return TRUE when BB should be optimized for size. */ bool diff --git a/gcc/predict.h b/gcc/predict.h index 85150fe..486ce17 100644 --- a/gcc/predict.h +++ b/gcc/predict.h @@ -54,8 +54,10 @@ extern bool probably_never_executed_bb_p (struct function *, const_basic_block); extern bool probably_never_executed_edge_p (struct function *, edge); extern bool optimize_function_for_size_p (struct function *); extern bool optimize_function_for_speed_p (struct function *); +extern optimization_type function_optimization_type (struct function *); extern bool optimize_bb_for_size_p (const_basic_block); extern bool optimize_bb_for_speed_p (const_basic_block); +extern optimization_type bb_optimization_type (const_basic_block); extern bool optimize_edge_for_size_p (edge); extern bool optimize_edge_for_speed_p (edge); extern bool optimize_insn_for_size_p (void); diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c index 5bab1f5..cd142e1 100644 --- a/gcc/tree-vect-patterns.c +++ b/gcc/tree-vect-patterns.c @@ -1056,7 +1056,9 @@ vect_recog_pow_pattern (vec<gimple *> *stmts, tree *type_in, && real_equal (&TREE_REAL_CST (exp), &dconsthalf)) { *type_in = get_vectype_for_scalar_type (TREE_TYPE (base)); - if (*type_in && direct_internal_fn_supported_p (IFN_SQRT, *type_in)) + if (*type_in + && direct_internal_fn_supported_p (IFN_SQRT, *type_in, + OPTIMIZE_FOR_SPEED)) { gcall *stmt = gimple_build_call_internal (IFN_SQRT, 1, base); var = vect_recog_temp_ssa_var (TREE_TYPE (base), stmt); diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c index 687f982..fe6f44b 100644 --- a/gcc/tree-vect-stmts.c +++ b/gcc/tree-vect-stmts.c @@ -1663,7 +1663,8 @@ vectorizable_internal_function (combined_fn cfn, tree fndecl, { tree type0 = (info.type0 < 0 ? vectype_out : vectype_in); tree type1 = (info.type1 < 0 ? vectype_out : vectype_in); - if (direct_internal_fn_supported_p (ifn, tree_pair (type0, type1))) + if (direct_internal_fn_supported_p (ifn, tree_pair (type0, type1), + OPTIMIZE_FOR_SPEED)) return ifn; } } diff --git a/gcc/tree.c b/gcc/tree.c index 779fe93..e367030 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -11124,7 +11124,8 @@ maybe_build_call_expr_loc (location_t loc, combined_fn fn, tree type, if (direct_internal_fn_p (ifn)) { tree_pair types = direct_internal_fn_types (ifn, type, argarray); - if (!direct_internal_fn_supported_p (ifn, types)) + if (!direct_internal_fn_supported_p (ifn, types, + OPTIMIZE_FOR_BOTH)) return NULL_TREE; } return build_call_expr_internal_loc_array (loc, ifn, type, n, argarray);