This is a long standing PR that shouldn't have languished this long.
The was a lack of clarity of what to do with VIEW_CONVERT_EXPR when it
did not meet the technical requirement of both arguments being the same
precision.
If the precision is the same, and both arguments are integral, then we
can simply treat it as a cast.. its just a reinterpretation of the bits.
We should have at least been doing that.
If the precisions are not the same, and the arguments are integral, we
can still treat it as a cast. It doesn't really matter if the upper
bits are sign or zero extended if the results are undefined. And for
truncating views, its exactly the same as a truncating cast as well.
As such, this patch provides a range_operator for VIEW_CONVERT_EXPR that
uses the cast operator for integral operands, and does not support any
other combination, such as float/doublr or int/float. If someone wants
to handle floating point, have at it :-) The class is there and waiting.
Bootstraps on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
From a144739ab9069e27f2627aa44d5951198c916a93 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Sat, 25 Oct 2025 11:59:55 -0400
Subject: [PATCH] Handle VIEW_CONVERT_EXPR for non-floats.
Handle VIEW_CONVERT_EXPR for ranges as if it were a cast.
PR tree-optimization/91191
gcc/
* gimple-range-op.cc (gimple_range_op_handler): Descend one
operand lower for a VIEW_CONVERT_EXPR.
* range-op-mixed.h (class operator_view): New.
* range-op.cc (range_op_table): Add VIEW_CONVERT_EXPR case.
(operator_view::fold_range): New.
(operator_view::op1_range): New.
(operator_view::update_bitmask): New.
gcc/testsuite/
* gcc.dg/pr91191.c: New.
---
gcc/gimple-range-op.cc | 4 ++
gcc/range-op-mixed.h | 41 ++++++++++++++++++
gcc/range-op.cc | 76 ++++++++++++++++++++++++++++++++++
gcc/testsuite/gcc.dg/pr91191.c | 20 +++++++++
4 files changed, 141 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/pr91191.c
diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
index c9bc5c0c6b9..3a22606180b 100644
--- a/gcc/gimple-range-op.cc
+++ b/gcc/gimple-range-op.cc
@@ -150,6 +150,10 @@ gimple_range_op_handler::gimple_range_op_handler (gimple *s)
if (TREE_CODE (ssa) == SSA_NAME)
m_op1 = ssa;
}
+ // VIEW_CONVERT_EXPR needs to descend one level deeper to pick
+ // up the symbolic operand.
+ if (TREE_CODE (m_op1) == VIEW_CONVERT_EXPR)
+ m_op1 = TREE_OPERAND (m_op1, 0);
if (gimple_num_ops (m_stmt) >= 3)
m_op2 = gimple_assign_rhs2 (m_stmt);
// Check that operands are supported types. One check is enough.
diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index 567b0cdd31b..db31c2bc8c9 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -527,6 +527,47 @@ private:
const irange &outer) const;
};
+
+class operator_view : public range_operator
+{
+public:
+ using range_operator::fold_range;
+ using range_operator::op1_range;
+ using range_operator::update_bitmask;
+ bool fold_range (irange &r, tree type,
+ const irange &op1, const irange &op2,
+ relation_trio rel = TRIO_VARYING) const override;
+ bool fold_range (prange &r, tree type,
+ const prange &op1, const prange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+ bool fold_range (irange &r, tree type,
+ const prange &op1, const irange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+ bool fold_range (prange &r, tree type,
+ const irange &op1, const prange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+
+ bool op1_range (irange &r, tree type,
+ const irange &lhs, const irange &op2,
+ relation_trio rel = TRIO_VARYING) const override;
+ bool op1_range (prange &r, tree type,
+ const prange &lhs, const prange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+ bool op1_range (irange &r, tree type,
+ const prange &lhs, const irange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+ bool op1_range (prange &r, tree type,
+ const irange &lhs, const prange &op2,
+ relation_trio rel = TRIO_VARYING) const final override;
+
+ void update_bitmask (irange &r, const irange &lh,
+ const irange &) const final override;
+private:
+// VIEW_CONVERT_EXPR works much like a cast between integral values, so use
+// the cast operator. Non-integrals are not handled as yet.
+ operator_cast m_cast;
+};
+
class operator_plus : public range_operator
{
public:
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 6b6bf78cb2f..cf5b8fe960f 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -60,6 +60,7 @@ operator_ge op_ge;
operator_identity op_ident;
operator_cst op_cst;
operator_cast op_cast;
+operator_view op_view;
operator_plus op_plus;
operator_abs op_abs;
operator_minus op_minus;
@@ -97,6 +98,7 @@ range_op_table::range_op_table ()
set (INTEGER_CST, op_cst);
set (NOP_EXPR, op_cast);
set (CONVERT_EXPR, op_cast);
+ set (VIEW_CONVERT_EXPR, op_view);
set (FLOAT_EXPR, op_cast);
set (FIX_TRUNC_EXPR, op_cast);
set (PLUS_EXPR, op_plus);
@@ -3247,6 +3249,80 @@ operator_cast::op1_range (irange &r, tree type,
return true;
}
+// VIEW_CONVERT_EXPR works like a cast between integral values.
+// If the number of bits are not the same, behaviour is undefined,
+// so cast behaviour still works.
+
+bool
+operator_view::fold_range (irange &r, tree type,
+ const irange &op1, const irange &op2,
+ relation_trio rel) const
+{
+ return m_cast.fold_range (r, type, op1, op2, rel);
+}
+
+bool
+operator_view::fold_range (prange &r, tree type,
+ const prange &op1, const prange &op2,
+ relation_trio rel) const
+{
+ return m_cast.fold_range (r, type, op1, op2, rel);
+}
+bool
+operator_view::fold_range (irange &r, tree type,
+ const prange &op1, const irange &op2,
+ relation_trio rel) const
+{
+ return m_cast.fold_range (r, type, op1, op2, rel);
+}
+
+bool
+operator_view::fold_range (prange &r, tree type,
+ const irange &op1, const prange &op2,
+ relation_trio rel) const
+{
+ return m_cast.fold_range (r, type, op1, op2, rel);
+}
+
+bool
+operator_view::op1_range (irange &r, tree type,
+ const irange &lhs, const irange &op2,
+ relation_trio rel) const
+{
+ return m_cast.op1_range (r, type, lhs, op2, rel);
+}
+
+bool
+operator_view::op1_range (prange &r, tree type,
+ const prange &lhs, const prange &op2,
+ relation_trio rel) const
+{
+ return m_cast.op1_range (r, type, lhs, op2, rel);
+}
+
+bool
+operator_view::op1_range (irange &r, tree type,
+ const prange &lhs, const irange &op2,
+ relation_trio rel) const
+{
+ return m_cast.op1_range (r, type, lhs, op2, rel);
+}
+
+bool
+operator_view::op1_range (prange &r, tree type,
+ const irange &lhs, const prange &op2,
+ relation_trio rel) const
+{
+ return m_cast.op1_range (r, type, lhs, op2, rel);
+}
+
+void
+operator_view::update_bitmask (irange &r, const irange &lh,
+ const irange &rh) const
+{
+ m_cast.update_bitmask (r, lh, rh);
+}
+
class operator_logical_and : public range_operator
{
diff --git a/gcc/testsuite/gcc.dg/pr91191.c b/gcc/testsuite/gcc.dg/pr91191.c
new file mode 100644
index 00000000000..7bf727e5b4f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr91191.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+unsigned char reg(_Bool b) {
+ union U {
+ unsigned char f0;
+ _Bool f1;
+ };
+ union U u;
+ u.f1 = b;
+ if (u.f0 > 1) {
+ // This cannot happen
+ // if b is only allowed
+ // to be 0 or 1:
+ return 42;
+ }
+ return 13;
+}
+
+/* { dg-final { scan-tree-dump "return 13" "evrp" } } */
--
2.45.0