--- old/src/share/vm/opto/cfgnode.cpp 2017-07-14 16:33:37.073651974 +0200 +++ new/src/share/vm/opto/cfgnode.cpp 2017-07-14 16:33:36.985651979 +0200 @@ -944,15 +944,10 @@ const TypeInstPtr* ttip = (ttp != NULL) ? ttp->isa_instptr() : NULL; const TypeKlassPtr* ttkp = (ttp != NULL) ? ttp->isa_klassptr() : NULL; bool is_intf = false; - if (ttip != NULL) { - ciKlass* k = ttip->klass(); - if (k->is_loaded() && k->is_interface()) - is_intf = true; - } - if (ttkp != NULL) { - ciKlass* k = ttkp->klass(); - if (k->is_loaded() && k->is_interface()) - is_intf = true; + if (ttip != NULL && ttip->is_loaded() && ttip->klass()->is_interface()) { + is_intf = true; + } else if (ttkp != NULL && ttkp->is_loaded() && ttkp->klass()->is_interface()) { + is_intf = true; } // Default case: merge all inputs @@ -1009,9 +1004,9 @@ // be 'I' or 'j/l/O'. Thus we'll pick 'j/l/O'. If this then flows // into a Phi which "knows" it's an Interface type we'll have to // uplift the type. - if (!t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface()) { + if (!t->empty() && ttip != NULL && ttip->is_loaded() && ttip->klass()->is_interface()) { assert(ft == _type, ""); // Uplift to interface - } else if (!t->empty() && ttkp && ttkp->is_loaded() && ttkp->klass()->is_interface()) { + } else if (!t->empty() && ttkp != NULL && ttkp->is_loaded() && ttkp->klass()->is_interface()) { assert(ft == _type, ""); // Uplift to interface } else { // We also have to handle 'evil cases' of interface- vs. class-arrays --- old/src/share/vm/opto/compile.cpp 2017-07-14 16:33:37.385651960 +0200 +++ new/src/share/vm/opto/compile.cpp 2017-07-14 16:33:37.301651964 +0200 @@ -1540,7 +1540,7 @@ } ciKlass* klass = tk->klass(); - if( klass->is_obj_array_klass() ) { + if (klass != NULL && klass->is_obj_array_klass()) { ciKlass* k = TypeAryPtr::OOPS->klass(); if( !k || !k->is_loaded() ) // Only fails for some -Xcomp runs k = TypeInstPtr::BOTTOM->klass(); @@ -4163,11 +4163,11 @@ // (2) subklass does not overlap with superklass => always fail // (3) superklass has NO subtypes and we can check with a simple compare. int Compile::static_subtype_check(ciKlass* superk, ciKlass* subk) { - if (StressReflectiveCode) { + if (StressReflectiveCode || superk == NULL || subk == NULL) { return SSC_full_test; // Let caller generate the general case. } - if (superk == env()->Object_klass()) { + if (!EnableMVT && !EnableValhalla && superk == env()->Object_klass()) { return SSC_always_true; // (0) this test cannot fail } --- old/src/share/vm/opto/graphKit.cpp 2017-07-14 16:33:37.741651943 +0200 +++ new/src/share/vm/opto/graphKit.cpp 2017-07-14 16:33:37.637651948 +0200 @@ -2565,7 +2565,7 @@ // types load from the super-class display table which is immutable. m = mem->memory_at(C->get_alias_index(gvn->type(p2)->is_ptr())); Node *kmem = might_be_cache ? m : C->immutable_memory(); - Node *nkls = gvn->transform(LoadKlassNode::make(*gvn, NULL, kmem, p2, gvn->type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL)); + Node *nkls = gvn->transform(LoadKlassNode::make(*gvn, NULL, kmem, p2, gvn->type(p2)->is_ptr(), TypeKlassPtr::BOTTOM)); // Compile speed common case: ARE a subtype and we canNOT fail if( superklass == nkls ) @@ -2917,6 +2917,7 @@ Node* *failure_control) { kill_dead_locals(); // Benefit all the uncommon traps const TypeKlassPtr *tk = _gvn.type(superklass)->is_klassptr(); + assert(tk->is_loaded(), "must be loaded"); const Type *toop = TypeOopPtr::make_from_klass(tk->klass()); // Fast cutout: Check the case that the cast is vacuously true. @@ -3223,6 +3224,7 @@ const TypeKlassPtr* inst_klass = _gvn.type(klass_node)->isa_klassptr(); if (!StressReflectiveCode && inst_klass != NULL) { ciKlass* klass = inst_klass->klass(); + assert(klass != NULL, "klass should not be NULL"); bool xklass = inst_klass->klass_is_exact(); if (xklass || klass->is_array_klass()) { jint lhelper = klass->layout_helper(); --- old/src/share/vm/opto/library_call.cpp 2017-07-14 16:33:38.109651926 +0200 +++ new/src/share/vm/opto/library_call.cpp 2017-07-14 16:33:38.013651931 +0200 @@ -3586,7 +3586,7 @@ phi->add_req(makecon(TypeInstPtr::make(env()->Object_klass()->java_mirror()))); // If we fall through, it's a plain class. Get its _super. p = basic_plus_adr(kls, in_bytes(Klass::super_offset())); - kls = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeKlassPtr::OBJECT_OR_NULL)); + kls = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeKlassPtr::BOTTOM)); null_ctl = top(); kls = null_check_oop(kls, &null_ctl); if (null_ctl != top()) { @@ -3728,7 +3728,7 @@ record_for_igvn(region); const TypePtr* adr_type = TypeRawPtr::BOTTOM; // memory type of loads - const TypeKlassPtr* kls_type = TypeKlassPtr::OBJECT_OR_NULL; + const TypeKlassPtr* kls_type = TypeKlassPtr::BOTTOM; int class_klass_offset = java_lang_Class::klass_offset_in_bytes(); // First null-check both mirrors and load each mirror's klass metaobject. --- old/src/share/vm/opto/memnode.cpp 2017-07-14 16:33:38.493651908 +0200 +++ new/src/share/vm/opto/memnode.cpp 2017-07-14 16:33:38.389651913 +0200 @@ -1753,6 +1753,7 @@ } else if (tp->base() == Type::KlassPtr) { assert( off != Type::OffsetBot || // arrays can be cast to Objects + tp->is_klassptr()->klass() == NULL || tp->is_klassptr()->klass()->is_java_lang_Object() || // also allow array-loading from the primary supertype // array during subtype checks @@ -1764,7 +1765,7 @@ const TypeKlassPtr *tkls = tp->isa_klassptr(); if (tkls != NULL && !StressReflectiveCode) { ciKlass* klass = tkls->klass(); - if (klass->is_loaded() && tkls->klass_is_exact()) { + if (tkls->is_loaded() && tkls->klass_is_exact()) { // We are loading a field from a Klass metaobject whose identity // is known at compile time (the type is "exact" or "precise"). // Check for fields we know are maintained as constants by the VM. @@ -1797,7 +1798,7 @@ // We can still check if we are loading from the primary_supers array at a // shallow enough depth. Even though the klass is not exact, entries less // than or equal to its super depth are correct. - if (klass->is_loaded() ) { + if (tkls->is_loaded()) { ciType *inner = klass; while( inner->is_obj_array_klass() ) inner = inner->as_obj_array_klass()->base_element_type(); @@ -2128,9 +2129,10 @@ // Check for loading klass from an array klass const TypeKlassPtr *tkls = tp->isa_klassptr(); if (tkls != NULL && !StressReflectiveCode) { - ciKlass* klass = tkls->klass(); - if( !klass->is_loaded() ) + if (!tkls->is_loaded()) { return _type; // Bail out if not loaded + } + ciKlass* klass = tkls->klass(); if( klass->is_obj_array_klass() && tkls->offset() == in_bytes(ObjArrayKlass::element_klass_offset())) { ciKlass* elem = klass->as_obj_array_klass()->element_klass(); --- old/src/share/vm/opto/type.cpp 2017-07-14 16:33:38.869651891 +0200 +++ new/src/share/vm/opto/type.cpp 2017-07-14 16:33:38.785651895 +0200 @@ -645,6 +645,7 @@ TypeKlassPtr::OBJECT = TypeKlassPtr::make(TypePtr::NotNull, current->env()->Object_klass(), Offset(0) ); TypeKlassPtr::OBJECT_OR_NULL = TypeKlassPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), Offset(0) ); + TypeKlassPtr::BOTTOM = (EnableValhalla | EnableMVT) ? TypeKlassPtr::make(TypePtr::BotPTR, NULL, Offset(0)) : TypeKlassPtr::OBJECT_OR_NULL; const Type **fi2c = TypeTuple::fields(2); fi2c[TypeFunc::Parms+0] = TypeInstPtr::BOTTOM; // Method* @@ -5248,6 +5249,7 @@ // Not-null object klass or below const TypeKlassPtr *TypeKlassPtr::OBJECT; const TypeKlassPtr *TypeKlassPtr::OBJECT_OR_NULL; +const TypeKlassPtr* TypeKlassPtr::BOTTOM; //------------------------------TypeKlassPtr----------------------------------- TypeKlassPtr::TypeKlassPtr( PTR ptr, ciKlass* klass, Offset offset ) @@ -5257,27 +5259,21 @@ //------------------------------make------------------------------------------- // ptr to klass 'k', if Constant, or possibly to a sub-klass if not a Constant const TypeKlassPtr* TypeKlassPtr::make(PTR ptr, ciKlass* k, Offset offset) { - assert( k != NULL, "Expect a non-NULL klass"); - assert(k->is_instance_klass() || k->is_array_klass(), "Incorrect type of klass oop"); - TypeKlassPtr *r = - (TypeKlassPtr*)(new TypeKlassPtr(ptr, k, offset))->hashcons(); - - return r; + assert(k == NULL || k->is_instance_klass() || k->is_array_klass(), "Incorrect type of klass oop"); + return (TypeKlassPtr*)(new TypeKlassPtr(ptr, k, offset))->hashcons(); } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeKlassPtr::eq( const Type *t ) const { const TypeKlassPtr *p = t->is_klassptr(); - return - klass()->equals(p->klass()) && - TypePtr::eq(p); + return klass() == p->klass() && TypePtr::eq(p); } //------------------------------hash------------------------------------------- // Type-specific hashing function. int TypeKlassPtr::hash(void) const { - return java_add(klass()->hash(), TypePtr::hash()); + return java_add(klass() != NULL ? klass()->hash() : 0, TypePtr::hash()); } //------------------------------singleton-------------------------------------- @@ -5298,7 +5294,7 @@ const TypeKlassPtr* ktkp = kills->isa_klassptr(); if (ft->empty()) { - if (!empty() && ktkp != NULL && ktkp->klass()->is_loaded() && ktkp->klass()->is_interface()) + if (!empty() && ktkp != NULL && ktkp->is_loaded() && ktkp->klass()->is_interface()) return kills; // Uplift to interface return Type::TOP; // Canonical empty value @@ -5432,6 +5428,7 @@ // It will be NotNull, and exact if and only if the klass type is exact. const TypeOopPtr* TypeKlassPtr::as_instance_type() const { ciKlass* k = klass(); + assert(k != NULL, "klass should not be NULL"); bool xk = klass_is_exact(); //return TypeInstPtr::make(TypePtr::NotNull, k, xk, NULL, 0); const TypeOopPtr* toop = TypeOopPtr::make_from_klass_raw(k); @@ -5516,6 +5513,14 @@ Offset off = meet_offset(tkls->offset()); PTR ptr = meet_ptr(tkls->ptr()); + if (klass() == NULL || tkls->klass() == NULL) { + ciKlass* k = NULL; + if (ptr == Constant) { + k = (klass() == NULL) ? tkls->klass() : klass(); + } + return make(ptr, k, off); + } + // Check for easy case; klasses are equal (and perhaps not loaded!) // If we have constants, then we created oops so classes are loaded // and we can handle the constants further down. This case handles @@ -5606,11 +5611,11 @@ st->print("precise "); case NotNull: { - const char *name = klass()->name()->as_utf8(); - if( name ) { + if (klass() != NULL) { + const char* name = klass()->name()->as_utf8(); st->print("klass %s: " INTPTR_FORMAT, name, p2i(klass())); } else { - ShouldNotReachHere(); + st->print("klass BOTTOM"); } } case BotPTR: --- old/src/share/vm/opto/type.hpp 2017-07-14 16:33:39.261651872 +0200 +++ new/src/share/vm/opto/type.hpp 2017-07-14 16:33:39.161651877 +0200 @@ -1411,15 +1411,10 @@ bool _klass_is_exact; public: - ciSymbol* name() const { return klass()->name(); } - ciKlass* klass() const { return _klass; } bool klass_is_exact() const { return _klass_is_exact; } - bool is_loaded() const { return klass()->is_loaded(); } - - // Make a generic (unclassed) pointer to metadata. - static const TypeKlassPtr* make(PTR ptr, Offset offset); + bool is_loaded() const { return klass() != NULL && klass()->is_loaded(); } // ptr to klass 'k' static const TypeKlassPtr* make(ciKlass* k) { return make( TypePtr::Constant, k, Offset(0)); } @@ -1444,6 +1439,7 @@ // Convenience common pre-built types. static const TypeKlassPtr* OBJECT; // Not-null object klass or below static const TypeKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same + static const TypeKlassPtr* BOTTOM; #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping #endif --- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-07-14 16:33:39.637651855 +0200 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-07-14 16:33:39.549651859 +0200 @@ -2605,6 +2605,84 @@ Asserts.assertEQ(result, 0L); } + // Test correctness of the Class::isAssignableFrom intrinsic + @Test() + public boolean test94(Class supercls, Class subcls) { + return supercls.isAssignableFrom(subcls); + } + + public void test94_verifier(boolean warmup) { + Asserts.assertTrue(test94(__Value.class, MyValue1.class), "test94_1 failed"); + Asserts.assertTrue(test94(MyValue1.class, MyValue1.class), "test94_2 failed"); + Asserts.assertTrue(test94(Object.class, java.util.ArrayList.class), "test94_3 failed"); + Asserts.assertTrue(test94(java.util.ArrayList.class, java.util.ArrayList.class), "test94_4 failed"); + Asserts.assertTrue(!test94(Object.class, MyValue1.class), "test94_5 failed"); + Asserts.assertTrue(!test94(__Value.class, java.util.ArrayList.class), "test94_6 failed"); + } + + // Verify that Class::isAssignableFrom checks with statically known classes are folded + @Test(failOn = LOADK) + public boolean test95() { + boolean check1 = java.util.AbstractList.class.isAssignableFrom(java.util.ArrayList.class); + boolean check2 = MyValue1.class.isAssignableFrom(MyValue1.class); + boolean check3 = Object.class.isAssignableFrom(java.util.ArrayList.class); + boolean check4 = java.lang.__Value.class.isAssignableFrom(MyValue1.class); + boolean check5 = !Object.class.isAssignableFrom(MyValue1.class); + boolean check6 = !MyValue1.class.isAssignableFrom(Object.class); + return check1 && check2 && check3 && check4 && check5 && check6; + } + + public void test95_verifier(boolean warmup) { + Asserts.assertTrue(test95(), "test95 failed"); + } + + // Test correctness of the Class::getSuperclass intrinsic + @Test() + public Class test96(Class cls) { + return cls.getSuperclass(); + } + + public void test96_verifier(boolean warmup) { + Asserts.assertTrue(test96(__Value.class) == null, "test94_1 failed"); + Asserts.assertTrue(test96(Object.class) == null, "test94_2 failed"); + Asserts.assertTrue(test96(MyValue1.class) == __Value.class, "test94_3 failed"); + Asserts.assertTrue(test96(Class.class) == Object.class, "test94_4 failed"); + } + + // Verify that Class::getSuperclass checks with statically known classes are folded + @Test(failOn = LOADK) + public boolean test97() { + boolean check1 = __Value.class.getSuperclass() == null; + boolean check2 = Object.class.getSuperclass() == null; + boolean check3 = MyValue1.class.getSuperclass() == __Value.class; + boolean check4 = Class.class.getSuperclass() == Object.class; + return check1 && check2 && check3 && check4; + } + + public void test97_verifier(boolean warmup) { + Asserts.assertTrue(test97(), "test97 failed"); + } + + // Test Class::cast intrinsic + @Test() + public Object test98(Class cls, Object o) throws ClassCastException { + return cls.cast(o); + } + + public void test98_verifier(boolean warmup) { + try { + test98(ValueCapableClass1.class, vcc); + } catch (ClassCastException e) { + throw new RuntimeException("test98_1 failed"); + } + try { + test98(__Value.class, new Object()); + throw new RuntimeException("test98_2 failed"); + } catch (ClassCastException e) { + // Expected + } + } + // ========== Test infrastructure ========== private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -2634,6 +2712,7 @@ private static final String ALLOCA = "(.*precise klass \\[Qcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END; private static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; private static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END; + private static final String LOADK = START + "LoadK" + MID + END; private static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END; private static final String LOOP = START + "Loop" + MID + "" + END; --- old/test/runtime/valhalla/valuetypes/DeriveValueTypeCreation.java 2017-07-14 16:33:40.109651833 +0200 +++ new/test/runtime/valhalla/valuetypes/DeriveValueTypeCreation.java 2017-07-14 16:33:39.985651839 +0200 @@ -44,9 +44,7 @@ * @modules java.base/jdk.internal.org.objectweb.asm * @build runtime.valhalla.valuetypes.ValueCapableClass * @run main/othervm -Xint -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation - * @run main/othervm -Xcomp -noverify -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom - * runtime.valhalla.valuetypes.DeriveValueTypeCreation + * @run main/othervm -Xcomp -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.DeriveValueTypeCreation */ public class DeriveValueTypeCreation { --- old/test/runtime/valhalla/valuetypes/MVTComboTier1.java 2017-07-14 16:33:40.473651816 +0200 +++ new/test/runtime/valhalla/valuetypes/MVTComboTier1.java 2017-07-14 16:33:40.373651821 +0200 @@ -38,9 +38,7 @@ * @build jdk.test.lib.combo.ComboTestHelper * @run main/othervm -noverify -Xint -XX:+EnableMVT runtime.valhalla.valuetypes.MVTComboTier1 3 * @run main/othervm -noverify -Xint -XX:+EnableMVT runtime.valhalla.valuetypes.MVTComboTier1 -reducetypes 6 - * @run main/othervm -noverify -Xcomp -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom - * runtime.valhalla.valuetypes.MVTComboTier1 -reducetypes 5 + * @run main/othervm -noverify -Xcomp -XX:+EnableMVT runtime.valhalla.valuetypes.MVTComboTier1 -reducetypes 5 */ public class MVTComboTier1 { --- old/test/runtime/valhalla/valuetypes/ValueOops.java 2017-07-14 16:33:40.921651795 +0200 +++ new/test/runtime/valhalla/valuetypes/ValueOops.java 2017-07-14 16:33:40.817651800 +0200 @@ -55,19 +55,15 @@ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * runtime.valhalla.valuetypes.ValueOops * @run main/othervm -Xcomp -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * runtime.valhalla.valuetypes.ValueOops * @run main/othervm -Xcomp -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * runtime.valhalla.valuetypes.ValueOops * @run main/othervm -Xcomp -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * runtime.valhalla.valuetypes.ValueOops * @run main/othervm -Xcomp -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * runtime.valhalla.valuetypes.ValueOops */ --- old/test/runtime/valhalla/valuetypes/VboxUnbox.java 2017-07-14 16:33:41.317651777 +0200 +++ new/test/runtime/valhalla/valuetypes/VboxUnbox.java 2017-07-14 16:33:41.197651782 +0200 @@ -33,9 +33,7 @@ * @library /test/lib * @build runtime.valhalla.valuetypes.ValueCapableClass * @run main/othervm -Xint -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.VboxUnbox - * @run main/othervm -Xcomp -noverify -XX:+EnableMVT - * -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_isAssignableFrom - * runtime.valhalla.valuetypes.VboxUnbox + * @run main/othervm -Xcomp -noverify -XX:+EnableMVT runtime.valhalla.valuetypes.VboxUnbox */ public class VboxUnbox {