diff --git a/runtime/compiler/codegen/J9RecognizedMethodsEnum.hpp b/runtime/compiler/codegen/J9RecognizedMethodsEnum.hpp index cd179d4d24c..39aaca5de0c 100644 --- a/runtime/compiler/codegen/J9RecognizedMethodsEnum.hpp +++ b/runtime/compiler/codegen/J9RecognizedMethodsEnum.hpp @@ -483,6 +483,9 @@ LastVectorMethod = LastVectorIntrinsicMethod, java_lang_reflect_Array_getLength, + jdk_internal_value_ValueClass_newArrayInstance, + jdk_internal_value_ValueClass_newNullRestrictedArray, + jdk_internal_value_NullRestrictedCheckedType_of, java_lang_reflect_Method_invoke, java_util_Arrays_fill, java_util_Arrays_equals, diff --git a/runtime/compiler/control/JITClientCompilationThread.cpp b/runtime/compiler/control/JITClientCompilationThread.cpp index c628f38610a..79456ffa8af 100644 --- a/runtime/compiler/control/JITClientCompilationThread.cpp +++ b/runtime/compiler/control/JITClientCompilationThread.cpp @@ -617,6 +617,12 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes client->write(response, fe->getArrayClassFromComponentClass(clazz)); } break; + case MessageType::VM_getNullRestrictedArrayClassFromComponentClass: + { + auto clazz = std::get<0>(client->getRecvData()); + client->write(response, fe->getNullRestrictedArrayClassFromComponentClass(clazz)); + } + break; case MessageType::VM_matchRAMclassFromROMclass: { J9ROMClass *clazz = std::get<0>(client->getRecvData()); diff --git a/runtime/compiler/control/JITServerHelpers.cpp b/runtime/compiler/control/JITServerHelpers.cpp index 95169d2057f..4509ae9c0be 100644 --- a/runtime/compiler/control/JITServerHelpers.cpp +++ b/runtime/compiler/control/JITServerHelpers.cpp @@ -969,6 +969,7 @@ JITServerHelpers::cacheRemoteROMClass(ClientSessionData *clientSessionData, J9Cl classInfoStruct._classNameIdentifyingLoader = std::get<22>(classInfoTuple); classInfoStruct._arrayElementSize = std::get<23>(classInfoTuple); classInfoStruct._defaultValueSlotAddress = std::get<24>(classInfoTuple); + classInfoStruct._nullRestrictedArrayClass = std::get<26>(classInfoTuple); auto result = clientSessionData->getROMClassMap().insert({ clazz, classInfoStruct }); @@ -1031,6 +1032,7 @@ JITServerHelpers::packRemoteROMClassInfo(J9Class *clazz, J9VMThread *vmThread, T TR_OpaqueClassBlock *hostClass = fe->convertClassPtrToClassOffset(clazz->hostClass); TR_OpaqueClassBlock *componentClass = fe->getComponentClassFromArrayClass((TR_OpaqueClassBlock *)clazz); TR_OpaqueClassBlock *arrayClass = fe->getArrayClassFromComponentClass((TR_OpaqueClassBlock *)clazz); + TR_OpaqueClassBlock *nullRestrictedArrayClass = fe->getNullRestrictedArrayClassFromComponentClass((TR_OpaqueClassBlock *)clazz); uintptr_t totalInstanceSize = clazz->totalInstanceSize; uintptr_t cp = fe->getConstantPoolFromClass((TR_OpaqueClassBlock *)clazz); uintptr_t classFlags = fe->getClassFlagsValue((TR_OpaqueClassBlock *)clazz); @@ -1082,7 +1084,7 @@ JITServerHelpers::packRemoteROMClassInfo(J9Class *clazz, J9VMThread *vmThread, T classHasFinalFields, classDepthAndFlags, classInitialized, byteOffsetToLockword, leafComponentClass, classLoader, hostClass, componentClass, arrayClass, totalInstanceSize, clazz->romClass, cp, classFlags, classChainOffsetIdentifyingLoader, origROMMethods, classNameIdentifyingLoader, arrayElementSize, - defaultValueSlotAddress, romClassHash + defaultValueSlotAddress, romClassHash, nullRestrictedArrayClass ); } @@ -1261,6 +1263,9 @@ JITServerHelpers::getROMClassData(const ClientSessionData::ClassInfo &classInfo, case CLASSINFO_DEFAULT_VALUE_SLOT_ADDRESS: *(j9object_t **)data = classInfo._defaultValueSlotAddress; break; + case CLASSINFO_NULLRESTRICTED_ARRAY_CLASS : + *(TR_OpaqueClassBlock **)data = classInfo._nullRestrictedArrayClass; + break; default: TR_ASSERT(false, "Class Info not supported %u\n", dataType); break; diff --git a/runtime/compiler/control/JITServerHelpers.hpp b/runtime/compiler/control/JITServerHelpers.hpp index 2465c9c754d..fff02eff31f 100644 --- a/runtime/compiler/control/JITServerHelpers.hpp +++ b/runtime/compiler/control/JITServerHelpers.hpp @@ -57,6 +57,7 @@ class JITServerHelpers CLASSINFO_CLASS_CHAIN_OFFSET_IDENTIFYING_LOADER, CLASSINFO_ARRAY_ELEMENT_SIZE, CLASSINFO_DEFAULT_VALUE_SLOT_ADDRESS, + CLASSINFO_NULLRESTRICTED_ARRAY_CLASS, }; // NOTE: when adding new elements to this tuple, add them to the end, @@ -88,7 +89,8 @@ class JITServerHelpers std::string, // 22: _classNameIdentifyingLoader int32_t, // 23: _arrayElementSize j9object_t *, // 24: _defaultValueSlotAddress - std::string // 25: optional hash of packedROMClass + std::string, // 25: optional hash of packedROMClass + TR_OpaqueClassBlock * // 26: _nullRestrictedArrayClass >; // Packs a ROMClass to be transferred to the server. diff --git a/runtime/compiler/env/J9ClassEnv.cpp b/runtime/compiler/env/J9ClassEnv.cpp index 085ad90eebd..1d0446074d8 100644 --- a/runtime/compiler/env/J9ClassEnv.cpp +++ b/runtime/compiler/env/J9ClassEnv.cpp @@ -902,6 +902,29 @@ J9::ClassEnv::isValueBasedOrValueTypeClass(TR_OpaqueClassBlock *clazz) return J9_ARE_ANY_BITS_SET(j9class->classFlags, J9_CLASS_DISALLOWS_LOCKING_FLAGS); } +bool +J9::ClassEnv::isArrayNullRestricted(TR::Compilation *comp,TR_OpaqueClassBlock *arrayClass) + { +#if defined(J9VM_OPT_JITSERVER) + if (auto stream = comp->getStream()) + { + uintptr_t classFlags = 0; + JITServerHelpers::getAndCacheRAMClassInfo((J9Class *)arrayClass, TR::compInfoPT->getClientData(), stream, JITServerHelpers::CLASSINFO_CLASS_FLAGS, (void *)&classFlags); +#ifdef DEBUG + stream->write(JITServer::MessageType::ClassEnv_classFlagsValue, clazz); + uintptr_t classFlagsRemote = std::get<0>(stream->read()); + // Check that class flags from remote call is equal to the cached ones + classFlags = classFlags & J9ClassArrayIsNullRestricted; + classFlagsRemote = classFlagsRemote & J9ClassArrayIsNullRestricted; + TR_ASSERT_FATAL(classFlags == classFlagsRemote, "remote call class flags is not equal to cached class flags"); +#endif + return J9_ARE_ALL_BITS_SET(classFlags, J9ClassArrayIsNullRestricted); + } +#endif /* defined(J9VM_OPT_JITSERVER) */ + J9ArrayClass *j9class = reinterpret_cast(arrayClass); + return J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(j9class); + } + bool J9::ClassEnv::classHasIdentity(TR_OpaqueClassBlock *clazz) { diff --git a/runtime/compiler/env/J9ClassEnv.hpp b/runtime/compiler/env/J9ClassEnv.hpp index 6f832f6493a..0237456f905 100644 --- a/runtime/compiler/env/J9ClassEnv.hpp +++ b/runtime/compiler/env/J9ClassEnv.hpp @@ -96,6 +96,7 @@ class OMR_EXTENSIBLE ClassEnv : public OMR::ClassEnvConnector bool isPrimitiveValueTypeClass(TR_OpaqueClassBlock *); bool isValueTypeClassFlattened(TR_OpaqueClassBlock *clazz); bool isValueBasedOrValueTypeClass(TR_OpaqueClassBlock *); + bool isArrayNullRestricted(TR::Compilation *comp, TR_OpaqueClassBlock *arrayClass); /** \brief * Returns the size of the flattened array element diff --git a/runtime/compiler/env/VMJ9.cpp b/runtime/compiler/env/VMJ9.cpp index a11beff0f39..0991a21b916 100644 --- a/runtime/compiler/env/VMJ9.cpp +++ b/runtime/compiler/env/VMJ9.cpp @@ -7107,6 +7107,13 @@ TR_J9VM::getArrayClassFromComponentClass(TR_OpaqueClassBlock * componentClass) return convertClassPtrToClassOffset(TR::Compiler->cls.convertClassOffsetToClassPtr(componentClass)->arrayClass); } +TR_OpaqueClassBlock * +TR_J9VM::getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock * componentClass) + { + J9Class *clazz = TR::Compiler->cls.convertClassOffsetToClassPtr(componentClass); + return convertClassPtrToClassOffset(J9CLASS_GET_NULLRESTRICTED_ARRAY(clazz)); + } + TR_OpaqueClassBlock * TR_J9VM::getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass) { @@ -9285,6 +9292,27 @@ TR_J9SharedCacheVM::getArrayClassFromComponentClass(TR_OpaqueClassBlock * compon return NULL; } +TR_OpaqueClassBlock * +TR_J9SharedCacheVM::getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock * componentClass) + { + TR::Compilation* comp = _compInfoPT->getCompilation(); + TR_ASSERT(comp, "Should be called only within a compilation"); + + bool validated = false; + TR_OpaqueClassBlock *nullRestrictedArrayClass = TR_J9VM::getNullRestrictedArrayClassFromComponentClass(componentClass); + + if (comp->getOption(TR_UseSymbolValidationManager)) + { + validated = comp->getSymbolValidationManager()->addArrayClassFromComponentClassRecord(nullRestrictedArrayClass, componentClass); + } + else + { + validated = ((TR_ResolvedRelocatableJ9Method *) comp->getCurrentMethod())->validateArbitraryClass(comp, (J9Class *) componentClass); + } + + return validated ? nullRestrictedArrayClass : NULL; + } + TR_OpaqueClassBlock * TR_J9SharedCacheVM::getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass) { diff --git a/runtime/compiler/env/VMJ9.h b/runtime/compiler/env/VMJ9.h index e3b030c10cb..959d3a2c7f9 100644 --- a/runtime/compiler/env/VMJ9.h +++ b/runtime/compiler/env/VMJ9.h @@ -1559,6 +1559,16 @@ class TR_J9VM : public TR_J9VMBase virtual TR_OpaqueClassBlock * getComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass); virtual TR_OpaqueClassBlock * getArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass); + /** \brief + * Retrieves the nullRestrictedArrayClass from the array component class. + * + * \param componentClass + * The array component class + * + * \return + * A pointer to nullRestrictedArrayClass if it exists, otherwise NULL + */ + virtual TR_OpaqueClassBlock * getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass); virtual TR_OpaqueClassBlock * getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass); virtual int32_t getNewArrayTypeFromClass(TR_OpaqueClassBlock *clazz); virtual TR_OpaqueClassBlock * getClassFromSignature(const char * sig, int32_t length, TR_ResolvedMethod *method, bool isVettedForAOT=false); @@ -1685,6 +1695,16 @@ class TR_J9SharedCacheVM : public TR_J9VM virtual bool isPrimitiveClass(TR_OpaqueClassBlock *clazz); virtual TR_OpaqueClassBlock * getComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass); virtual TR_OpaqueClassBlock * getArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass); + /** \brief + * Retrieves the nullRestrictedArrayClass from the array component class. + * + * \param componentClass + * The array component class + * + * \return + * A pointer to nullRestrictedArrayClass if it exists, otherwise NULL + */ + virtual TR_OpaqueClassBlock * getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass); virtual TR_OpaqueClassBlock * getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass); virtual TR_OpaqueClassBlock * getBaseComponentClass(TR_OpaqueClassBlock * clazz, int32_t & numDims); virtual TR_OpaqueClassBlock * getClassFromNewArrayType(int32_t arrayType); diff --git a/runtime/compiler/env/VMJ9Server.cpp b/runtime/compiler/env/VMJ9Server.cpp index f606237cd2d..0bc52f25216 100644 --- a/runtime/compiler/env/VMJ9Server.cpp +++ b/runtime/compiler/env/VMJ9Server.cpp @@ -1171,6 +1171,30 @@ TR_J9ServerVM::getArrayClassFromComponentClass(TR_OpaqueClassBlock *componentCla return arrayClass; } +TR_OpaqueClassBlock * +TR_J9ServerVM::getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass) + { + JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream; + TR_OpaqueClassBlock *nullRestrictedArrayClass = NULL; + JITServerHelpers::getAndCacheRAMClassInfo((J9Class *)componentClass, _compInfoPT->getClientData(), stream, JITServerHelpers::CLASSINFO_NULLRESTRICTED_ARRAY_CLASS, (void *)&nullRestrictedArrayClass); + if (!nullRestrictedArrayClass) + { + stream->write(JITServer::MessageType::VM_getNullRestrictedArrayClassFromComponentClass, componentClass); + nullRestrictedArrayClass = std::get<0>(stream->read()); + if (nullRestrictedArrayClass) + { + // if client initialized nullRestrictedArrayClass, cache the new value + OMR::CriticalSection getRemoteROMClass(_compInfoPT->getClientData()->getROMMapMonitor()); + auto it = _compInfoPT->getClientData()->getROMClassMap().find((J9Class*) componentClass); + if (it != _compInfoPT->getClientData()->getROMClassMap().end()) + { + it->second._nullRestrictedArrayClass = nullRestrictedArrayClass; + } + } + } + return nullRestrictedArrayClass; + } + J9Class * TR_J9ServerVM::matchRAMclassFromROMclass(J9ROMClass *clazz, TR::Compilation *comp) { @@ -3292,6 +3316,26 @@ TR_J9SharedCacheServerVM::getArrayClassFromComponentClass(TR_OpaqueClassBlock * return validated ? arrayClass : NULL; } +TR_OpaqueClassBlock * +TR_J9SharedCacheServerVM::getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock * componentClass) + { + TR::Compilation* comp = _compInfoPT->getCompilation(); + TR_ASSERT(comp, "Should be called only within a compilation"); + + bool validated = false; + TR_OpaqueClassBlock *nullRestrictedArrayClass = TR_J9ServerVM::getNullRestrictedArrayClassFromComponentClass(componentClass); + + if (comp->getOption(TR_UseSymbolValidationManager)) + { + validated = comp->getSymbolValidationManager()->addArrayClassFromComponentClassRecord(nullRestrictedArrayClass, componentClass); + } + else + { + validated = ((TR_ResolvedRelocatableJ9JITServerMethod *) comp->getCurrentMethod())->validateArbitraryClass(comp, (J9Class *) componentClass); + } + return validated ? nullRestrictedArrayClass : NULL; + } + TR_OpaqueClassBlock * TR_J9SharedCacheServerVM::getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass) { diff --git a/runtime/compiler/env/VMJ9Server.hpp b/runtime/compiler/env/VMJ9Server.hpp index 727f23a6f70..7036dcc3f25 100644 --- a/runtime/compiler/env/VMJ9Server.hpp +++ b/runtime/compiler/env/VMJ9Server.hpp @@ -126,6 +126,16 @@ class TR_J9ServerVM: public TR_J9VM virtual bool isCloneable(TR_OpaqueClassBlock *clazzPointer) override; virtual bool canAllocateInlineClass(TR_OpaqueClassBlock *clazz) override; virtual TR_OpaqueClassBlock * getArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass) override; + /** \brief + * Retrieves the nullRestrictedArrayClass from the array component class. + * + * \param componentClass + * The array component class + * + * \return + * A pointer to nullRestrictedArrayClass if it exists, otherwise NULL + */ + virtual TR_OpaqueClassBlock * getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass) override; virtual J9Class * matchRAMclassFromROMclass(J9ROMClass *clazz, TR::Compilation *comp) override; virtual int32_t * getCurrentLocalsMapForDLT(TR::Compilation *comp) override; virtual uintptr_t getReferenceFieldAt(uintptr_t objectPointer, uintptr_t offsetFromHeader) override; @@ -345,6 +355,16 @@ class TR_J9SharedCacheServerVM: public TR_J9ServerVM virtual bool isPrimitiveClass(TR_OpaqueClassBlock *clazzPointer) override; virtual TR_OpaqueClassBlock *getComponentClassFromArrayClass(TR_OpaqueClassBlock *arrayClass) override; virtual TR_OpaqueClassBlock *getArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass) override; + /** \brief + * Retrieves the nullRestrictedArrayClass from the array component class. + * + * \param componentClass + * The array component class + * + * \return + * A pointer to nullRestrictedArrayClass if it exists, otherwise NULL + */ + virtual TR_OpaqueClassBlock * getNullRestrictedArrayClassFromComponentClass(TR_OpaqueClassBlock *componentClass) override; virtual TR_OpaqueClassBlock *getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock *arrayClass) override; virtual TR_OpaqueClassBlock *getBaseComponentClass(TR_OpaqueClassBlock *clazz, int32_t & numDims) override; virtual TR_OpaqueClassBlock *getClassFromNewArrayType(int32_t arrayType) override; diff --git a/runtime/compiler/env/j9method.cpp b/runtime/compiler/env/j9method.cpp index 41c7b767625..2461f1d8b7c 100644 --- a/runtime/compiler/env/j9method.cpp +++ b/runtime/compiler/env/j9method.cpp @@ -3847,6 +3847,19 @@ void TR_ResolvedJ9Method::construct() { TR::unknownMethod} }; + static X ValueClassMethods[] = + { + {x(TR::jdk_internal_value_ValueClass_newArrayInstance, "newArrayInstance", "(Ljdk/internal/value/CheckedType;I)[Ljava/lang/Object;")}, + {x(TR::jdk_internal_value_ValueClass_newNullRestrictedArray, "newNullRestrictedArray", "(Ljava/lang/Class;I)[Ljava/lang/Object;")}, + { TR::unknownMethod} + }; + + static X NullRestrictedCheckedTypeMethods[] = + { + {x(TR::jdk_internal_value_NullRestrictedCheckedType_of, "of", "(Ljava/lang/Class;)Ljdk/internal/value/NullRestrictedCheckedType;")}, + { TR::unknownMethod} + }; + static X SpreadHandleMethods[] = { {x(TR::java_lang_invoke_SpreadHandle_numArgsToPassThrough, "numArgsToPassThrough", "()I")}, @@ -4183,6 +4196,7 @@ void TR_ResolvedJ9Method::construct() { "sun/nio/cs/ISO_8859_1$Decoder", EncodeMethods }, { "java/io/ByteArrayOutputStream", ByteArrayOutputStreamMethods }, { "java/lang/ScopedValue$Carrier", ScopedValueMethods }, + { "jdk/internal/value/ValueClass", ValueClassMethods }, { 0 } }; @@ -4311,6 +4325,7 @@ void TR_ResolvedJ9Method::construct() { { "java/lang/invoke/ConvertHandle$FilterHelpers", ConvertHandleFilterHelpersMethods }, { "java/lang/invoke/DirectMethodHandle$Accessor", DirectMethodHandleAccessorMethods }, + { "jdk/internal/value/NullRestrictedCheckedType", NullRestrictedCheckedTypeMethods }, { 0 } }; diff --git a/runtime/compiler/net/CommunicationStream.hpp b/runtime/compiler/net/CommunicationStream.hpp index 16bf18bcd51..7a45df4a4b2 100644 --- a/runtime/compiler/net/CommunicationStream.hpp +++ b/runtime/compiler/net/CommunicationStream.hpp @@ -128,7 +128,7 @@ class CommunicationStream // likely to lose an increment when merging/rebasing/etc. // static const uint8_t MAJOR_NUMBER = 1; - static const uint16_t MINOR_NUMBER = 68; // ID: +/QzEedP8cjGovxgYECy + static const uint16_t MINOR_NUMBER = 69; // ID: SMN6DXY0b2X1Z76N9825 static const uint8_t PATCH_NUMBER = 0; static uint32_t CONFIGURATION_FLAGS; diff --git a/runtime/compiler/net/MessageTypes.cpp b/runtime/compiler/net/MessageTypes.cpp index b3d0ee619a7..02d1dd47843 100644 --- a/runtime/compiler/net/MessageTypes.cpp +++ b/runtime/compiler/net/MessageTypes.cpp @@ -122,6 +122,7 @@ const char *messageNames[] = "VM_classInitIsFinished", "VM_getClassFromNewArrayType", "VM_getArrayClassFromComponentClass", + "VM_getNullRestrictedArrayClassFromComponentClass", "VM_matchRAMclassFromROMclass", "VM_getInt32FieldAt", "VM_getInt64FieldAt", diff --git a/runtime/compiler/net/MessageTypes.hpp b/runtime/compiler/net/MessageTypes.hpp index aa696bbb08c..0e88fb04fee 100644 --- a/runtime/compiler/net/MessageTypes.hpp +++ b/runtime/compiler/net/MessageTypes.hpp @@ -131,6 +131,7 @@ enum MessageType : uint16_t VM_classInitIsFinished, VM_getClassFromNewArrayType, VM_getArrayClassFromComponentClass, + VM_getNullRestrictedArrayClassFromComponentClass, VM_matchRAMclassFromROMclass, VM_getInt32FieldAt, VM_getInt64FieldAt, diff --git a/runtime/compiler/optimizer/J9ValuePropagation.cpp b/runtime/compiler/optimizer/J9ValuePropagation.cpp index 7e8e829a31a..2ea318a8c41 100644 --- a/runtime/compiler/optimizer/J9ValuePropagation.cpp +++ b/runtime/compiler/optimizer/J9ValuePropagation.cpp @@ -973,7 +973,7 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) TR::Node *indexNode = node->getChild(elementIndexOpIndex); TR::Node *arrayRefNode = node->getChild(arrayRefOpIndex); TR::VPConstraint *arrayConstraint = getConstraint(arrayRefNode, arrayRefGlobal); - TR_YesNoMaybe isCompTypePrimVT = isArrayCompTypePrimitiveValueType(arrayConstraint); + TR_YesNoMaybe isNullRestrictedArray = isArrayNullRestricted(arrayConstraint); TR::Node *storeValueNode = NULL; TR::VPConstraint *storeValueConstraint = NULL; @@ -1053,21 +1053,21 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) } // Transform the helper call to regular aaload and aastore if array flattening is not enabled, - // or the array is known to be of a primitive value type that is not flattened. + // or the array is known to be of a null-restricted array that is not flattened. bool canTransformUnflattenedArrayElementLoadStore = TR::Compiler->om.isValueTypeArrayFlatteningEnabled() ? false : true; if (!canTransformUnflattenedArrayElementLoadStore && arrayConstraint && - (isCompTypePrimVT == TR_yes) && + (isNullRestrictedArray == TR_yes) && !TR::Compiler->cls.isValueTypeClassFlattened(arrayConstraint->getClass())) { canTransformUnflattenedArrayElementLoadStore = true; } - // If the array is known to have a component type that is not a primitive value type or + // If the array is not a null-restricted array or // the value being stored is known not to be a value type, transform the helper // call to a regular aaload or aastore bool canTransformIdentityArrayElementLoadStore = false; - if ((arrayConstraint != NULL && isCompTypePrimVT == TR_no) + if ((arrayConstraint != NULL && isNullRestrictedArray == TR_no) || (isStoreFlattenableArrayElement && isStoreValueVT == TR_no)) { canTransformIdentityArrayElementLoadStore = true; @@ -1076,7 +1076,9 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) bool canTransformFlattenedArrayElementLoadStoreUseTypeHint = false; bool canTransformUnflattenedArrayElementLoadStoreUseTypeHint = false; bool canTransformIdentityArrayElementLoadStoreUseTypeHint = false; - static const char *disableFlattenedArrayElementTypeHintXForm = feGetEnv("TR_DisableFlattenedArrayElementTypeHintXForm"); + // Disable transformation based on type hint which is no longer sufficient enough + // to decide whether the array is null-restricted or not + static const char *enableFlattenedArrayElementTypeHintXForm = feGetEnv("TR_EnableFlattenedArrayElementTypeHintXForm"); static const char *enableUnflattenedArrayElementTypeHintXForm = feGetEnv("TR_EnableUnflattenedArrayElementTypeHintXForm"); TR_OpaqueClassBlock *typeHintClass = arrayConstraint ? arrayConstraint->getTypeHintClass() : NULL; @@ -1098,7 +1100,7 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) { if (TR::Compiler->cls.isValueTypeClassFlattened(hintComponentClass)) { - if (!disableFlattenedArrayElementTypeHintXForm) + if (enableFlattenedArrayElementTypeHintXForm) { if (isLoadFlattenableArrayElement) { @@ -1260,8 +1262,8 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) // if (storeValueBaseNode == NULL || getValueNumber(storeValueBaseNode) != getValueNumber(arrayRefNode)) { - // If storing to an array whose component type is or might be a primitive value - // type and the value that's being assigned is or might be null, both a run-time + // If storing to an array that is or might be null-restricted + // and the value that's being assigned is or might be null, both a run-time // NULLCHK of the value is required (guarded by a check of whether the // component type is a value type) and an ArrayStoreCHK are required; // otherwise, only the ArrayStoreCHK is required. @@ -1273,10 +1275,11 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) flagsForTransform.set(ValueTypesHelperCallTransform::RequiresStoreCheck); } - // If the value being stored is NULL and the destination array component is null-restricted at runtime, - // a NPE is expected to throw. Therefore, when the array component type is not known to be identity type - // in compilation time, a NULLCHK on store value is required - if ((isCompTypePrimVT != TR_no) && + // If the value being stored is NULL and the destination array is null-restricted at runtime, + // an ArrayStoreException is expected to throw. Therefore, when the array component type is not + // known to be identity type in compilation time, a call to is + // required to check whether a null reference is being stored to a null-restricted array + if ((isNullRestrictedArray != TR_no) && (storeValueConstraint == NULL || !storeValueConstraint->isNonNullObject()) && !owningMethodDoesNotContainNonNullableArrayNullStoreCheck(this, node)) { @@ -1317,12 +1320,9 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) // if (storeValueBaseNode == NULL || getValueNumber(storeValueBaseNode) != getValueNumber(arrayRefNode)) { - // If storing to an array whose component type is or might be a primitive value - // type and the value that's being assigned is or might be null, both a run-time - // NULLCHK of the value is required (guarded by a check of whether the - // component type is a value type) and an ArrayStoreCHK are required; - // otherwise, only the ArrayStoreCHK is required. - // + // If storing to an array that is or might be null restricted and the value that's being assigned + // is or might be null, a call to is required to check whether + // a null reference is being stored to a null-restricted array bool mustFail = false; if (!owningMethodDoesNotContainStoreChecks(this, node) && isArrayStoreCheckNeeded(arrayRefNode, storeValueNode, mustFail, storeClassForCheck, componentClassForCheck)) @@ -1385,11 +1385,11 @@ J9::ValuePropagation::constrainRecognizedMethod(TR::Node *node) { reason = "no-array-constraint"; } - else if (isCompTypePrimVT == TR_yes) + else if (isNullRestrictedArray == TR_yes) { reason = "comp-type-is-vt"; } - else if (isCompTypePrimVT == TR_maybe) + else if (isNullRestrictedArray == TR_maybe) { reason = "comp-type-may-be-vt"; } @@ -2949,9 +2949,9 @@ J9::ValuePropagation::isArrayElementFlattened(TR::VPConstraint *arrayConstraint) return TR_no; } - TR_YesNoMaybe isCompTypePrimVT = isArrayCompTypePrimitiveValueType(arrayConstraint); + TR_YesNoMaybe isNullRestrictedArray = isArrayNullRestricted(arrayConstraint); - if (isCompTypePrimVT == TR_yes) + if (isNullRestrictedArray == TR_yes) { TR_OpaqueClassBlock *arrayClass = arrayConstraint->getClass(); if (TR::Compiler->cls.isValueTypeClassFlattened(arrayClass)) @@ -2965,73 +2965,71 @@ J9::ValuePropagation::isArrayElementFlattened(TR::VPConstraint *arrayConstraint) } // Return TR_maybe or TR_no - return isCompTypePrimVT; + return isNullRestrictedArray; } TR_YesNoMaybe -J9::ValuePropagation::isArrayCompTypePrimitiveValueType(TR::VPConstraint *arrayConstraint) +J9::ValuePropagation::isArrayNullRestricted(TR::VPConstraint *arrayConstraint) { if (!TR::Compiler->om.areValueTypesEnabled() || - !TR::Compiler->om.areFlattenableValueTypesEnabled()) // Only null-restricted or primitive value type are flattenable + !TR::Compiler->om.areFlattenableValueTypesEnabled()) // Only null-restricted arrays are flattenable { return TR_no; } // If there's no constraint for the array operand, or no information // is available about the class of the array, or the operand is not - // even definitely known to be an array, VP has to assume that it might - // have a component type that is a primitive value type + // even definitely known to be an array, VP has to assume that the array + // might be null-restricted // if (!(arrayConstraint && arrayConstraint->getClass() && arrayConstraint->getClassType()->isArray() == TR_yes)) { + if (trace()) + traceMsg(comp(), "%s: return TR_maybe. arrayConstraint %p\n", __FUNCTION__, arrayConstraint); return TR_maybe; } - TR_OpaqueClassBlock *arrayComponentClass = fe()->getComponentClassFromArrayClass(arrayConstraint->getClass()); + TR_OpaqueClassBlock *arrayClass = arrayConstraint->getClass(); - // Cases to consider: - // - // - Is no information available about the component type of the array? - // If not, assume it might be a primitive value type. - // - Is the component type definitely a identity type? - // - Is the component type definitely a primitive value type? - // - Is the component type definitely a value type, but not primitive? - // - Is the component type either an abstract class or an interface - // (i.e., not a concrete class)? If so, it might be a value type. - // - Is the array an array of java/lang/Object? See below. - // - Otherwise, it must be a concrete class known not to be a value - // type - // - if (!arrayComponentClass) - { - return TR_maybe; - } - - // No need to check array class type because array classes should be marked as having identity. - if (TR::Compiler->cls.classHasIdentity(arrayComponentClass)) - { - return TR_no; - } - - if (TR::Compiler->cls.isPrimitiveValueTypeClass(arrayComponentClass)) + if (TR::Compiler->cls.isArrayNullRestricted(comp(), arrayClass)) { + if (trace()) + traceMsg(comp(), "%s: return TR_yes. arrayClass %p\n", __FUNCTION__, arrayClass); return TR_yes; } - if (TR::Compiler->cls.isValueTypeClass(arrayComponentClass)) + TR_OpaqueClassBlock *arrayComponentClass = fe()->getComponentClassFromArrayClass(arrayConstraint->getClass()); + + if (!arrayComponentClass) { - return TR_no; + if (trace()) + traceMsg(comp(), "%s: return TR_maybe. arrayComponentClass NULL\n", __FUNCTION__); + return TR_maybe; } if (!TR::Compiler->cls.isConcreteClass(comp(), arrayComponentClass)) { - return TR_maybe; + // Interface shouldn't have identity flag set and it can be implemented by both + // value class and identity class. + // If abstract class has identity flag set, it cannot be extended by value class. + if (TR::Compiler->cls.classHasIdentity(arrayComponentClass)) + { + if (trace()) + traceMsg(comp(), "%s: return TR_no. abstract classHasIdentity\n", __FUNCTION__); + return TR_no; + } + else + { + if (trace()) + traceMsg(comp(), "%s: return TR_maybe. Not concrete class\n", __FUNCTION__); + return TR_maybe; + } } int32_t len; const char *sig = arrayConstraint->getClassSignature(len); - + TR_YesNoMaybe ret; // If the array is an array of java/lang/Object, and it is fixed to // that type, the component type is not a value type (though it // can still hold references to instances of value types). If it is @@ -3041,14 +3039,20 @@ J9::ValuePropagation::isArrayCompTypePrimitiveValueType(TR::VPConstraint *arrayC if (sig && sig[0] == '[' && len == 19 && !strncmp(sig, "[Ljava/lang/Object;", 19)) { - return (arrayConstraint->isFixedClass()) ? TR_no : TR_maybe; + ret = (arrayConstraint->isFixedClass()) ? TR_no : TR_maybe; + if (trace()) + traceMsg(comp(), "%s: return %s. java.lang.Object\n", __FUNCTION__, (ret == TR_no) ? "TR_no" : "TR_maybe"); + return ret; } // If we get to this point, we know this is not an array of // java/lang/Object, and we know the component must be a concrete - // class that is not a value type. + // class. // - return TR_no; + ret = TR::Compiler->cls.classHasIdentity(arrayComponentClass) ? TR_no : TR_maybe; + if (trace()) + traceMsg(comp(), "%s: return %s. Concrete class\n", __FUNCTION__, (ret == TR_no) ? "TR_no" : "TR_maybe"); + return ret; } void @@ -3604,6 +3608,26 @@ J9::ValuePropagation::getParmValues() bool isClassErased = false; TR_OpaqueClassBlock *opaqueClass = parmIterator->getOpaqueClass(); + + if (opaqueClass && + parmIterator->isArray() && + TR::Compiler->om.areFlattenableValueTypesEnabled()) + { + TR_OpaqueClassBlock *arrayComponentClass = comp()->fej9()->getComponentClassFromArrayClass(opaqueClass); + uint32_t len = 0; + char *classSig = parmIterator->getUnresolvedJavaClassSignature(len); + + // Both regular nullable array and null-restricted array have the same signature. + // If the array component class is not an identity type, the array might either be + // a null-restricted array or a nullable array. Therefore, we can't trust the array + // class returned by the signature. + if (!TR::Compiler->cls.classHasIdentity(arrayComponentClass) || + (len == 19 && !strncmp(classSig, "[Ljava/lang/Object;", 19))) + { + opaqueClass = NULL; + } + } + if (opaqueClass) { TR_OpaqueClassBlock *prexClass = NULL; @@ -4121,8 +4145,46 @@ J9::ValuePropagation::innerConstrainAcall(TR::Node *node) addGlobalConstraint(node, TR::VPNonNullObject::create(this)); } } + else if ((method->getRecognizedMethod() == TR::jdk_internal_value_ValueClass_newArrayInstance) && + (node->getFirstChild()->getOpCodeValue() == TR::acall)) + { + /* + * n12n acall jdk/internal/value/ValueClass.newArrayInstance(Ljdk/internal/value/CheckedType;I)[Ljava/lang/Object; + * n9n acall jdk/internal/value/NullRestrictedCheckedType.of(Ljava/lang/Class;)Ljdk/internal/value/NullRestrictedCheckedType; + * n8n aloadi + * n7n loadaddr SomeValueClass + * n11n iload Test.ARRAY_SIZE + */ + bool isGlobal; + TR::Node *firstChildAcallNode = node->getFirstChild(); + constraint = getConstraint(firstChildAcallNode, isGlobal); + + if (constraint && + constraint->isFixedClass() && + firstChildAcallNode->getSymbol()->isResolvedMethod() && + (firstChildAcallNode->getSymbol()->getResolvedMethodSymbol()->getRecognizedMethod() == TR::jdk_internal_value_NullRestrictedCheckedType_of)) + { + if (trace()) + traceMsg(comp(), "%s: node n%dn its first child fixed class %p is an instance of NullRestrictedCheckedType\n", __FUNCTION__, node->getGlobalIndex(), constraint->getClass()); + + constraint = firstChildAcallNode->getFirstChild() ? getConstraint(firstChildAcallNode->getFirstChild(), isGlobal) : NULL; + TR_OpaqueClassBlock *arrayComponentClass = (constraint && constraint->isFixedClass()) ? constraint->getClass() : NULL; + TR_OpaqueClassBlock *nullRestrictedArrayClass = arrayComponentClass ? fe()->getNullRestrictedArrayClassFromComponentClass(arrayComponentClass) : NULL; + + if (trace()) + traceMsg(comp(), "%s: node n%dn arrayComponentClass %p nullRestrictedArrayClass %p\n", __FUNCTION__, node->getGlobalIndex(), arrayComponentClass, nullRestrictedArrayClass); + + if (nullRestrictedArrayClass) + { + TR::VPConstraint *newConstraint = TR::VPFixedClass::create(this, nullRestrictedArrayClass); + addBlockOrGlobalConstraint(node, newConstraint, isGlobal); + addGlobalConstraint(node, TR::VPNonNullObject::create(this)); + return node; + } + } + } } - else + else // if (!node->getOpCode().isIndirect()) { if ((method->getRecognizedMethod() == TR::java_math_BigDecimal_add) || (method->getRecognizedMethod() == TR::java_math_BigDecimal_subtract) || diff --git a/runtime/compiler/optimizer/J9ValuePropagation.hpp b/runtime/compiler/optimizer/J9ValuePropagation.hpp index 467375cae51..3f11d45871a 100644 --- a/runtime/compiler/optimizer/J9ValuePropagation.hpp +++ b/runtime/compiler/optimizer/J9ValuePropagation.hpp @@ -76,14 +76,14 @@ class ValuePropagation : public OMR::ValuePropagation virtual TR_YesNoMaybe isValue(TR::VPConstraint *constraint, TR_OpaqueClassBlock *& clazz); /** - * Determine whether the component type of an array is, or might be, a primitive value - * type. + * Determine whether the array is, or might be, null-restricted + * * \param arrayConstraint The \ref TR::VPConstraint type constraint for the array reference - * \returns \c TR_yes if the array's component type is definitely a primitive value type;\n - * \c TR_no if it is definitely not a primitive value type; or\n + * \returns \c TR_yes if the array is definitely a null-restricted array;\n + * \c TR_no if it is definitely not a null-restricted array; or\n * \c TR_maybe otherwise. */ - virtual TR_YesNoMaybe isArrayCompTypePrimitiveValueType(TR::VPConstraint *arrayConstraint); + virtual TR_YesNoMaybe isArrayNullRestricted(TR::VPConstraint *arrayConstraint); /** * \brief diff --git a/runtime/compiler/runtime/JITClientSession.cpp b/runtime/compiler/runtime/JITClientSession.cpp index e2ca7bcd1f6..4f30a444d59 100644 --- a/runtime/compiler/runtime/JITClientSession.cpp +++ b/runtime/compiler/runtime/JITClientSession.cpp @@ -413,6 +413,7 @@ ClientSessionData::ClassInfo::ClassInfo(TR_PersistentMemory *persistentMemory) : _aotCacheClassRecord(NULL), _arrayElementSize(0), _defaultValueSlotAddress(NULL), + _nullRestrictedArrayClass(NULL), _classOfStaticCache(decltype(_classOfStaticCache)::allocator_type(persistentMemory->_persistentAllocator.get())), _constantClassPoolCache(decltype(_constantClassPoolCache)::allocator_type(persistentMemory->_persistentAllocator.get())), _fieldAttributesCache(decltype(_fieldAttributesCache)::allocator_type(persistentMemory->_persistentAllocator.get())), diff --git a/runtime/compiler/runtime/JITClientSession.hpp b/runtime/compiler/runtime/JITClientSession.hpp index e5c7a1f7988..4d0e8c6cff7 100644 --- a/runtime/compiler/runtime/JITClientSession.hpp +++ b/runtime/compiler/runtime/JITClientSession.hpp @@ -203,6 +203,7 @@ class ClientSessionData TR_OpaqueClassBlock *_hostClass; TR_OpaqueClassBlock *_componentClass; // caching the componentType of the J9ArrayClass TR_OpaqueClassBlock *_arrayClass; + TR_OpaqueClassBlock *_nullRestrictedArrayClass; uintptr_t _totalInstanceSize; J9ConstantPool *_constantPool; uintptr_t _classFlags; diff --git a/runtime/compiler/runtime/SymbolValidationManager.cpp b/runtime/compiler/runtime/SymbolValidationManager.cpp index 4d7cb23e238..45b5111d758 100644 --- a/runtime/compiler/runtime/SymbolValidationManager.cpp +++ b/runtime/compiler/runtime/SymbolValidationManager.cpp @@ -1252,7 +1252,11 @@ TR::SymbolValidationManager::validateArrayClassFromComponentClassRecord(uint16_t if (isDefinedID(componentClassID)) { TR_OpaqueClassBlock *componentClass = getClassFromID(componentClassID); - return validateSymbol(arrayClassID, _fej9->getArrayClassFromComponentClass(componentClass)); + if (validateSymbol(arrayClassID, _fej9->getArrayClassFromComponentClass(componentClass))) + return true; + + TR_OpaqueClassBlock *nullRestrictedArray = _fej9->getNullRestrictedArrayClassFromComponentClass(componentClass); + return nullRestrictedArray ? validateSymbol(arrayClassID, nullRestrictedArray) : false; } else {