From 05d5976831d528f2422cf28eb449a2ca03a0e970 Mon Sep 17 00:00:00 2001 From: Annabelle Huo Date: Thu, 3 Dec 2020 15:40:44 -0500 Subject: [PATCH 1/2] Add utility functions in JIT for value based classes Add `areValueBasedMonitorChecksEnabled` which calls VM to check if value based class monitor check is enabled. Add `isValueBasedClass` and `isMonitorValueBased` to determine if a monitor object is a value based class instance. Related to JEP390 Signed-off-by: Annabelle Huo --- runtime/compiler/codegen/J9CodeGenerator.cpp | 26 ++++++++++++++++++-- runtime/compiler/codegen/J9CodeGenerator.hpp | 10 ++++++++ runtime/compiler/env/J9ClassEnv.cpp | 23 +++++++++++++++++ runtime/compiler/env/J9ClassEnv.hpp | 1 + runtime/compiler/env/J9ObjectModel.cpp | 16 ++++++++++++ runtime/compiler/env/J9ObjectModel.hpp | 4 +++ 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/runtime/compiler/codegen/J9CodeGenerator.cpp b/runtime/compiler/codegen/J9CodeGenerator.cpp index 4bbc6c2d06e..3f3f3c24ad6 100644 --- a/runtime/compiler/codegen/J9CodeGenerator.cpp +++ b/runtime/compiler/codegen/J9CodeGenerator.cpp @@ -5426,10 +5426,11 @@ J9::CodeGenerator::getMonClass(TR::Node* monNode) TR_YesNoMaybe J9::CodeGenerator::isMonitorValueType(TR::Node* monNode) { - if (_monitorMapping.find(monNode->getGlobalIndex()) == _monitorMapping.end()) + TR_OpaqueClassBlock *clazz = self()->getMonClass(monNode); + + if (!clazz) return TR_maybe; - TR_OpaqueClassBlock *clazz = _monitorMapping[monNode->getGlobalIndex()]; //java.lang.Object class is only set when monitor is java.lang.Object but not its subclass if (clazz == self()->comp()->getObjectClassPointer()) return TR_no; @@ -5442,3 +5443,24 @@ J9::CodeGenerator::isMonitorValueType(TR::Node* monNode) return TR_no; } + +TR_YesNoMaybe +J9::CodeGenerator::isMonitorValueBasedOrValueType(TR::Node* monNode) + { + TR_OpaqueClassBlock *clazz = self()->getMonClass(monNode); + + if (!clazz) + return TR_maybe; + + //java.lang.Object class is only set when monitor is java.lang.Object but not its subclass + if (clazz == self()->comp()->getObjectClassPointer()) + return TR_no; + + if (!TR::Compiler->cls.isConcreteClass(self()->comp(), clazz)) + return TR_maybe; + + if (TR::Compiler->cls.isValueBasedOrValueTypeClass(clazz)) + return TR_yes; + + return TR_no; + } diff --git a/runtime/compiler/codegen/J9CodeGenerator.hpp b/runtime/compiler/codegen/J9CodeGenerator.hpp index b76bbd4a34d..64ceb7a6809 100644 --- a/runtime/compiler/codegen/J9CodeGenerator.hpp +++ b/runtime/compiler/codegen/J9CodeGenerator.hpp @@ -332,6 +332,16 @@ class OMR_EXTENSIBLE CodeGenerator : public OMR::CodeGeneratorConnector * TR_maybe It is unknown whether the monitor object is identity type or value type */ TR_YesNoMaybe isMonitorValueType(TR::Node* monNode); + /* + * \brief + * Whether a monitor object is of value based class type or value type + * + * \return + * TR_yes The monitor object is definitely value based class type or value type + * TR_no The monitor object is definitely not value based class type or value type + * TR_maybe It is unknown whether the monitor object is value based class type or value type + */ + TR_YesNoMaybe isMonitorValueBasedOrValueType(TR::Node* monNode); protected: diff --git a/runtime/compiler/env/J9ClassEnv.cpp b/runtime/compiler/env/J9ClassEnv.cpp index 22b94a939eb..92331838855 100644 --- a/runtime/compiler/env/J9ClassEnv.cpp +++ b/runtime/compiler/env/J9ClassEnv.cpp @@ -825,6 +825,29 @@ J9::ClassEnv::isValueTypeClassFlattened(TR_OpaqueClassBlock *clazz) return (clazz && J9_IS_J9CLASS_FLATTENED(reinterpret_cast(clazz))); } +bool +J9::ClassEnv::isValueBasedOrValueTypeClass(TR_OpaqueClassBlock *clazz) + { +#if defined(J9VM_OPT_JITSERVER) + if (auto stream = TR::CompilationInfo::getStream()) + { + uintptr_t classFlags = 0; + JITServerHelpers::getAndCacheRAMClassInfo((J9Class *)clazz, 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 & J9_CLASS_DISALLOWS_LOCKING_FLAGS; + classFlagsRemote = classFlagsRemote & J9_CLASS_DISALLOWS_LOCKING_FLAGS; + TR_ASSERT_FATAL(classFlags == classFlagsRemote, "remote call class flags is not equal to cached class flags"); +#endif + return J9_ARE_ANY_BITS_SET(classFlags, J9_CLASS_DISALLOWS_LOCKING_FLAGS); + } +#endif /* defined(J9VM_OPT_JITSERVER) */ + J9Class *j9class = reinterpret_cast(clazz); + return J9_ARE_ANY_BITS_SET(j9class->classFlags, J9_CLASS_DISALLOWS_LOCKING_FLAGS); + } + bool J9::ClassEnv::isZeroInitializable(TR_OpaqueClassBlock *clazz) { diff --git a/runtime/compiler/env/J9ClassEnv.hpp b/runtime/compiler/env/J9ClassEnv.hpp index 51677614f00..4a3476dcf50 100644 --- a/runtime/compiler/env/J9ClassEnv.hpp +++ b/runtime/compiler/env/J9ClassEnv.hpp @@ -93,6 +93,7 @@ class OMR_EXTENSIBLE ClassEnv : public OMR::ClassEnvConnector bool isConcreteClass(TR::Compilation *comp, TR_OpaqueClassBlock * clazzPointer); bool isValueTypeClass(TR_OpaqueClassBlock *); bool isValueTypeClassFlattened(TR_OpaqueClassBlock *clazz); + bool isValueBasedOrValueTypeClass(TR_OpaqueClassBlock *); /** * \brief diff --git a/runtime/compiler/env/J9ObjectModel.cpp b/runtime/compiler/env/J9ObjectModel.cpp index e0127b93c79..730343c3945 100644 --- a/runtime/compiler/env/J9ObjectModel.cpp +++ b/runtime/compiler/env/J9ObjectModel.cpp @@ -116,6 +116,22 @@ J9::ObjectModel::areValueTypesEnabled() } +bool +J9::ObjectModel::areValueBasedMonitorChecksEnabled() + { +#if defined(J9VM_OPT_JITSERVER) + if (auto stream = TR::CompilationInfo::getStream()) + { + auto *vmInfo = TR::compInfoPT->getClientData()->getOrCacheVMInfo(stream); + return J9_ARE_ANY_BITS_SET(vmInfo->_extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_VALUE_BASED_EXCEPTION | J9_EXTENDED_RUNTIME2_VALUE_BASED_WARNING); + } +#endif /* defined(J9VM_OPT_JITSERVER) */ + + J9JavaVM * javaVM = TR::Compiler->javaVM; + return javaVM->internalVMFunctions->areValueBasedMonitorChecksEnabled(javaVM); + } + + int32_t J9::ObjectModel::sizeofReferenceField() { diff --git a/runtime/compiler/env/J9ObjectModel.hpp b/runtime/compiler/env/J9ObjectModel.hpp index 63cd5e9eee1..74a67314d80 100644 --- a/runtime/compiler/env/J9ObjectModel.hpp +++ b/runtime/compiler/env/J9ObjectModel.hpp @@ -62,6 +62,10 @@ class ObjectModel : public OMR::ObjectModelConnector bool mayRequireSpineChecks(); bool areValueTypesEnabled(); + /** + * @brief Whether the check is enabled on monitor object being value based class type + */ + bool areValueBasedMonitorChecksEnabled(); int32_t sizeofReferenceField(); bool isHotReferenceFieldRequired(); From 881cddc6feda6b6c33fa1f1644d4fe5170088e0f Mon Sep 17 00:00:00 2001 From: Annabelle Huo Date: Thu, 3 Dec 2020 15:52:13 -0500 Subject: [PATCH 2/2] Handle monitor enter/exit on value based instances in x86 If the monitor object type is value based class, VM will either issue a warning or throw an exception based on -XX:ValueBasedClassCheck option. If the monitor object type is unkonwn, insert a runtime memory check in monitor enter/exit on the class flag J9ClassIsValueBased. If the monitor object type is not value based class, proceed as how it is handled today. Value based class and value type are mutually exclusive. Related to JEP390 Signed-off-by: Annabelle Huo --- .../compiler/x/codegen/J9TreeEvaluator.cpp | 40 +++++++++++++------ .../compiler/x/codegen/J9TreeEvaluator.hpp | 13 +++--- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp index 6f6ac149db4..50c11fe5010 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp @@ -133,7 +133,8 @@ inline void generateLoadJ9Class(TR::Node* node, TR::Register* j9class, TR::Regis { case TR::monent: case TR::monexit: - TR_ASSERT_FATAL(TR::Compiler->om.areValueTypesEnabled(), "monent and monexit are expected for generateLoadJ9Class only when value type is enabled"); + TR_ASSERT_FATAL(TR::Compiler->om.areValueTypesEnabled() || TR::Compiler->om.areValueBasedMonitorChecksEnabled(), + "monent and monexit are expected for generateLoadJ9Class only when value type or when value based monitor check is enabled"); case TR::checkcastAndNULLCHK: needsNULLCHK = true; break; @@ -4482,21 +4483,26 @@ void J9::X86::TreeEvaluator::transactionalMemoryJITMonitorEntry(TR::Node } void -J9::X86::TreeEvaluator::generateCheckForValueTypeMonitorEnterOrExit( +J9::X86::TreeEvaluator::generateCheckForValueMonitorEnterOrExit( TR::Node *node, + int32_t classFlag, TR::LabelSymbol *snippetLabel, TR::CodeGenerator *cg) { - if (cg->isMonitorValueType(node) != TR_maybe) - return; TR::Register *objectReg = cg->evaluate(node->getFirstChild()); TR::Register *j9classReg = cg->allocateRegister(); generateLoadJ9Class(node, j9classReg, objectReg, cg); auto fej9 = (TR_J9VMBase *)(cg->fe()); TR::MemoryReference *classFlagsMR = generateX86MemoryReference(j9classReg, (uintptr_t)(fej9->getOffsetOfClassFlags()), cg); - static_assert((uint32_t) J9ClassIsValueType < USHRT_MAX, "Expecting J9ClassIsValueType to be less than 16 bits for use in test instruction"); - //test [j9classReg.classFlags], J9ClassIsValueType - generateMemImmInstruction(TEST2MemImm2, node, classFlagsMR, J9ClassIsValueType, cg); + + //test [j9classReg.classFlags], J9ClassIsValueType or J9ClassIsValueBased + TR_X86OpCodes testOpCode; + if ((uint32_t)classFlag <= USHRT_MAX) + testOpCode = TEST2MemImm2; + else + testOpCode = TEST4MemImm4; + + generateMemImmInstruction(testOpCode, node, classFlagsMR, classFlag, cg); generateLabelInstruction(JNE4, node, snippetLabel, cg); } @@ -4525,7 +4531,8 @@ J9::X86::TreeEvaluator::VMmonentEvaluator( if (comp->getOption(TR_MimicInterpreterFrameShape) || (comp->getOption(TR_FullSpeedDebug) && node->isSyncMethodMonitor()) || noInline || - TR::Compiler->om.areValueTypesEnabled() && cg->isMonitorValueType(node) == TR_yes || + ((TR::Compiler->om.areValueTypesEnabled() || TR::Compiler->om.areValueBasedMonitorChecksEnabled()) && + (cg->isMonitorValueBasedOrValueType(node) == TR_yes)) || comp->getOption(TR_DisableInlineMonEnt) || (firstMonEnt && (*firstMonEnt-'0') > monEntCount++)) { @@ -4590,8 +4597,11 @@ J9::X86::TreeEvaluator::VMmonentEvaluator( TR::SymbolReference *originalNodeSymRef = NULL; TR::Node *helperCallNode = node; - if (TR::Compiler->om.areValueTypesEnabled()) - TR::TreeEvaluator::generateCheckForValueTypeMonitorEnterOrExit(node, snippetLabel, cg); + + if ((TR::Compiler->om.areValueTypesEnabled() || TR::Compiler->om.areValueBasedMonitorChecksEnabled()) + && (cg->isMonitorValueBasedOrValueType(node) == TR_maybe)) + TR::TreeEvaluator::generateCheckForValueMonitorEnterOrExit(node, J9_CLASS_DISALLOWS_LOCKING_FLAGS, snippetLabel, cg); + if (comp->getOption(TR_ReservingLocks)) { // About to change the node's symref... store the original. @@ -5143,7 +5153,8 @@ TR::Register if ((comp->getOption(TR_MimicInterpreterFrameShape) /*&& !comp->getOption(TR_EnableLiveMonitorMetadata)*/) || noInline || - TR::Compiler->om.areValueTypesEnabled() && cg->isMonitorValueType(node) == TR_yes || + ((TR::Compiler->om.areValueTypesEnabled() || TR::Compiler->om.areValueBasedMonitorChecksEnabled()) && + (cg->isMonitorValueBasedOrValueType(node) == TR_yes)) || comp->getOption(TR_DisableInlineMonExit) || (firstMonExit && (*firstMonExit-'0') > monExitCount++)) { @@ -5193,8 +5204,11 @@ TR::Register TR::LabelSymbol *fallThru = generateLabelSymbol(cg); // Create the monitor exit snippet TR::LabelSymbol *snippetLabel = generateLabelSymbol(cg); - if (TR::Compiler->om.areValueTypesEnabled()) - TR::TreeEvaluator::generateCheckForValueTypeMonitorEnterOrExit(node, snippetLabel, cg); + + if ((TR::Compiler->om.areValueTypesEnabled() || TR::Compiler->om.areValueBasedMonitorChecksEnabled()) + && (cg->isMonitorValueBasedOrValueType(node) == TR_maybe)) + TR::TreeEvaluator::generateCheckForValueMonitorEnterOrExit(node, J9_CLASS_DISALLOWS_LOCKING_FLAGS, snippetLabel, cg); + #if !defined(J9VM_OPT_REAL_TIME_LOCKING_SUPPORT) // Now that the object reference has been generated, see if this is the end // of a small synchronized block. diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.hpp b/runtime/compiler/x/codegen/J9TreeEvaluator.hpp index 49d65165597..9dd0b4fa519 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.hpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.hpp @@ -93,7 +93,8 @@ class OMR_EXTENSIBLE TreeEvaluator: public J9::TreeEvaluator /* * \brief - * Generates the sequence to handle cases where the monitor object is value type + * Generates the sequence to handle cases where the monitor object + * is value type or value based class type * * \param node * the monitor enter/exit node @@ -102,14 +103,16 @@ class OMR_EXTENSIBLE TreeEvaluator: public J9::TreeEvaluator * the label for OOL code calling VM monitor enter/exit helpers * * \details - * Call the VM helper if it's detected at runtime that the monitor object is value type. - * The VM helper throws appropriate IllegalMonitorStateException. + * Call the VM helper if it's detected at runtime that the monitor object + * is value type or value based class type. + * The VM helper throws appropriate IllegalMonitorStateException for value type + * and VirtualMachineError for value based class type. * * \note * This method only handles the cases where, at compile time, it's unknown whether the - * object is reference type or value type. + * object is reference type or value type or value based class type. */ - static void generateCheckForValueTypeMonitorEnterOrExit(TR::Node *node, TR::LabelSymbol *snippetLabel, TR::CodeGenerator *cg); + static void generateCheckForValueMonitorEnterOrExit(TR::Node *node, int32_t classFlag, TR::LabelSymbol *snippetLabel, TR::CodeGenerator *cg); static void transactionalMemoryJITMonitorEntry(TR::Node *node, TR::CodeGenerator *cg, TR::LabelSymbol *startLabel, TR::LabelSymbol *snippetLabel, TR::LabelSymbol *JITMonitorEnterSnippetLabel, TR::Register *objectReg, int lwoffset); static void generateValueTracingCode(TR::Node *node, TR::Register *vmThreadReg, TR::Register *scratchReg, TR::Register *valueRegHigh, TR::Register *valueRegLow, TR::CodeGenerator *cg); static void generateValueTracingCode(TR::Node *node, TR::Register *vmThreadReg, TR::Register *scratchReg, TR::Register *valueReg, TR::CodeGenerator *cg);