Skip to content

Iterative implementation for isSubstitutable#23445

Open
AditiS11 wants to merge 1 commit intoeclipse-openj9:masterfrom
AditiS11:iterative_impl
Open

Iterative implementation for isSubstitutable#23445
AditiS11 wants to merge 1 commit intoeclipse-openj9:masterfrom
AditiS11:iterative_impl

Conversation

@AditiS11
Copy link
Copy Markdown

@AditiS11 AditiS11 commented Mar 4, 2026

Replace recursive approach to traverse fields.
Modify isSubstitutable to iteratively traverse the fields.
Use BFS with a queue to handle nested flattened value types.

Related: #15768

@AditiS11
Copy link
Copy Markdown
Author

AditiS11 commented Mar 4, 2026

Creating a single helper function for field iteration would require two-passes of the stack, first collecting all fields into a stack, then performing the comparison. I thought that this would increase the memory overhead.
The current approach performs comparison as fields are discovered during traversal.
Hence I thought of creating separate helper functions for isSubstitutable and hash code calculation, each maintaining its own stack for field traversal.

@theresa-m theresa-m self-requested a review March 4, 2026 13:59
@theresa-m theresa-m added comp:vm project:valhalla Used to track Project Valhalla related work labels Mar 4, 2026
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated

struct FieldComparisonFrame
{
j9object_t lhs;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of lhs and rhs appear to be the same for each entry so I don't think they need to be saved. state is not used.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
UDATA startOffset,
J9Class *clazz)
{
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make more sense to move this flag around isSubstitutable. Your helper code should be under J9VM_OPT_VALHALLA_VALUE_TYPES as well.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Assert_VM_notNull(lhs);
Assert_VM_notNull(rhs);
Assert_VM_true(J9OBJECT_CLAZZ(currentThread, lhs) == J9OBJECT_CLAZZ(currentThread, rhs));
rc = compareValueType(currentThread, objectAccessBarrier, lhs, rhs, startOffset, clazz);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly return the result of compareValueType instead of creating a variable.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
static bool
isSubstitutable(J9VMThread *currentThread, MM_ObjectAccessBarrierAPI objectAccessBarrier, j9object_t lhs, j9object_t rhs, UDATA startOffset, J9Class *clazz)
* Iteratively traverse the fields and determine if they are equal.
* Use depth-first search with a stack to handle nested flattened value types.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a BFS approach would make more sense for this method so you don't need to store iterators.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
goto done;
j9object_t lhsFieldObject = objectAccessBarrier.inlineMixedObjectReadObject(currentThread, frame.lhs, fieldOffset);
j9object_t rhsFieldObject = objectAccessBarrier.inlineMixedObjectReadObject(currentThread, frame.rhs, fieldOffset);
bool rc = VM_ValueTypeHelpers::acmp(currentThread, objectAccessBarrier, lhsFieldObject, rhsFieldObject);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a circular call here. acmp() calls isSubstitutable.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although acmp() calls isSubstitutable(), it starts a new traversal frame on the referenced object. I think it will not go into an infinite recursion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could still be many method calls added to the stack if an object field has another object field.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will not will not go into an infinite recursion

Please elaborate: Why are cycles involving value objects and non-value objects impossible?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my previous approach, acmp() was called from isSubstitutable() to compare reference fields. isSubstitutable() had a check to push value type objects into a queue so that their fields could be compared.
Hence it wouldn't be an infinite recursion.

However in the current implementation I have removed the call to acmp() in isSubstitutable() completely as Theresa suggested.

@AditiS11 AditiS11 force-pushed the iterative_impl branch 2 times, most recently from 14eedce to ab51a40 Compare March 9, 2026 12:10
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
U_32 walkFlags = J9VM_FIELD_OFFSET_WALK_INCLUDE_INSTANCE;
J9ROMFieldOffsetWalkState state;

std::queue<FieldComparisonFrame> queue;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not use STL. You can implement a queue using an array.

@AditiS11 AditiS11 force-pushed the iterative_impl branch 2 times, most recently from 0bd0a32 to 885375f Compare March 11, 2026 15:30
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
U_32 walkFlags = J9VM_FIELD_OFFSET_WALK_INCLUDE_INSTANCE;
J9ROMFieldOffsetWalkState state;

const UDATA MAX_QUEUE_SIZE = 512;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defined the max queue size as 512. Not sure if this is sufficient to support the behaviour of valuetypes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

512 should be sufficient.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this sufficient? There could still be any number of fields.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description says Use DFS so I assumed it is the depth.

Seems that the implementation is changed to do BFS, so 512 is not guaranteed to be big enough.

You can start with an array on stack with a default initial size (like 128). In the case of overflow, you can switch to a new array (2X size of old array) allocated from malloc and copy the data into the new array.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had suggested to switch to BFS since the DFS approach required saving much more information including field iterators in each queue entry. If the memory outlook for DFS is potentially better we can do that instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong preference between DFS or BFS.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
#include "ObjectAccessBarrierAPI.hpp"
#include "VMHelpers.hpp"
#include "vm_api.h"
#include <queue>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This library is not used.

Copy link
Copy Markdown
Contributor

@theresa-m theresa-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the pull request description to reflect that this is now BFS.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
UDATA queueSize = initialQueueSize;
FieldComparisonFrame *queue = (FieldComparisonFrame *)j9mem_allocate_memory(sizeof(FieldComparisonFrame) * queueSize, OMRMEM_CATEGORY_VM);
if (NULL == queue) {
return false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be an out of memory error thrown if memory allocation fails. You can modify the return value to an integer to return different types of results like is done in copyFlattenableArray.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
U_64 rhsValue = objectAccessBarrier.inlineMixedObjectReadU64(currentThread, frame.rhs, fieldOffset);

if (lhsValue != rhsValue) {
j9mem_free_memory(queue);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest jumping to a label that will free the queue memory since this is done in a lot of places.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
if (flattened) {
if (tail >= queueSize) {
UDATA newQueueSize = queueSize * 2;
FieldComparisonFrame *newQueue = (FieldComparisonFrame *)j9mem_allocate_memory(newQueueSize * sizeof(FieldComparisonFrame), OMRMEM_CATEGORY_VM);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest creating a helper method to create and resize the queue since a lot of this code is duplicated.

@AditiS11 AditiS11 force-pushed the iterative_impl branch 2 times, most recently from 82e4078 to 11dd004 Compare March 16, 2026 12:32
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
* Use breadth-first search with a queue to handle nested flattened value types.
*
* @param[in] currentThread the current thread
* @param[in] lhs the lhs object address
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objectAccessBarrier is missing from the param list.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
FieldComparisonFrame frame = queue[head++];
if (J9_ARE_ALL_BITS_SET(frame.clazz->classFlags, J9ClassCanSupportFastSubstitutability)) {
bool compare = objectAccessBarrier.structuralFlattenedCompareObjects(currentThread, frame.clazz, frame.lhs, frame.rhs, frame.offset);
if ( false == compare) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space between ( and f.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
UDATA queueSize = initialQueueSize;
UDATA head = 0;
UDATA tail = 0;
FieldComparisonFrame *queue = (FieldComparisonFrame *)j9mem_allocate_memory(sizeof(FieldComparisonFrame) * queueSize, OMRMEM_CATEGORY_VM);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid allocating unnecessary memory I think this queue should only be created if we run into a field that needs to be stored.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
}
done:
if (-1 == result) {
vm->internalVMFunctions->setHeapOutOfMemoryError(currentThread);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is only called from the interpreter the stack will also need to be setup like https://github.com/eclipse-openj9/openj9/blob/master/runtime/vm/BytecodeInterpreter.hpp#L11718-L11723. I suggest to pass this error through acmp and handle the exception throwing in the interpreter instead.

@AditiS11 AditiS11 force-pushed the iterative_impl branch 4 times, most recently from 4578055 to 9309b5d Compare March 17, 2026 16:09
Comment thread runtime/vm/BytecodeInterpreter.hpp Outdated
_vm->internalVMFunctions->setHeapOutOfMemoryError(_currentThread);
rc = GOTO_THROW_CURRENT_EXCEPTION;
return rc;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else if. Same for line 8835. Also please compare cmp to its value in the next line since the result is no longer bool.

Comment thread runtime/vm/BytecodeInterpreter.hpp Outdated
I_32 cmp = VM_ValueTypeHelpers::acmp(_currentThread, _objectAccessBarrier, lhs, rhs);
if (-1 == cmp) {
_vm->internalVMFunctions->setHeapOutOfMemoryError(_currentThread);
rc = GOTO_THROW_CURRENT_EXCEPTION;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use THROW_HEAP_OOM and follow the same pattern as other bytecodes by jumping to the line 8847 to return. Same for the other acmp use.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
UDATA startOffset,
J9Class *clazz)
{
Assert_VM_notNull(lhs);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These asserts are redundant now since isSubstitutable is only called by acmp. I think compareValueType method can just be named isSubstitutable.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
goto done;
}
case 'L': {
goto nextFrame;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this defined.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I had added it in an earlier version, but I have now wrapped the entire field walk in the else block. Hence there is no need for the nextFrame label.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
UDATA head = 0;
UDATA tail = 0;
FieldComparisonFrame *queue = NULL;
FieldComparisonFrame frame = { lhs, rhs, clazz, startOffset};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra space between { and l or a missing space between t and }.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
flattened = J9_IS_FIELD_FLATTENED(fieldClass, walkResult->field);
}
if (flattened) {
if (queue == NULL) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NULL == queue, same with 237.

@AditiS11 AditiS11 force-pushed the iterative_impl branch 2 times, most recently from 5357d9b to 7364525 Compare March 18, 2026 11:19
Comment thread runtime/vm/BytecodeInterpreter.hpp Outdated
rc = GOTO_BRANCH_WITH_ASYNC_CHECK;
} else if (-1 == cmp) {
rc = THROW_HEAP_OOM;
goto done;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I think my last review was confusing, this goto isn't needed.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
* Data members
*/
private:
struct FieldComparisonFrame
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be under J9VM_OPT_VALHALLA_VALUE_TYPES.

@theresa-m theresa-m requested a review from hangshao0 March 18, 2026 15:19
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
JIT_RETURN_UDATA(currentThread->javaVM->internalVMFunctions->valueTypeCapableAcmp(currentThread, lhs, rhs));
I_32 cmp = currentThread->javaVM->internalVMFunctions->valueTypeCapableAcmp(currentThread, lhs, rhs);
if (-1 == cmp) {
JIT_RETURN_UDATA(setHeapOutOfMemoryErrorFromJIT(currentThread));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

j9mem_allocate_memory() is native memory allocation, so this should be setNativeOutOfMemoryErrorFromJIT.

Similarly in BytecodeInterpreter.hpp, the j9mem_allocate_memory() allocation failure (from isSubstitutable()) should result in NativeOutOfMemoryError.

@hangshao0
Copy link
Copy Markdown
Contributor

There are some change to JIT helpers in cnathelp.cpp here. FYI @a7ehuo @hzongaro

@hangshao0 hangshao0 requested review from a7ehuo and hzongaro March 19, 2026 14:11
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
Comment thread runtime/oti/vm_api.h Outdated
Comment thread runtime/vm/ValueTypeHelpers.cpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
@keithc-ca
Copy link
Copy Markdown
Contributor

Please use parentheses here and in the commit message to clarify that the references is to a function: isSubstitutable().

@AditiS11 AditiS11 force-pushed the iterative_impl branch 2 times, most recently from 901fdd8 to 96634be Compare March 23, 2026 18:00
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
Comment thread runtime/codert_vm/cnathelp.cpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
@hangshao0
Copy link
Copy Markdown
Contributor

hangshao0 commented Mar 31, 2026

Any more review comments ? @keithc-ca

@keithc-ca
Copy link
Copy Markdown
Contributor

I'll have another look at this later today.

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
@hangshao0
Copy link
Copy Markdown
Contributor

@hzongaro @a7ehuo Do you have a chance to review the JIT helper changes here ?

@a7ehuo
Copy link
Copy Markdown
Contributor

a7ehuo commented Mar 31, 2026

@a7ehuo Do you have a chance to review the JIT helper changes here ?

Thanks for checking in. Sorry, unfortunately, I haven't had a chance to review this change yet. It would probably be better to have @hzongaro take a look instead.

@hangshao0
Copy link
Copy Markdown
Contributor

@keithc-ca @hzongaro Review comments ?

Copy link
Copy Markdown
Contributor

@keithc-ca keithc-ca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be rebased to avoid implicit merges for

  • runtime/oti/j9nonbuilder.h
  • runtime/oti/vm_api.h
  • runtime/vm/BytecodeInterpreter.hpp

Comment thread runtime/vm/ValueTypeHelpers.hpp Outdated
Replace recursive approach to traverse fields.
Modify isSubstitutable() to iteratively traverse the fields.
Use BFS with a queue to handle nested flattened value types.
Related: eclipse-openj9#15768

Signed-off-by: Aditi Srinivas M <Aditi.Srini@ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:vm project:valhalla Used to track Project Valhalla related work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants