8238384: CTW: C2 compilation fails with "assert(store != load->find_exact_control(load->in(0))) failed: dependence cycle found"

Reviewed-by: vlivanov, thartmann
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 8cb3818..8446266 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -1905,8 +1905,9 @@
 bool Compile::can_alias(const TypePtr* adr_type, int alias_idx) {
   if (alias_idx == AliasIdxTop)         return false; // the empty category
   if (adr_type == NULL)                 return false; // NULL serves as TypePtr::TOP
-  if (alias_idx == AliasIdxBot)         return true;  // the universal category
-  if (adr_type->base() == Type::AnyPtr) return true;  // TypePtr::BOTTOM or its twins
+  // Known instance doesn't alias with bottom memory
+  if (alias_idx == AliasIdxBot)         return !adr_type->is_known_instance();                   // the universal category
+  if (adr_type->base() == Type::AnyPtr) return !C->get_adr_type(alias_idx)->is_known_instance(); // TypePtr::BOTTOM or its twins
 
   // the only remaining possible overlap is identity
   int adr_idx = get_alias_index(adr_type);
diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp
index aebf8f9..15ba69e 100644
--- a/src/hotspot/share/opto/gcm.cpp
+++ b/src/hotspot/share/opto/gcm.cpp
@@ -707,19 +707,6 @@
         // instead of control + memory.
         if (mstore->ideal_Opcode() == Op_SafePoint)
           continue;
-
-        // Check if the store is a membar on which the load is control dependent.
-        // Inserting an anti-dependency between that membar and the load would
-        // create a cycle that causes local scheduling to fail.
-        if (mstore->isa_MachMemBar()) {
-          Node* dom = load->find_exact_control(load->in(0));
-          while (dom != NULL && dom != dom->in(0) && dom != mstore) {
-            dom = dom->in(0);
-          }
-          if (dom == mstore) {
-            continue;
-          }
-        }
       } else {
         // Some raw memory, such as the load of "top" at an allocation,
         // can be control dependent on the previous safepoint. See
diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp
index a907f59..6a89476 100644
--- a/src/hotspot/share/opto/loopnode.cpp
+++ b/src/hotspot/share/opto/loopnode.cpp
@@ -31,6 +31,7 @@
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "opto/addnode.hpp"
+#include "opto/arraycopynode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
@@ -4089,8 +4090,16 @@
           }
         } else {
           Node *sctrl = has_ctrl(s) ? get_ctrl(s) : s->in(0);
+          const TypePtr* adr_type = s->adr_type();
+          if (s->is_ArrayCopy()) {
+            // Copy to known instance needs destination type to test for aliasing
+            const TypePtr* dest_type = s->as_ArrayCopy()->_dest_type;
+            if (dest_type != TypeOopPtr::BOTTOM) {
+              adr_type = dest_type;
+            }
+          }
           assert(sctrl != NULL || !s->is_reachable_from_root(), "must have control");
-          if (sctrl != NULL && !sctrl->is_top() && C->can_alias(s->adr_type(), load_alias_idx) && is_dominator(early, sctrl)) {
+          if (sctrl != NULL && !sctrl->is_top() && C->can_alias(adr_type, load_alias_idx) && is_dominator(early, sctrl)) {
             LCA = dom_lca_for_get_late_ctrl(LCA, sctrl, n);
           }
         }
diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp
index 7171c05..a9bd67d 100644
--- a/src/hotspot/share/opto/macro.hpp
+++ b/src/hotspot/share/opto/macro.hpp
@@ -119,7 +119,7 @@
   void expand_unlock_node(UnlockNode *unlock);
 
   // More helper methods modeled after GraphKit for array copy
-  void insert_mem_bar(Node** ctrl, Node** mem, int opcode, Node* precedent = NULL);
+  void insert_mem_bar(Node** ctrl, Node** mem, int opcode, int alias_idx = Compile::AliasIdxBot, Node* precedent = NULL);
   Node* array_element_address(Node* ary, Node* idx, BasicType elembt);
   Node* ConvI2L(Node* offset);
 
diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp
index 8706563..b98aedd 100644
--- a/src/hotspot/share/opto/macroArrayCopy.cpp
+++ b/src/hotspot/share/opto/macroArrayCopy.cpp
@@ -33,8 +33,8 @@
 #include "utilities/align.hpp"
 #include "utilities/powerOfTwo.hpp"
 
-void PhaseMacroExpand::insert_mem_bar(Node** ctrl, Node** mem, int opcode, Node* precedent) {
-  MemBarNode* mb = MemBarNode::make(C, opcode, Compile::AliasIdxBot, precedent);
+void PhaseMacroExpand::insert_mem_bar(Node** ctrl, Node** mem, int opcode, int alias_idx, Node* precedent) {
+  MemBarNode* mb = MemBarNode::make(C, opcode, alias_idx, precedent);
   mb->init_req(TypeFunc::Control, *ctrl);
   mb->init_req(TypeFunc::Memory, *mem);
   transform_later(mb);
@@ -706,7 +706,15 @@
   // the membar also.
   //
   // Do not let reads from the cloned object float above the arraycopy.
-  if (alloc != NULL && !alloc->initialization()->does_not_escape()) {
+  if (ac->_dest_type != TypeOopPtr::BOTTOM && adr_type != ac->_dest_type) {
+    // Known instance: add memory of the destination type
+    MergeMemNode* mm = out_mem->clone()->as_MergeMem();
+    transform_later(mm);
+    uint dest_idx = C->get_alias_index(ac->_dest_type);
+    insert_mem_bar(ctrl, &out_mem, Op_MemBarCPUOrder, dest_idx);
+    mm->set_memory_at(dest_idx, out_mem);
+    out_mem = mm;
+  } else if (alloc != NULL && !alloc->initialization()->does_not_escape()) {
     // Do not let stores that initialize this object be reordered with
     // a subsequent store that would make this object accessible by
     // other threads.
diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp
index eae613f..08c78c3 100644
--- a/src/hotspot/share/opto/type.hpp
+++ b/src/hotspot/share/opto/type.hpp
@@ -452,6 +452,7 @@
   const Type* maybe_remove_speculative(bool include_speculative) const;
 
   virtual bool maybe_null() const { return true; }
+  virtual bool is_known_instance() const { return false; }
 
 private:
   // support arrays
@@ -1397,6 +1398,10 @@
     return _ptrtype;
   }
 
+  bool is_known_instance() const {
+    return _ptrtype->is_known_instance();
+  }
+
 #ifndef PRODUCT
   virtual void dump2( Dict &d, uint depth, outputStream *st ) const;
 #endif
diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestCopyOfBrokenAntiDependency.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestCopyOfBrokenAntiDependency.java
new file mode 100644
index 0000000..508d507
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestCopyOfBrokenAntiDependency.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8238384
+ * @summary CTW: C2 compilation fails with "assert(store != load->find_exact_control(load->in(0))) failed: dependence cycle found"
+ *
+ * @run main/othervm -XX:-BackgroundCompilation TestCopyOfBrokenAntiDependency
+ *
+ */
+
+import java.util.Arrays;
+
+public class TestCopyOfBrokenAntiDependency {
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 20_000; i++) {
+            test(100);
+        }
+    }
+
+    private static Object test(int length) {
+        Object[] src  = new Object[length]; // non escaping
+        final Object[] dst = Arrays.copyOf(src, 10); // can't be removed
+        final Object[] dst2 = Arrays.copyOf(dst, 100);
+        // load is control dependent on membar from previous copyOf
+        // but has memory edge to first copyOf.
+        final Object v = dst[0];
+        return v;
+    }
+}