changeset 53166:efdd3044724d lworld

8214977: [lworld] checkcast should perform a null-check when its CP entry contains a Q-descriptor
author fparain
date Thu, 06 Dec 2018 16:28:02 -0500
parents eaaa683b0d67
children 92aad0353190
files src/hotspot/cpu/x86/templateTable_x86.cpp src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/oops/constantPool.cpp src/hotspot/share/oops/constantPool.hpp src/hotspot/share/utilities/constantTag.cpp src/hotspot/share/utilities/constantTag.hpp test/hotspot/jtreg/runtime/valhalla/valuetypes/CheckcastTest.java
diffstat 7 files changed, 187 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Thu Dec 06 16:28:02 2018 -0500
@@ -4480,10 +4480,11 @@
   __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array
   __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index
   // See if bytecode has already been quicked
-  __ cmpb(Address(rdx, rbx,
-                  Address::times_1,
-                  Array<u1>::base_offset_in_bytes()),
-          JVM_CONSTANT_Class);
+  __ movzbl(rdx, Address(rdx, rbx,
+      Address::times_1,
+      Array<u1>::base_offset_in_bytes()));
+  __ andl (rdx, ~JVM_CONSTANT_QDESC_BIT);
+  __ cmpl(rdx, JVM_CONSTANT_Class);
   __ jcc(Assembler::equal, quicked);
   __ push(atos); // save receiver for result, and for GC
   call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
@@ -4521,15 +4522,29 @@
   // Come here on success
   __ bind(ok_is_subtype);
   __ mov(rax, rdx); // Restore object in rdx
+  __ jmp(done);
+
+  __ bind(is_null);
 
   // Collect counts on whether this check-cast sees NULLs a lot or not.
   if (ProfileInterpreter) {
-    __ jmp(done);
-    __ bind(is_null);
     __ profile_null_seen(rcx);
-  } else {
-    __ bind(is_null);   // same as 'done'
   }
+
+  if (EnableValhalla) {
+    // Get cpool & tags index
+    __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array
+    __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index
+    // See if CP entry is a Q-descriptor
+    __ movzbl(rcx, Address(rdx, rbx,
+        Address::times_1,
+        Array<u1>::base_offset_in_bytes()));
+    __ andl (rcx, JVM_CONSTANT_QDESC_BIT);
+    __ cmpl(rcx, JVM_CONSTANT_QDESC_BIT);
+    __ jcc(Assembler::notEqual, done);
+    __ jump(ExternalAddress(Interpreter::_throw_NullPointerException_entry));
+  }
+
   __ bind(done);
 }
 
--- a/src/hotspot/share/classfile/classFileParser.cpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Thu Dec 06 16:28:02 2018 -0500
@@ -495,8 +495,11 @@
 
         Symbol* const name = cp->symbol_at(class_index);
         const unsigned int name_len = name->utf8_length();
-
-        cp->unresolved_klass_at_put(index, class_index, num_klasses++);
+        if (name->is_Q_signature()) {
+          cp->unresolved_qdescriptor_at_put(index, class_index, num_klasses++);
+        } else {
+          cp->unresolved_klass_at_put(index, class_index, num_klasses++);
+        }
         break;
       }
       case JVM_CONSTANT_StringIndex: {
--- a/src/hotspot/share/oops/constantPool.cpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.cpp	Thu Dec 06 16:28:02 2018 -0500
@@ -237,10 +237,11 @@
 
   // The interpreter assumes when the tag is stored, the klass is resolved
   // and the Klass* non-NULL, so we need hardware store ordering here.
+  jbyte qdesc_bit = name->is_Q_signature() ? (jbyte)JVM_CONSTANT_QDESC_BIT : 0;
   if (k != NULL) {
-    release_tag_at_put(class_index, JVM_CONSTANT_Class);
+    release_tag_at_put(class_index, JVM_CONSTANT_Class | qdesc_bit);
   } else {
-    release_tag_at_put(class_index, JVM_CONSTANT_UnresolvedClass);
+    release_tag_at_put(class_index, JVM_CONSTANT_UnresolvedClass | qdesc_bit);
   }
 }
 
@@ -547,7 +548,11 @@
   // The interpreter assumes when the tag is stored, the klass is resolved
   // and the Klass* stored in _resolved_klasses is non-NULL, so we need
   // hardware store ordering here.
-  this_cp->release_tag_at_put(which, JVM_CONSTANT_Class);
+  jbyte tag = JVM_CONSTANT_Class;
+  if (this_cp->tag_at(which).is_Qdescriptor_klass()) {
+    tag |= JVM_CONSTANT_QDESC_BIT;
+  }
+  this_cp->release_tag_at_put(which, tag);
   return k;
 }
 
