tree.cc: Add tree_builtin_call_types_compatible_p [PR105150]

And here is the follow-up patch that does the argument checking
on GENERIC.  It ensures TYPE_MAIN_VARIANT == TYPE_MAIN_VARIANT
compatibility on the arguments, except for pointer arguments
where both builtin's prototype and actual arguments have to be
pointers and satisfy tree_nop_conversion_p, and for promoted
char/short arguments where argument need to have integral
signed type tree_nop_conversion_p compatible with integer_type_node.

2022-04-07  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/105150
	* tree.cc (tree_builtin_call_types_compatible_p): New function.
	(get_call_combined_fn): Use it.

	* gcc.dg/pr105150.c: New test.
This commit is contained in:
Jakub Jelinek 2022-04-07 09:10:58 +02:00
parent 54ed6563d2
commit 973a2ce71f
2 changed files with 64 additions and 1 deletions

View File

@ -0,0 +1,8 @@
/* PR tree-optimization/105150 */
/* { dg-options "-w -Ofast" } */
#define A(name) __typeof (__builtin_##name (0)) name (); \
float name##1 () { return !name (1); } \
double name##2 () { return name (1.0L); }
#define B(name) A(name) A(name##l)
B (sqrt)

View File

@ -8406,6 +8406,59 @@ get_callee_fndecl (const_tree call)
return NULL_TREE;
}
/* Return true when STMTs arguments and return value match those of FNDECL,
a decl of a builtin function. */
static bool
tree_builtin_call_types_compatible_p (const_tree call, tree fndecl)
{
gcc_checking_assert (DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN);
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
if (tree decl = builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)))
fndecl = decl;
if (TYPE_MAIN_VARIANT (TREE_TYPE (call))
!= TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))))
return false;
tree targs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
unsigned nargs = call_expr_nargs (call);
for (unsigned i = 0; i < nargs; ++i, targs = TREE_CHAIN (targs))
{
/* Variadic args follow. */
if (!targs)
return true;
tree arg = CALL_EXPR_ARG (call, i);
tree type = TREE_VALUE (targs);
if (TYPE_MAIN_VARIANT (type) != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
{
/* For pointer arguments be more forgiving, e.g. due to
FILE * vs. fileptr_type_node, or say char * vs. const char *
differences etc. */
if (POINTER_TYPE_P (type)
&& POINTER_TYPE_P (TREE_TYPE (arg))
&& tree_nop_conversion_p (type, TREE_TYPE (arg)))
continue;
/* char/short integral arguments are promoted to int
by several frontends if targetm.calls.promote_prototypes
is true. Allow such promotion too. */
if (INTEGRAL_TYPE_P (type)
&& TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)
&& INTEGRAL_TYPE_P (TREE_TYPE (arg))
&& !TYPE_UNSIGNED (TREE_TYPE (arg))
&& targetm.calls.promote_prototypes (TREE_TYPE (fndecl))
&& tree_nop_conversion_p (integer_type_node,
TREE_TYPE (arg)))
continue;
return false;
}
}
if (targs && !VOID_TYPE_P (TREE_VALUE (targs)))
return false;
return true;
}
/* If CALL_EXPR CALL calls a normal built-in function or an internal function,
return the associated function code, otherwise return CFN_LAST. */
@ -8419,7 +8472,9 @@ get_call_combined_fn (const_tree call)
return as_combined_fn (CALL_EXPR_IFN (call));
tree fndecl = get_callee_fndecl (call);
if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
if (fndecl
&& fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
&& tree_builtin_call_types_compatible_p (call, fndecl))
return as_combined_fn (DECL_FUNCTION_CODE (fndecl));
return CFN_LAST;