Our beloved condition combining code to BIT_FIELD_REFs miscompiles
the testcase because it relies on operand_equal_p to detect equal
bases.  For some reason that very same operand_equal_p is
treating any dereference of a pointer based on the same pointer
or decl the same - idependent on the actual type used for the
access (in this case, two different sized structs).  Weirdly
it already checks alignment...

The following patch makes operand_equal_p behave and more
in line with what it does for MEM_REF handling.

Bootstrap & regtest running on x86_64-unknown-linux-gnu.

Richard.

2019-03-14  Richard Biener  <rguent...@suse.de>

        PR middle-end/89698
        * fold-const.c (operand_equal_p): For INDIRECT_REF check
        that the access types are similar.

        * g++.dg/torture/pr89698.C: New testcase.

Index: gcc/fold-const.c
===================================================================
--- gcc/fold-const.c    (revision 269641)
+++ gcc/fold-const.c    (working copy)
@@ -3220,10 +3220,16 @@ operand_equal_p (const_tree arg0, const_
       switch (TREE_CODE (arg0))
        {
        case INDIRECT_REF:
-         if (!(flags & OEP_ADDRESS_OF)
-             && (TYPE_ALIGN (TREE_TYPE (arg0))
-                 != TYPE_ALIGN (TREE_TYPE (arg1))))
-           return 0;
+         if (!(flags & OEP_ADDRESS_OF))
+           {
+             if (TYPE_ALIGN (TREE_TYPE (arg0))
+                 != TYPE_ALIGN (TREE_TYPE (arg1)))
+               return 0;
+             /* Verify that the access types are compatible.  */
+             if (TYPE_MAIN_VARIANT (TREE_TYPE (arg0))
+                 != TYPE_MAIN_VARIANT (TREE_TYPE (arg1)))
+               return 0;
+           }
          flags &= ~OEP_ADDRESS_OF;
          return OP_SAME (0);
 
Index: gcc/testsuite/g++.dg/torture/pr89698.C
===================================================================
--- gcc/testsuite/g++.dg/torture/pr89698.C      (nonexistent)
+++ gcc/testsuite/g++.dg/torture/pr89698.C      (working copy)
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+
+extern "C" void abort (void);
+
+class A {
+    virtual void f(){};
+public:
+    int x;
+    A(int in): x(in) {};
+};
+
+class B: public A {
+public:
+    int y;
+    B(int in):A(in-1), y(in) {};
+};
+
+int test(void)
+{
+  int res;
+  B b(2);
+  A* bp = &b;
+  void* vp = dynamic_cast<void*>(bp);
+  if (((A*)vp)->x == 1 && ((B*)vp)->y == 2)
+    return 1;
+  return 0;
+}
+int main() { if (test() != 1) abort (); return 0; }

Reply via email to