--- a/src/hotspot/share/oops/constantPool.hpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.hpp	Thu Dec 06 16:28:02 2018 -0500
@@ -275,6 +275,15 @@
   void klass_at_put(int class_index, int name_index, int resolved_klass_index, Klass* k, Symbol* name);
   void klass_at_put(int class_index, Klass* k);
 
+  void unresolved_qdescriptor_at_put(int which, int name_index, int resolved_klass_index) {
+      release_tag_at_put(which, JVM_CONSTANT_UnresolvedClass | (jbyte)JVM_CONSTANT_QDESC_BIT);
+
+      assert((name_index & 0xffff0000) == 0, "must be");
+      assert((resolved_klass_index & 0xffff0000) == 0, "must be");
+      *int_at_addr(which) =
+        build_int_from_shorts((jushort)resolved_klass_index, (jushort)name_index);
+    }
+
   void unresolved_klass_at_put(int which, int name_index, int resolved_klass_index) {
     release_tag_at_put(which, JVM_CONSTANT_UnresolvedClass);
 
--- a/src/hotspot/share/utilities/constantTag.cpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.cpp	Thu Dec 06 16:28:02 2018 -0500
@@ -35,7 +35,7 @@
 #endif // PRODUCT
 
 BasicType constantTag::basic_type() const {
-  switch (_tag) {
+  switch (value()) {
     case JVM_CONSTANT_Integer :
       return T_INT;
     case JVM_CONSTANT_Float :
@@ -69,7 +69,7 @@
 
 
 jbyte constantTag::non_error_value() const {
-  switch (_tag) {
+  switch (value()) {
   case JVM_CONSTANT_UnresolvedClassInError:
     return JVM_CONSTANT_UnresolvedClass;
   case JVM_CONSTANT_MethodHandleInError:
@@ -79,13 +79,13 @@
   case JVM_CONSTANT_DynamicInError:
     return JVM_CONSTANT_Dynamic;
   default:
-    return _tag;
+    return value();
   }
 }
 
 
 jbyte constantTag::error_value() const {
-  switch (_tag) {
+  switch (value()) {
   case JVM_CONSTANT_UnresolvedClass:
     return JVM_CONSTANT_UnresolvedClassInError;
   case JVM_CONSTANT_MethodHandle:
@@ -106,6 +106,8 @@
       return "Invalid index";
     case JVM_CONSTANT_Class :
       return "Class";
+    case (JVM_CONSTANT_Class | (jbyte)JVM_CONSTANT_QDESC_BIT):
+      return "Q-Descriptor";
     case JVM_CONSTANT_Fieldref :
       return "Field";
     case JVM_CONSTANT_Methodref :
--- a/src/hotspot/share/utilities/constantTag.hpp	Wed Dec 05 09:48:04 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.hpp	Thu Dec 06 16:28:02 2018 -0500
@@ -47,12 +47,13 @@
   JVM_CONSTANT_InternalMax              = 106   // Last implementation tag
 };
 
+#define JVM_CONSTANT_QDESC_BIT (1 << 7)
 
 class constantTag {
  private:
   jbyte _tag;
  public:
-  bool is_klass() const             { return _tag == JVM_CONSTANT_Class; }
+  bool is_klass() const             { return value() == JVM_CONSTANT_Class; }
   bool is_field () const            { return _tag == JVM_CONSTANT_Fieldref; }
   bool is_method() const            { return _tag == JVM_CONSTANT_Methodref; }
   bool is_interface_method() const  { return _tag == JVM_CONSTANT_InterfaceMethodref; }
@@ -67,11 +68,15 @@
   bool is_invalid() const           { return _tag == JVM_CONSTANT_Invalid; }
 
   bool is_unresolved_klass() const {
-    return _tag == JVM_CONSTANT_UnresolvedClass || _tag == JVM_CONSTANT_UnresolvedClassInError;
+    return value() == JVM_CONSTANT_UnresolvedClass || value() == JVM_CONSTANT_UnresolvedClassInError;
   }
 
   bool is_unresolved_klass_in_error() const {
-    return _tag == JVM_CONSTANT_UnresolvedClassInError;
+    return value() == JVM_CONSTANT_UnresolvedClassInError;
+  }
+
+  bool is_Qdescriptor_klass() const {
+    return (_tag & JVM_CONSTANT_QDESC_BIT) != 0;
   }
 
   bool is_method_handle_in_error() const {
@@ -108,9 +113,14 @@
     _tag = JVM_CONSTANT_Invalid;
   }
   constantTag(jbyte tag) {
-    assert((tag >= 0 && tag <= JVM_CONSTANT_NameAndType) ||
-           (tag >= JVM_CONSTANT_MethodHandle && tag <= JVM_CONSTANT_InvokeDynamic) ||
-           (tag >= JVM_CONSTANT_InternalMin && tag <= JVM_CONSTANT_InternalMax), "Invalid constant tag");
+    jbyte entry_tag = tag & ~JVM_CONSTANT_QDESC_BIT;
+    assert((((tag & JVM_CONSTANT_QDESC_BIT) == 0) && (entry_tag >= 0 && entry_tag <= JVM_CONSTANT_NameAndType) ||
+           (entry_tag >= JVM_CONSTANT_MethodHandle && entry_tag <= JVM_CONSTANT_InvokeDynamic) ||
+           (entry_tag >= JVM_CONSTANT_InternalMin && entry_tag <= JVM_CONSTANT_InternalMax))
+           || (((tag & JVM_CONSTANT_QDESC_BIT) != 0) && (entry_tag == JVM_CONSTANT_Class ||
+               entry_tag == JVM_CONSTANT_UnresolvedClass || entry_tag == JVM_CONSTANT_UnresolvedClassInError
+               || entry_tag == JVM_CONSTANT_ClassIndex))
+               , "Invalid constant tag");
     _tag = tag;
   }
 
@@ -128,7 +138,8 @@
     return constantTag();
   }
 
-  jbyte value() const                { return _tag; }
+  jbyte value() const                { return _tag & ~JVM_CONSTANT_QDESC_BIT; }
+  jbyte tag() const                  { return _tag; }
   jbyte error_value() const;
   jbyte non_error_value() const;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/CheckcastTest.java	Thu Dec 06 16:28:02 2018 -0500
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018, 2018, Oracle and/or its affiliates. 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.
+ */
+
+package runtime.valhalla.valuetypes;
+
+import jdk.test.lib.Asserts;
+
+/*
+ * @test CheckcastTest
+ * @summary checkcast bytecode test
+ * @library /test/lib
+ * @compile VDefaultTest.java
+ * @run main/othervm -Xint -XX:+EnableValhalla runtime.valhalla.valuetypes.CheckcastTest
+ * @run main/othervm -Xcomp -XX:+EnableValhalla runtime.valhalla.valuetypes.CheckcastTest
+ */
+
+public class CheckcastTest {
+
+    static value class Point {
+        int x;
+        int y;
+
+        public Point() {
+            x = 0;
+            y = 0;
+        }
+
+        public Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+
+    static void testCastingFromObjectToVal(Object o) {
+	boolean npe = false;
+	try {
+	    Point.val pv = (Point.val)o;
+	} catch(NullPointerException e) {
+	    npe = true;
+	}
+	Asserts.assertTrue(npe == false || o == null, "Casting null to val should throw a NPE");
+    }
+    
+    static void testCastingFromValToBox(Point.val p) {
+	boolean npe = false;
+	try {
+	    Point.box pb = p;
+	} catch(NullPointerException e) {
+	    npe = true;
+	}
+	Asserts.assertFalse(npe, "Casting from val to box should not throw an NPE");
+    }
+    
+    static void testCastingFromBoxToVal(Point.box p) {
+	boolean npe = false;
+	try {
+	    Point.val pv = p;
+	} catch(NullPointerException e) {
+	    npe = true;
+	}
+	if (npe) {
+	    Asserts.assertEquals(p, null, "NPE must be thrown only if p is null");
+	} else {
+	    Asserts.assertNotEquals(p, null, "Casting null to val must thrown a NPE");
+	}
+	
+    }
+
+    public static void main(String[] args) {
+	// Testing casting from box to val
+	// First invocation: casting null to Point.val with an unresolved class entry
+	testCastingFromBoxToVal(null);
+	// Second invocation: casting non-null to val, will trigger resolution of the class entry
+	testCastingFromBoxToVal(new Point(3,4));
+	// Third invocation: casting null to Point.val  with a resolved class entry
+	testCastingFromBoxToVal(null);
+
+	// Testing casting from val to box
+	testCastingFromBoxToVal(new Point(3,4));
+
+	// Testing casting from object to val
+	// First invocation: casting null to Point.val with an unresolved class entry
+	testCastingFromObjectToVal(null);
+	// Second invocation: casting non-null to al, will trigger resolution of the class entry
+	testCastingFromObjectToVal(new Point(3,4));
+	// Third invocation: casting null to Point.val  with a resolved class entry");
+	testCastingFromObjectToVal(null);
+	// Fourth invocation: with something not the right type
+	boolean cce = false;
+	try {
+	    testCastingFromObjectToVal(new String("NotPoint"));
+	} catch(ClassCastException e) {
+	    cce = true;
+	}
+	Asserts.assertTrue(cce,"casting invalid type to val should throw CCE");
+    }
+}