diff --git a/runtime/vm/BytecodeInterpreter.hpp b/runtime/vm/BytecodeInterpreter.hpp index 2af88dc20c1..930f029ad95 100644 --- a/runtime/vm/BytecodeInterpreter.hpp +++ b/runtime/vm/BytecodeInterpreter.hpp @@ -1195,10 +1195,43 @@ class INTERPRETER_CLASS _currentThread->tempSlot = (UDATA)invalidIndex; rc = THROW_AIOB; } else { - I_32 value = VM_ArrayCopyHelpers::referenceArrayCopy(_currentThread, srcObject, srcStart, destObject, destStart, elementCount); - if (-1 != value) { - buildInternalNativeStackFrame(REGISTER_ARGS); - rc = THROW_ARRAY_STORE; +#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) + J9Class *srcClazz = J9OBJECT_CLAZZ(_currentThread, srcObject); + J9Class *destClazz = J9OBJECT_CLAZZ(_currentThread, destObject); + J9Class *srcComponentClass = ((J9ArrayClass *)srcClazz)->componentType; + J9Class *destComponentClass = ((J9ArrayClass *)destClazz)->componentType; + + if (J9_IS_J9CLASS_FLATTENED(srcClazz) && J9_IS_J9CLASS_FLATTENED(destClazz) && J9_ARE_NO_BITS_SET(srcComponentClass->classFlags, J9ClassHasReferences) && J9_ARE_NO_BITS_SET(destComponentClass->classFlags, J9ClassHasReferences)) { + if (srcClazz == destClazz) { + UDATA elementSize = J9ARRAYCLASS_GET_STRIDE(srcClazz); + /* This only works for contiguous flattened arrays, since discontiguous flattened arrays are not supported by GC */ + VM_ArrayCopyHelpers::primitiveArrayCopy(_currentThread, srcObject, srcStart * elementSize, destObject, destStart * elementSize, elementSize * elementCount, 0); + } else { + rc = THROW_ARRAY_STORE; + } + } else if (J9_IS_J9CLASS_FLATTENED(srcClazz) || J9_IS_J9CLASS_FLATTENED(destClazz) || J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(destComponentClass)) { + /* VM_ArrayCopyHelpers::referenceArrayCopy cannot handle flattened arrays or null elements being copied into arrays of primitive value types, so for those cases use copyFlattenableArray instead */ + buildGenericSpecialStackFrame(REGISTER_ARGS, 0); + updateVMStruct(REGISTER_ARGS); + I_32 value = VM_ValueTypeHelpers::copyFlattenableArray(_currentThread, _objectAccessBarrier, _objectAllocate, srcObject, destObject, srcStart, destStart, elementCount); + VMStructHasBeenUpdated(REGISTER_ARGS); + restoreGenericSpecialStackFrame(REGISTER_ARGS); + + if (-1 == value) { + buildInternalNativeStackFrame(REGISTER_ARGS); + rc = THROW_ARRAY_STORE; + } else if (-2 == value) { + buildInternalNativeStackFrame(REGISTER_ARGS); + rc = THROW_NPE; + } + } else +#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ + { + I_32 errorIndex = VM_ArrayCopyHelpers::referenceArrayCopy(_currentThread, srcObject, srcStart, destObject, destStart, elementCount); + if (-1 != errorIndex) { + buildInternalNativeStackFrame(REGISTER_ARGS); + rc = THROW_ARRAY_STORE; + } } } } diff --git a/runtime/vm/ValueTypeHelpers.hpp b/runtime/vm/ValueTypeHelpers.hpp index ecda6b684ce..b24eeb515a7 100644 --- a/runtime/vm/ValueTypeHelpers.hpp +++ b/runtime/vm/ValueTypeHelpers.hpp @@ -567,6 +567,101 @@ class VM_ValueTypeHelpers { return value; } + /** + * Copies an array of non-primitive objects + * Handles flattened and non-flattened cases + * A generic special stack frame must be built before calling this function + * + * Assumes srcObject and destObject are not null + * Assumes array bounds (srcIndex to (srcIndex + lengthInSlots), and destIndex to (destIndex + lengthInSlots)) are valid + * Assumes a generic special stack frame has been built on the stack + * + * @param[in] currentThread thread token + * @param[in] objectAccessBarrier access barrier + * @param[in] objectAllocate allocator + * @param[in] srcObject the source array to copy objects from + * @param[out] destObject the destination array in which objects should be stored + * @param[in] srcIndex the index in the source array to begin copying objects from + * @param[in] destIndex the index in the destination array to begin storing objects + * @param[in] lengthInSlots the number of elements to copy + * + * @return 0 if copy was successful, -1 if there was an array store error, -2 if there was a null pointer exception + */ + static VMINLINE I_32 + copyFlattenableArray(J9VMThread *currentThread, MM_ObjectAccessBarrierAPI objectAccessBarrier, MM_ObjectAllocationAPI objectAllocate, j9object_t srcObject, j9object_t destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots) + { + I_32 srcEndIndex = srcIndex + lengthInSlots; + J9Class *srcClazz = J9OBJECT_CLAZZ(currentThread, srcObject); + J9Class *destClazz = J9OBJECT_CLAZZ(currentThread, destObject); + J9Class *destComponentClass = ((J9ArrayClass *)destClazz)->componentType; + + /* Array elements must be copied backwards if source and destination overlap in memory and source is before destination */ + if ((srcObject == destObject) && (srcIndex < destIndex) && ((srcIndex + lengthInSlots) > destIndex)) { + srcEndIndex = srcIndex; + srcIndex += lengthInSlots; + destIndex += lengthInSlots; + + while (srcIndex > srcEndIndex) { + srcIndex--; + destIndex--; + + j9object_t copyObject = loadFlattenableArrayElement(currentThread, objectAccessBarrier, objectAllocate, srcObject, srcIndex, true); + + /* + When the return value of loadFlattenableArrayElement is NULL, 2 things are possible: + 1: The array element at that index is actually NULL + 2: There was an allocation failure + But loadFlattenableArrayElement only tries to allocate when srcClazz is flattened, and so if copyObject is NULL and srcClazz is flattened then there has been an allocation failure + */ + if ((NULL == copyObject) && J9_IS_J9CLASS_FLATTENED(srcClazz)) { + VM_VMHelpers::pushObjectInSpecialFrame(currentThread, srcObject); + copyObject = loadFlattenableArrayElement(currentThread, objectAccessBarrier, objectAllocate, srcObject, srcIndex, false); + srcObject = VM_VMHelpers::popObjectInSpecialFrame(currentThread); + } + + /* No type checks required since srcObject == destObject */ + + storeFlattenableArrayElement(currentThread, objectAccessBarrier, destObject, destIndex, copyObject); + } + } else { + + UDATA typeChecksRequired = !isSameOrSuperClassOf(destClazz, srcClazz); + + while (srcIndex < srcEndIndex) { + j9object_t copyObject = loadFlattenableArrayElement(currentThread, objectAccessBarrier, objectAllocate, srcObject, srcIndex, true); + + /* + When the return value of loadFlattenableArrayElement is NULL, 2 things are possible: + 1: The array element at that index is actually NULL + 2: There was an allocation failure + But loadFlattenableArrayElement only tries to allocate when srcClazz is flattened, and so if copyObject is NULL and srcClazz is flattened then there has been an allocation failure + */ + if ((NULL == copyObject) && J9_IS_J9CLASS_FLATTENED(srcClazz)) { + VM_VMHelpers::pushObjectInSpecialFrame(currentThread, srcObject); + copyObject = loadFlattenableArrayElement(currentThread, objectAccessBarrier, objectAllocate, srcObject, srcIndex, false); + srcObject = VM_VMHelpers::popObjectInSpecialFrame(currentThread); + } + + if (typeChecksRequired) { + if (J9_IS_J9CLASS_PRIMITIVE_VALUETYPE(destComponentClass) && (NULL == copyObject)) { + /* Null objects cannot be stored in an array of primitive value types */ + return -2; + } + if (!VM_VMHelpers::objectArrayStoreAllowed(currentThread, destObject, copyObject)) { + return -1; + } + } + + storeFlattenableArrayElement(currentThread, objectAccessBarrier, destObject, destIndex, copyObject); + + srcIndex++; + destIndex++; + } + } + + return 0; + } + }; #endif /* VALUETYPEHELPERS_HPP_ */