Skip to content

Commit

Permalink
ACMP compatibility for value types
Browse files Browse the repository at this point in the history
This commit adds the implementation to handle value types when the ACMP bytecode is used to compare object fields. An ACMP optimization is also added and depends on the flag defined in eclipse-openj9#7743.

Signed-off-by: Adithya Venkatarao <adi_101@live.com>
  • Loading branch information
XxAdi101xX committed Dec 19, 2019
1 parent f171734 commit 77efa38
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 25 deletions.
32 changes: 10 additions & 22 deletions runtime/vm/BytecodeInterpreter.hpp
Expand Up @@ -48,6 +48,7 @@
#include "MHInterpreter.hpp"
#include "ObjectAccessBarrierAPI.hpp"
#include "ObjectHash.hpp"
#include "valueTypeHelpers.hpp"
#include "VMHelpers.hpp"
#include "VMAccess.hpp"
#include "ObjectAllocationAPI.hpp"
Expand Down Expand Up @@ -7275,13 +7276,18 @@ done:;
} else if ((NULL == rhs) || (NULL == lhs)) {
acmpResult = false;
} else {
J9Class * lhsClass = J9OBJECT_CLAZZ(_currentThread, lhs);
J9Class * rhsClass = J9OBJECT_CLAZZ(_currentThread, rhs);
J9Class *lhsClass = J9OBJECT_CLAZZ(_currentThread, lhs);
J9Class *rhsClass = J9OBJECT_CLAZZ(_currentThread, rhs);
if ((J9_IS_J9CLASS_VALUETYPE(rhsClass)
&& J9_IS_J9CLASS_VALUETYPE(lhsClass))
&& (rhsClass == lhsClass)
) {
acmpResult = isSubstitutable(lhs, rhs);
/* If J9ClassCanSupportFastSubstitutability is set, we can use the barrier version of memcmp, else we recursively check the fields manually. */
if (J9_ARE_ALL_BITS_SET(lhsClass->classFlags, J9ClassCanSupportFastSubstitutability)) {
acmpResult = _objectAccessBarrier.structuralFlattenedCompareObjects(_currentThread, lhsClass, lhs, rhs, J9VMTHREAD_OBJECT_HEADER_SIZE(_currentThread));
} else {
acmpResult = ValueTypeHelpers::isSubstitutable(_currentThread, _vm, &_objectAccessBarrier, lhs, rhs, J9VMTHREAD_OBJECT_HEADER_SIZE(_currentThread), lhsClass);
}
}
}
return acmpResult;
Expand All @@ -7290,24 +7296,6 @@ done:;
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
}

#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
/*
* Determine if the two valueTypes are substitutable when rhs.class equals lhs.class
*
* @param[in] lhs the lhs object of acmp bytecodes and it's a valueType
* @param[in] rhs the rhs object of acmp bytecodes and it's a valueType
* return true if they are substitutable and false otherwise
*/
VMINLINE bool
isSubstitutable(j9object_t lhs, j9object_t rhs)
{
/*
* TODO: this will be updated in a future PR.
*/
return false;
}
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */

/* ..., lhs, rhs => ... */
VMINLINE VM_BytecodeAction
ifacmpeq(REGISTER_ARGS_LIST)
Expand Down Expand Up @@ -8465,7 +8453,7 @@ done:;
goto done;
}

