The following exposes function pointer canonicalization for
comparisons early by lowering to an internal function call
during gimplification. This allows us to make conversions
to function pointers useless, restoring symmetry and
avoiding issues like PR127263.
I have not removed gates on few optimizations.
Function pointer canonicalizations for comparisons are only
required for 32bit hppa. I built a cc1 cross and verified
this works for gcc.dg/tree-ssa/pr22051-1.c but I have no
further means (or intent ...) of testing. Jeff, I suppose
you have some way of testing 32bit hppa for regressions?
Is this otherwise sound?
Bootstrap/regtest running on x86_64-unknown-linux-gnu (2/2
separately succeeded in that)
Thanks,
Richard.
* internal-fn.def (CANONICALIZE_FUNCPTR_FOR_COMPARE): New
internal fn.
* internal-fn.h (expand_CANONICALIZE_FUNCPTR_FOR_COMPARE):
Declare.
* internal-fn.cc (expand_CANONICALIZE_FUNCPTR_FOR_COMPARE): New.
* gimplify.cc (gimplify_expr): Insert function pointer
canonicalization for compares.
* dojump.cc (do_compare_and_jump): Remove function pointer
canonicalization.
* gcc.dg/tree-ssa/pr22051-1.c: Adjust.
---
gcc/dojump.cc | 21 ---------------------
gcc/gimplify.cc | 21 ++++++++++++++++++++-
gcc/internal-fn.cc | 12 ++++++++++++
gcc/internal-fn.def | 3 +++
gcc/internal-fn.h | 1 +
gcc/testsuite/gcc.dg/tree-ssa/pr22051-1.c | 8 ++------
6 files changed, 38 insertions(+), 28 deletions(-)
diff --git a/gcc/dojump.cc b/gcc/dojump.cc
index 1ed22116a27..298fe7bd903 100644
--- a/gcc/dojump.cc
+++ b/gcc/dojump.cc
@@ -1305,27 +1305,6 @@ do_compare_and_jump (tree treeop0, tree treeop1, enum
rtx_code signed_code,
unsignedp = TYPE_UNSIGNED (type);
code = unsignedp ? unsigned_code : signed_code;
- /* If function pointers need to be "canonicalized" before they can
- be reliably compared, then canonicalize them. Canonicalize the
- expression when one of the operands is a function pointer. This
- handles the case where the other operand is a void pointer. See
- PR middle-end/17564. */
- if (targetm.have_canonicalize_funcptr_for_compare ()
- && ((POINTER_TYPE_P (TREE_TYPE (treeop0))
- && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (treeop0))))
- || (POINTER_TYPE_P (TREE_TYPE (treeop1))
- && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (treeop1))))))
- {
- rtx new_op0 = gen_reg_rtx (mode);
- rtx new_op1 = gen_reg_rtx (mode);
-
- emit_insn (targetm.gen_canonicalize_funcptr_for_compare (new_op0, op0));
- op0 = new_op0;
-
- emit_insn (targetm.gen_canonicalize_funcptr_for_compare (new_op1, op1));
- op1 = new_op1;
- }
-
do_compare_rtx_and_jump (op0, op1, code, unsignedp, treeop0, mode,
((mode == BLKmode)
? expr_size (treeop0) : NULL_RTX),
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 0f8bf533ba7..b72195efaf1 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -21317,7 +21317,26 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p,
gimple_seq *post_p,
ret = GS_OK;
}
else
- goto expr_2;
+ {
+ if (POINTER_TYPE_P (type)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type))
+ && targetm.have_canonicalize_funcptr_for_compare
())
+ {
+ if (!integer_zerop (TREE_OPERAND (*expr_p, 0)))
+ TREE_OPERAND (*expr_p, 0)
+ = build_call_expr_internal_loc
+ (UNKNOWN_LOCATION,
+ IFN_CANONICALIZE_FUNCPTR_FOR_COMPARE,
+ type, 1, TREE_OPERAND (*expr_p, 0));
+ if (!integer_zerop (TREE_OPERAND (*expr_p, 1)))
+ TREE_OPERAND (*expr_p, 1)
+ = build_call_expr_internal_loc
+ (UNKNOWN_LOCATION,
+ IFN_CANONICALIZE_FUNCPTR_FOR_COMPARE,
+ type, 1, TREE_OPERAND (*expr_p, 1));
+ }
+ goto expr_2;
+ }
}
else if (SCALAR_INT_MODE_P (TYPE_MODE (type)))
ret = gimplify_scalar_mode_aggregate_compare (expr_p);
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index c57b594ac59..5fd6cc19515 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -5902,3 +5902,15 @@ expand_BITREVERSE (internal_fn fn, gcall *stmt)
if (expand_bitquery (fn, stmt))
expand_unary_optab_fn (fn, stmt, bitreverse_optab);
}
+
+void
+expand_CANONICALIZE_FUNCPTR_FOR_COMPARE (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ tree rhs = gimple_call_arg (stmt, 0);
+ rtx plhs = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx prhs = expand_normal (rhs);
+ rtx new_rhs = gen_reg_rtx (Pmode);
+ emit_insn (targetm.gen_canonicalize_funcptr_for_compare (new_rhs, prhs));
+ emit_move_insn (plhs, new_rhs);
+}
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index af9f92950c7..e6619f53171 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -636,6 +636,9 @@ DEF_INTERNAL_FN (SPACESHIP, ECF_CONST | ECF_LEAF |
ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ASSUME, ECF_CONST | ECF_LEAF | ECF_NOTHROW
| ECF_LOOPING_CONST_OR_PURE, NULL)
+DEF_INTERNAL_FN (CANONICALIZE_FUNCPTR_FOR_COMPARE, ECF_CONST | ECF_LEAF
+ | ECF_NOTHROW, NULL)
+
/* For if-conversion of inbranch SIMD clones. */
DEF_INTERNAL_FN (MASK_CALL, ECF_NOVOPS, NULL)
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index 30930f388b0..23e62c03183 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -288,6 +288,7 @@ extern void expand_PARITY (internal_fn, gcall *);
extern void expand_POPCOUNT (internal_fn, gcall *);
extern void expand_BSWAP (internal_fn, gcall *);
extern void expand_BITREVERSE (internal_fn, gcall *);
+extern void expand_CANONICALIZE_FUNCPTR_FOR_COMPARE (internal_fn, gcall *);
extern bool vectorized_internal_fn_supported_p (internal_fn, tree);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr22051-1.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr22051-1.c
index e01f39216c4..fc21b4ebb77 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr22051-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr22051-1.c
@@ -14,9 +14,5 @@ foo()
return 2;
}
-/* The cast to a function pointer type must remain after all optimizations
- are complete so that function pointer canonicalization works on those
- targets which require it. */
-/* { dg-final { scan-tree-dump-times "= \\(void \\(\\*<.*>\\) \\(void\\)\\)
p_" 1 "optimized" } } */
-
-
+/* A function pointer must be canonicalized on hppa. */
+/* { dg-final { scan-tree-dump-times " = .CANONICALIZE_FUNCPTR_FOR_COMPARE
\\(p_" 1 "optimized" { target hppa-*-* } } } */
--
2.51.0