From 0709034de7289a3238e3635a94de23d9358059bb Mon Sep 17 00:00:00 2001 From: Yan Luo Date: Mon, 3 Dec 2018 13:29:19 -0500 Subject: [PATCH] Implement X86 codegen support for dynamic array clone Dynamic array cloning transforms refArrayObject.clone() into `anewarray` followed by `arraycopy`, where refArrayObject is a resolved, not fixed class. For code gen, It requires the class (2nd) child of anewarray to be runtime evaluated instead of compile time constant, as actual refArrayObject type is not known at compiled time (other than it's an ref array). This change implements support in X86 codegen to handle a runtime evaluated class child of anewarray. This is prerequisite for the optimizer change that performs the object.clone() call transformation. Signed-off-by: Yan Luo --- runtime/compiler/compile/J9Compilation.cpp | 9 +- .../compiler/x/codegen/J9TreeEvaluator.cpp | 92 ++++++++++++------- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/runtime/compiler/compile/J9Compilation.cpp b/runtime/compiler/compile/J9Compilation.cpp index 3bfde4cf0fb..adf8b7edc0a 100644 --- a/runtime/compiler/compile/J9Compilation.cpp +++ b/runtime/compiler/compile/J9Compilation.cpp @@ -648,8 +648,15 @@ J9::Compilation::canAllocateInline(TR::Node* node, TR_OpaqueClassBlock* &classIn else if (node->getOpCodeValue() == TR::anewarray) { classRef = node->getSecondChild(); - classSymRef = classRef->getSymbolReference(); + // In the case of dynamic array allocation, return 0 indicating variable dynamic array allocation + if (classRef->getOpCodeValue() != TR::loadaddr) + { + classInfo = NULL; + return 0; + } + + classSymRef = classRef->getSymbolReference(); // Can't skip the allocation if the class is unresolved // clazz = self()->fej9vm()->getClassForAllocationInlining(self(), classSymRef); diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp index d707f8bf94c..6a9405d93b5 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp @@ -6558,6 +6558,7 @@ static void genInitObjectHeader(TR::Node *node, TR::Register *objectReg, TR::Register *tempReg, bool isZeroInitialized, + bool isDynamicAllocation, TR::CodeGenerator *cg) { TR::Compilation *comp = cg->comp(); @@ -6567,12 +6568,7 @@ static void genInitObjectHeader(TR::Node *node, (!TR::Compiler->om.generateCompressedObjectHeaders() || (cg->comp()->compileRelocatableCode() && cg->comp()->getOption(TR_UseSymbolValidationManager))); - // This code was moved to this point so that the romClass can be used by the AOT - // portion without calling the method again. - // - J9ROMClass *romClass = 0; - TR_ASSERT(clazz, "Cannot have a null OpaqueClassBlock\n"); - romClass = TR::Compiler->cls.romClassOf(clazz); + TR_ASSERT((isDynamicAllocation || clazz), "Cannot have a null clazz while not doing dynamic array allocation\n"); // -------------------------------------------------------------------------------- // @@ -6584,8 +6580,16 @@ static void genInitObjectHeader(TR::Node *node, TR::Register * clzReg = classReg; + // For dynamic array allocation, load the array class from the component class and store into clzReg + if (isDynamicAllocation) + { + TR_ASSERT((node->getOpCodeValue() == TR::anewarray), "Dynamic allocation currently only supports reference arrays"); + TR_ASSERT(classReg, "must have a classReg for dynamic allocation"); + clzReg = tempReg; + generateRegMemInstruction(LRegMem(), node, clzReg, generateX86MemoryReference(classReg, offsetof(J9Class, arrayClass), cg), cg); + } // TODO: should be able to use a TR_ClassPointer relocation without this stuff (along with class validation) - if (cg->needClassAndMethodPointerRelocations() && !comp->getOption(TR_UseSymbolValidationManager)) + else if (cg->needClassAndMethodPointerRelocations() && !comp->getOption(TR_UseSymbolValidationManager)) { TR::Register *vmThreadReg = cg->getVMThreadRegister(); if (node->getOpCodeValue() == TR::newarray) @@ -6601,13 +6605,6 @@ static void genInitObjectHeader(TR::Node *node, tempReg, cg); clzReg = tempReg; } - else if (node->getOpCodeValue() == TR::anewarray) - { - TR_ASSERT(classReg, "must have a classReg for TR::anewarray in AOT mode"); - generateRegMemInstruction(LRegMem(), node, tempReg, - generateX86MemoryReference(classReg, offsetof(J9Class, arrayClass), cg), cg); - clzReg = tempReg; - } else { TR_ASSERT((node->getOpCodeValue() == TR::New) @@ -6661,6 +6658,7 @@ static void genInitObjectHeader(TR::Node *node, #ifndef J9VM_INTERP_FLAGS_IN_CLASS_SLOT // Enable macro once GC-Helper is fixed + J9ROMClass *romClass = TR::Compiler->cls.romClassOf(clazz); if (romClass) { orFlags |= romClass->instanceShape; @@ -6682,23 +6680,39 @@ static void genInitObjectHeader(TR::Node *node, // // -------------------------------------------------------------------------------- // - J9Class *j9class = TR::Compiler->cls.convertClassOffsetToClassPtr(clazz); - bool initReservable = J9CLASS_EXTENDED_FLAGS(j9class) & J9ClassReservableLockWordInit; - if (!isZeroInitialized || initReservable) + // For dynamic array allocation, in case (very unlikely) the object array has a lock word, we just initialized it to 0 conservatively. + // In this case, if the original array is reserved, initializating the cloned object's lock word to 0 will force the + // locking to go to the slow locking path. + if (isDynamicAllocation) + { + TR::LabelSymbol *doneLabel = generateLabelSymbol(cg); + generateRegMemInstruction(LRegMem(), node, tempReg, generateX86MemoryReference(clzReg, offsetof(J9ArrayClass, lockOffset), cg), cg); + generateRegImmInstruction(CMPRegImm4(), node, tempReg, (int32_t)-1, cg); + generateLabelInstruction (JE4, node, doneLabel, cg); + generateMemImmInstruction(SMemImm4(TR::Compiler->target.is64Bit() && !fej9->generateCompressedLockWord()), + node, generateX86MemoryReference(objectReg, tempReg, 0, cg), 0, cg); + generateLabelInstruction(LABEL, node, doneLabel, cg); + } + else { - bool initLw = (node->getOpCodeValue() != TR::New) || initReservable; - int lwOffset = fej9->getByteOffsetToLockword(clazz); - if (lwOffset == -1) - initLw = false; - - if (initLw) + J9Class *j9class = TR::Compiler->cls.convertClassOffsetToClassPtr(clazz); + bool initReservable = J9CLASS_EXTENDED_FLAGS(j9class) & J9ClassReservableLockWordInit; + if (!isZeroInitialized || initReservable) { - int32_t initialLwValue = 0; - if (initReservable) - initialLwValue = OBJECT_HEADER_LOCK_RESERVED; + bool initLw = (node->getOpCodeValue() != TR::New) || initReservable; + int lwOffset = fej9->getByteOffsetToLockword(clazz); + if (lwOffset == -1) + initLw = false; - TR_X86OpCodes op = (TR::Compiler->target.is64Bit() && fej9->generateCompressedLockWord()) ? S4MemImm4 : SMemImm4(); - generateMemImmInstruction(op, node, generateX86MemoryReference(objectReg, lwOffset, cg), initialLwValue, cg); + if (initLw) + { + int32_t initialLwValue = 0; + if (initReservable) + initialLwValue = OBJECT_HEADER_LOCK_RESERVED; + + generateMemImmInstruction(SMemImm4(TR::Compiler->target.is64Bit() && !fej9->generateCompressedLockWord()), + node, generateX86MemoryReference(objectReg, lwOffset, cg), initialLwValue, cg); + } } } } @@ -6716,13 +6730,14 @@ static void genInitArrayHeader( int32_t arrayletDataOffset, TR::Register *tempReg, bool isZeroInitialized, + bool isDynamicAllocation, TR::CodeGenerator *cg) { TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg->fe()); // Initialize the object header // - genInitObjectHeader(node, clazz, classReg, objectReg, tempReg, isZeroInitialized, cg); + genInitObjectHeader(node, clazz, classReg, objectReg, tempReg, isZeroInitialized, isDynamicAllocation, cg); int32_t arraySizeOffset = fej9->getOffsetOfContiguousArraySizeField(); @@ -7293,7 +7308,6 @@ J9::X86::TreeEvaluator::VMnewEvaluator( TR_OpaqueClassBlock *clazz = NULL; TR::Register *classReg = NULL; bool isArrayNew = false; - int32_t allocationSize = 0; int32_t objectSize = 0; int32_t elementSize = 0; @@ -7347,7 +7361,11 @@ J9::X86::TreeEvaluator::VMnewEvaluator( objectSize = comp->canAllocateInline(node, clazz); if (objectSize < 0) return NULL; - + // Currently dynamic allocation is only supported on reference array. + // We are performing dynamic array allocation if both object size and + // class block cannot be statically determined. + bool dynamicArrayAllocation = (node->getOpCodeValue() == TR::anewarray) + && (objectSize == 0) && (clazz == NULL); allocationSize = objectSize; static long count = 0; @@ -7392,6 +7410,9 @@ J9::X86::TreeEvaluator::VMnewEvaluator( elementSize = (int32_t)TR::Compiler->om.sizeofReferenceAddress(); classReg = node->getSecondChild()->getRegister(); + // For dynamic array allocation, need to evaluate second child + if (!classReg && dynamicArrayAllocation) + classReg = cg->evaluate(node->getSecondChild()); } isArrayNew = true; @@ -7683,9 +7704,8 @@ J9::X86::TreeEvaluator::VMnewEvaluator( // -------------------------------------------------------------------------------- // Initialize the header // -------------------------------------------------------------------------------- - if (fej9->inlinedAllocationsMustBeVerified() - && !comp->getOption(TR_UseSymbolValidationManager) - && node->getOpCodeValue() == TR::anewarray) + // If dynamic array allocation, must pass in classReg to initialize the array header + if ((fej9->inlinedAllocationsMustBeVerified() && !comp->getOption(TR_UseSymbolValidationManager) && node->getOpCodeValue() == TR::anewarray) || dynamicArrayAllocation) { genInitArrayHeader( node, @@ -7697,6 +7717,7 @@ J9::X86::TreeEvaluator::VMnewEvaluator( dataOffset, tempReg, monitorSlotIsInitialized, + true, cg); } else if (isArrayNew) @@ -7711,11 +7732,12 @@ J9::X86::TreeEvaluator::VMnewEvaluator( dataOffset, tempReg, monitorSlotIsInitialized, + false, cg); } else { - genInitObjectHeader(node, clazz, classReg, targetReg, tempReg, monitorSlotIsInitialized, cg); + genInitObjectHeader(node, clazz, classReg, targetReg, tempReg, monitorSlotIsInitialized, false, cg); } if (fej9->inlinedAllocationsMustBeVerified() && (node->getOpCodeValue() == TR::New ||