copyObjectRef = _objectAllocate.inlineAllocateObject(_currentThread, objectRefClass, false, false);
copyObjectRef = _objectAllocate.inlineAllocateObject(_currentThread, objectRefClass, true, false);
if (NULL == copyObjectRef) {
buildGenericSpecialStackFrame(REGISTER_ARGS, 0);
pushObjectInSpecialFrame(REGISTER_ARGS, originalObjectRef);
Expand Down
6 changes: 3 additions & 3 deletions runtime/vm/createramclass.cpp
Expand Up @@ -3140,9 +3140,9 @@ internalCreateRAMClassFromROMClass(J9VMThread *vmThread, J9ClassLoader *classLoa
if ((result->totalInstanceSize <= javaVM->valueFlatteningThreshold) && !J9ROMCLASS_IS_CONTENDED(romClass)) {
Trc_VM_CreateRAMClassFromROMClass_valueTypeIsFlattened(vmThread, J9UTF8_LENGTH(className), J9UTF8_DATA(className), result);
classFlags |= J9ClassIsFlattened;
}
if (J9_ARE_ALL_BITS_SET(valueTypeFlags, J9ClassCanSupportFastSubstitutability)) {
classFlags |= J9ClassCanSupportFastSubstitutability;
if (J9_ARE_ALL_BITS_SET(valueTypeFlags, J9ClassCanSupportFastSubstitutability)) {
classFlags |= J9ClassCanSupportFastSubstitutability;
}
}

if (J9_ARE_ALL_BITS_SET(valueTypeFlags, J9ClassLargestAlignmentConstraintDouble)) {
Expand Down
208 changes: 208 additions & 0 deletions runtime/vm/valueTypeHelpers.hpp
@@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (c) 2019, 2019 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
* or the Apache License, Version 2.0 which accompanies this distribution and
* is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* This Source Code may also be made available under the following
* Secondary Licenses when the conditions for such availability set
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
* General Public License, version 2 with the GNU Classpath
* Exception [1] and GNU General Public License, version 2 with the
* OpenJDK Assembly Exception [2].
*
* [1] https://www.gnu.org/software/classpath/license.html
* [2] http://openjdk.java.net/legal/assembly-exception.html
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
*******************************************************************************/

#if !defined(VALUETYPEHELPERS_HPP_)
#define VALUETYPEHELPERS_HPP_

#include "j9.h"
#include "ObjectAccessBarrierAPI.hpp"
#include "fltconst.h"
class ValueTypeHelpers
{
/*
* Data members
*/
private:

protected:

public:

/*
* Function members
*/
private:

protected:

public:
static VMINLINE bool
checkDoubleEquality(U_64 a, U_64 b)
{
jdouble adouble = *(jdouble*)&a;
jdouble bdouble = *(jdouble*)&b;
bool result = false;

if (adouble == bdouble) {
result = true;
} else if (IS_NAN_DBL(adouble) && IS_NAN_DBL(bdouble)) {
result = true;
}
return result;
}

static VMINLINE bool
checkFloatEquality(U_32 a, U_32 b)
{
jfloat afloat = *(jfloat*)&a;
jfloat bfloat = *(jfloat*)&b;
bool result = false;

if (afloat == bfloat) {
result = true;
} else if (IS_NAN_SNGL(afloat) && IS_NAN_SNGL(bfloat)) {
result = true;
}
return result;
}

/*
* Determine if the two valueTypes are substitutable when rhs.class equals lhs.class
*
* @param[in] lhs the lhs object address
* @param[in] rhs the rhs object address
* @param[in] startOffset the initial offset for the object
* @param[in] clazz the value type class
* return true if they are substitutable and false otherwise
*/
static bool
isSubstitutable(
J9VMThread *const _currentThread,
J9JavaVM *const _vm,
MM_ObjectAccessBarrierAPI *_objectAccessBarrier,
j9object_t lhs,
j9object_t rhs,
UDATA startOffset,
J9Class *clazz
) {
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
U_32 walkFlags = J9VM_FIELD_OFFSET_WALK_INCLUDE_INSTANCE;
J9ROMFieldOffsetWalkState state;
J9ROMFieldOffsetWalkResult *result = fieldOffsetsStartDo(_vm, clazz->romClass, VM_VMHelpers::getSuperclass(clazz), &state, walkFlags, clazz->flattenedClassCache);

while (NULL != result->field) {
J9UTF8 *signature = J9ROMNAMEANDSIGNATURE_SIGNATURE(&result->field->nameAndSignature);
U_8 *sigChar = J9UTF8_DATA(signature);

switch (*sigChar) {
case 'Z': /* Boolean */
case 'B': /* Byte */
case 'C': /* Char */
case 'I': /* Int */
case 'S': { /* Short */
I_32 lhsValue = _objectAccessBarrier->inlineMixedObjectReadI32(_currentThread, lhs, startOffset + result->offset);
I_32 rhsValue = _objectAccessBarrier->inlineMixedObjectReadI32(_currentThread, rhs, startOffset + result->offset);
if (lhsValue != rhsValue) {
return false;
}
break;
}
case 'J': { /* Long */
I_64 lhsValue = _objectAccessBarrier->inlineMixedObjectReadI64(_currentThread, lhs, startOffset + result->offset);
I_64 rhsValue = _objectAccessBarrier->inlineMixedObjectReadI64(_currentThread, rhs, startOffset + result->offset);
if (lhsValue != rhsValue) {
return false;
}
break;
}
case 'D': { /* Double */
U_64 lhsValue = _objectAccessBarrier->inlineMixedObjectReadU64(_currentThread, lhs, startOffset + result->offset);
U_64 rhsValue = _objectAccessBarrier->inlineMixedObjectReadU64(_currentThread, rhs, startOffset + result->offset);
bool result = checkDoubleEquality(lhsValue, rhsValue);
if (!result) {
return false;
}
break;
}
case 'F': { /* Float */
U_32 lhsValue = _objectAccessBarrier->inlineMixedObjectReadU32(_currentThread, lhs, startOffset + result->offset);
U_32 rhsValue = _objectAccessBarrier->inlineMixedObjectReadU32(_currentThread, rhs, startOffset + result->offset);
bool result = checkFloatEquality(lhsValue, rhsValue);
if (!result) {
return false;
}
break;
}
case '[': { /* Array */
j9object_t lhsObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, lhs, startOffset + result->offset);
j9object_t rhsObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, rhs, startOffset + result->offset);
if (lhsObject != rhsObject) {
return false;
}
break;
}
case 'L': { /* Nullable class type or interface type */
j9object_t lhsObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, lhs, startOffset + result->offset);
j9object_t rhsObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, rhs, startOffset + result->offset);
if (!J9_IS_J9CLASS_VALUETYPE(J9OBJECT_CLAZZ(_currentThread, lhsObject))) {
if (lhsObject != rhsObject) {
return false;
}
break;
}
/* Fall through if we find this to be a value type at runtime */
}
case 'Q': { /* Null-free class type */
J9Class *fieldClass = findJ9ClassInFlattenedClassCache(clazz->flattenedClassCache, sigChar + 1, J9UTF8_LENGTH(signature) - 2);
bool recursiveResult = false;

if (J9_IS_J9CLASS_FLATTENED(fieldClass)) {
/* If J9ClassCanSupportFastSubstitutability is set, we can use the barrier version of memcmp, else we recursively check the fields manually. */
if (J9_ARE_ALL_BITS_SET(fieldClass->classFlags, J9ClassCanSupportFastSubstitutability)) {
if (!_objectAccessBarrier->structuralFlattenedCompareObjects(_currentThread, fieldClass, lhs, rhs, startOffset + result->offset)) {
return false;
}
break;
} else {
recursiveResult = isSubstitutable(_currentThread, _vm, _objectAccessBarrier, lhs, rhs, startOffset + result->offset, fieldClass);
}
} else {
/* When unflattened, we get our object from the specified offset, then increment past the header to the first field. */
j9object_t lhsFieldObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, lhs, startOffset + result->offset);
j9object_t rhsFieldObject = _objectAccessBarrier->inlineMixedObjectReadObject(_currentThread, rhs, startOffset + result->offset);
if (NULL != lhsFieldObject && NULL != rhsFieldObject) {
recursiveResult = isSubstitutable(_currentThread, _vm, _objectAccessBarrier, lhsFieldObject, rhsFieldObject, J9VMTHREAD_OBJECT_HEADER_SIZE(_currentThread), fieldClass);
} else {
/* Either both lhsFieldObject and rhsFieldObject are null or only one of them is null */
recursiveResult = (lhsFieldObject == rhsFieldObject);
}
}

if (!recursiveResult) {
return false;
}
break;
}
default:
Assert_VM_unreachable();
} /* switch */

result = fieldOffsetsNextDo(&state);
}
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */

return true;
}

};

#endif /* VALUETYPEHELPERS_HPP_ */

0 comments on commit 77efa38

Please sign in to comment.