Skip to content

Improve x86 inline checkcast/instanceof sequences for array cast classes - #23383

Merged
vijaysun-omr merged 3 commits into
eclipse-openj9:masterfrom
0xdaryl:arraycheckcast
Mar 31, 2026
Merged

Improve x86 inline checkcast/instanceof sequences for array cast classes#23383
vijaysun-omr merged 3 commits into
eclipse-openj9:masterfrom
0xdaryl:arraycheckcast

Conversation

@0xdaryl

@0xdaryl 0xdaryl commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

This PR makes two contributions to JIT generated code on x86-64:

  1. Inline instanceof checks when cast class is [java/lang/Object

Extend the current x86 checkcast optimization when the cast class is a
java/lang/Object array to handle instanceof and isAssignableFrom() cases.

  1. Inline checkcast/instanceof checks when the cast class is a known array on x86

Essentially inlines the checkcast/instanceof/isAssignableFrom() sequence when the
cast class is an array that the VM implements [1], but specializes it for when the
cast class is an array known at compile-time. It performs:

  • An exact equality check
  • A check for a match in the cast class cache
  • An arity check followed by a subclass check on the array leaf components when the
    arities are the same
  • Updates the cast class cache on success/failure in the same manner that the VM
    implementation does

Any “unusual” cases are punted to the VM (e.g., mismatched arities, when the leaf
class is an interface), as well as to throw the CastClassException if required.

The opt can be disabled by setting TR_DisableInlineArrayExactCastClass.

[1]

inlineCheckCast(J9Class *instanceClass, J9Class *castClass, bool updateCache = true)

@0xdaryl

0xdaryl commented Feb 20, 2026

Copy link
Copy Markdown
Contributor Author

@a7ehuo : may I ask you to review this please? @vijaysun-omr FYI

@a7ehuo a7ehuo left a comment

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.

Here is my review on the first commit. I'll review the second commit next Monday.

logprintf(comp->getOption(TR_TraceCG), comp->log(), "Inline checkcast for [jlO : node=%p", node);
if (reportInlineObjectArrayCheck)
{
OMR::CStdIOStreamLogger::Stdout->printf("XXXXX Inline checkcast for [jlO : isCheckCast=%d (icall=%d) : %s\n", isCheckCast, (node->getOpCodeValue() == TR::icall), comp->signature());

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.

  1. What is XXXXX in the trace for? Should it be removed?

  2. Is OMR::CStdIOStreamLogger logging into verbose vlog, or onto the console, or into compilation log? The reason I'm asking is that if this information should be logged into the compilation log as well at line 4164 if comp->getOption(TR_TraceCG) is true.

  3. The message Inline checkcast for ... might be confusing since it could be instanceof or isAssignableFrom cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was all used for debugging and discovery that I thought I would keep around in case it was useful. However, I'll just remove it.

static char *disableInlineObjectArrayCheck = feGetEnv("TR_DisableInlineObjectArrayCheck");

if (!disableInlineObjectArrayCheckCast && isCheckCast && clazz && TR::Compiler->cls.isClassArray(comp, clazz))
bool isRelocatableCompile = comp->compileRelocatableCode() || comp->isOutOfProcessCompilation();

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.

isRelocatableCompile is not used in this commit. It should be removed in this commit. (I assumed likely it'd be used in the next commit)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node,
generateX86MemoryReference(romClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassArray, cg);
generateLabelInstruction(TR::InstOpCode::JE4, node, outlinedCallLabel, cg);
generateLabelInstruction(TR::InstOpCode::JE4, node, outlinedHelperCallLabel, cg);

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 wonder if this line could be updated as below to avoid going to the helper if it is not array for instanceof and isAssignableFrom cases since resultReg has already been initialized as zero.

generateLabelInstruction(TR::InstOpCode::JE4, node, isCheckCast ? outlinedHelperCallLabel : fallThruLabel, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node,
generateX86MemoryReference(romClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassInternalPrimitiveType, cg);
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedCallLabel, cg);
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedHelperCallLabel, cg);

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 wonder if this line could be updated as below to avoid going to the helper if it is primitive array for instanceof and isAssignableFrom cases since resultReg has already been initialized as zero.

generateLabelInstruction(TR::InstOpCode::JNE4, node, isCheckCast ? outlinedHelperCallLabel : fallThruLabel, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

Comment on lines 4200 to 4201
if (!objectNode->isNonNull())
{

@a7ehuo a7ehuo Feb 20, 2026

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.

Would it be a problem for isAssignableFrom(Class<?> cls)? If cls is null, NullPointerException (not return false) should be thrown, but the current behaviour would return 0/false by jumping to fallThruLabel.

         // If the object is NULL, no exception is thrown for a checkcast and a 0
         // is returned for an instanceof.
         //
         if (!objectNode->isNonNull())
            {
            generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), node, objectReg, objectReg, cg);
            generateLabelInstruction(TR::InstOpCode::JE4, node, fallThruLabel, cg);
            }

@0xdaryl 0xdaryl Feb 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

When the isAssignableFrom is transformed, a NULLCHK is inserted for the receiver and the cast class. So a null value should have been filtered out before this code is reached.

treetop->insertBefore(TR::TreeTop::create(comp(), TR::Node::createWithSymRef(TR::NULLCHK, 1, 1, TR::Node::create(node, TR::PassThrough, 1, toClass), nullchk)));

@vijaysun-omr

Copy link
Copy Markdown
Contributor

Is it possible to show the sequence from a JIT trace log after instruction selection (to help with review) ? Thanks

@a7ehuo a7ehuo left a comment

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.

Here is the second half of the review

OMR::CStdIOStreamLogger::Stdout->printf("YYYYY Found inlineArrayExactCastClass : isCheckCast=%d : %s\n", isCheckCast, comp->signature());
}

logprintf(comp->getOption(TR_TraceCG), comp->log(), "Inline instanceof/checkcast for const cast class array: node=%p", node);

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.

Could the compilation log be updated to include isCheckCast value and mention isAssignableFrom case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

//
if (reportInlineArrayExactCastClass)
{
OMR::CStdIOStreamLogger::Stdout->printf("YYYYY Found inlineArrayExactCastClass : isCheckCast=%d : %s\n", isCheckCast, comp->signature());

@a7ehuo a7ehuo Feb 22, 2026

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.

What does YYYYY refer to? Should it be removed or updated to something more meaningful?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed, as explained earlier.

Comment on lines +4392 to +4401
if (!objectNode->isNonNull())
{
generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), node, objectReg, objectReg, cg);

// checkcast leaves the operand stack unaffected
// instanceof returns 0 if the objectRef is null
//
TR::LabelSymbol *nullTargetLabel = isCheckCast ? fallThruLabel : notCastableDoNotCacheLabel;
generateLabelInstruction(TR::InstOpCode::JE4, node, nullTargetLabel, cg);
}

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.

Does TR::icall corresponds directly to Java Class.isAssignableFrom? Will objectNode always be non null if the node is TR::icall? The reason I'm asking is that If cls is null, NullPointerException should be thrown.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. It seems the way we distinguish isAssignableFrom from the other cases is by the presence of an icall opcode. As mentioned in an earlier comment, there are NULLCHKs inserted that should catch the NPE cases.

TR::Register *scratchReg2 = NULL;
TR::Register *scratchReg3 = NULL;

bool use64BitClasses = cg->comp()->target().is64Bit() && !TR::Compiler->om.generateCompressedObjectHeaders();

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 check cg->comp()->target().is64Bit() is redundant since this whole case 2 are wrapped under it

      else if (!disableInlineArrayExactCastClass && !isRelocatableCompile && cg->comp()->target().is64Bit())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

if (!scratchReg)
scratchReg = cg->allocateRegister();

generateRegMemInstruction(TR::InstOpCode::LRegMem(), node, scratchReg,

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.

Does it need to consider if use64BitClasses is true or false when generating TR::InstOpCode::LRegMem?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I replaced all the parameterized instructions with 64-bit ones.

Comment on lines +4540 to +4550
if (IS_32BIT_SIGNED(componentClazzAddress))
{
// TODO: Need a relocation for componentClazz
generateRegImmInstruction(TR::InstOpCode::CMPRegImm4(), node, objectClassLeafReg, (int32_t)componentClazzAddress, cg);
}
else
{
// TODO: Need a relocation for componentClazz
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, node, scratchReg, componentClazzAddress, cg);
generateRegRegInstruction(TR::InstOpCode::CMPRegReg(), node, objectClassLeafReg, scratchReg, cg);
}

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.

Should it explicitly consider use64BitClasses being true and false cases here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I replaced all the parameterized instructions with 64-bit ones.

Comment on lines +4495 to +4497
intptr_t castClassArity = ((J9ArrayClass*)clazz)->arity;

generateRegImmInstruction(TR::InstOpCode::CMPRegImm4(), node, scratchReg, (int32_t)castClassArity, cg);

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.

Is it necessary to cast ((J9ArrayClass*)clazz)->arity from UDATA → intptr_t → int32_t? Should it be cast directly from UDATA → int32_t?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I pulled that into a separate local variable because it was used for something else at some point, but now there is only one other use. I'll just fold the arity load into the genRegImmInstruction.

#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES)
if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(castClass))
{
static_assert(J9ClassArrayIsNullRestricted == 0x2000000, "Cannot do simple bit test for J9ClassArrayIsNullRestricted");

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 assert is currently written to require the value to be 0x2000000, but the assert message is unclear what is expected. Should it be updated to something like expect J9ClassArrayIsNullRestricted == 0x2000000 or J9ClassArrayIsNullRestricted must be 0x2000000 to be explicit?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

TR::Register *objectClassReg,
uintptr_t clazzAddress,
bool use64BitClasses,
TR::Register *scratchReg,

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.

scratchReg is not used in this function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
else
{
// TODO: process out of line, but could just throw a ClassCastException 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.

This TODO: process out of line ... comment sounds a bit ambiguous to me since processing out of line is already happening. Do you mean for checkcast failure, instead of punting to the full helper, generate a dedicated OOL to throw ClassCastException? If so, could the TODO comment be updated to be more accurate?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I just removed the TODO altogether and stated the current behaviour is for the out-of-line helper to throw the CastClassException.

@0xdaryl 0xdaryl left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have addressed all the comments (thank you!) in the next force push, in addition to the following:

  • Create a special "case 2" for when the cast class is an array known at compile-time when the leaf class is final. The JIT logic is simplified and more readable, and I think the generated code is better.
  • Rebased and reformatted the code per the new JIT code formatting guidelines
  • Added more code documentation

TR::Register *objectClassReg,
uintptr_t clazzAddress,
bool use64BitClasses,
TR::Register *scratchReg,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

static char *disableInlineObjectArrayCheck = feGetEnv("TR_DisableInlineObjectArrayCheck");

if (!disableInlineObjectArrayCheckCast && isCheckCast && clazz && TR::Compiler->cls.isClassArray(comp, clazz))
bool isRelocatableCompile = comp->compileRelocatableCode() || comp->isOutOfProcessCompilation();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

logprintf(comp->getOption(TR_TraceCG), comp->log(), "Inline checkcast for [jlO : node=%p", node);
if (reportInlineObjectArrayCheck)
{
OMR::CStdIOStreamLogger::Stdout->printf("XXXXX Inline checkcast for [jlO : isCheckCast=%d (icall=%d) : %s\n", isCheckCast, (node->getOpCodeValue() == TR::icall), comp->signature());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was all used for debugging and discovery that I thought I would keep around in case it was useful. However, I'll just remove it.

Comment on lines 4200 to 4201
if (!objectNode->isNonNull())
{

@0xdaryl 0xdaryl Feb 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

When the isAssignableFrom is transformed, a NULLCHK is inserted for the receiver and the cast class. So a null value should have been filtered out before this code is reached.

treetop->insertBefore(TR::TreeTop::create(comp(), TR::Node::createWithSymRef(TR::NULLCHK, 1, 1, TR::Node::create(node, TR::PassThrough, 1, toClass), nullchk)));

//
if (reportInlineArrayExactCastClass)
{
OMR::CStdIOStreamLogger::Stdout->printf("YYYYY Found inlineArrayExactCastClass : isCheckCast=%d : %s\n", isCheckCast, comp->signature());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed, as explained earlier.

TR::LabelSymbol *notCastableUpdateCacheLabel = generateLabelSymbol(cg);

generateLabelInstruction(TR::InstOpCode::label, node, startLabel, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, comment added.

#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES)
if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(castClass))
{
static_assert(J9ClassArrayIsNullRestricted == 0x2000000, "Cannot do simple bit test for J9ClassArrayIsNullRestricted");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

}
else
{
// TODO: process out of line, but could just throw a ClassCastException instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I just removed the TODO altogether and stated the current behaviour is for the out-of-line helper to throw the CastClassException.

generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node,
generateX86MemoryReference(romClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassArray, cg);
generateLabelInstruction(TR::InstOpCode::JE4, node, outlinedCallLabel, cg);
generateLabelInstruction(TR::InstOpCode::JE4, node, outlinedHelperCallLabel, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

generateMemImmInstruction(TR::InstOpCode::TEST4MemImm4, node,
generateX86MemoryReference(romClassReg, offsetof(J9ROMClass, modifiers), cg), J9AccClassInternalPrimitiveType, cg);
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedCallLabel, cg);
generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedHelperCallLabel, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, fixed.

@0xdaryl

0xdaryl commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Case 1: objectRef instanceof Object[]

 [0x7fb133073c80]       Label L0017:                    # label # (Start of internal control flow)
 [0x7fb133073d20]       xor     GPR_0019, GPR_0019              # XOR4RegReg
 [0x7fb133073db0]       test    &GPR_0016, &GPR_0016            # TEST8RegReg
 [0x7fb133073e40]       je      Label L0018                     # JE4
 [0x7fb133073f70]       mov     GPR_0017, dword ptr [&GPR_0016]         # L4RegMem
 [0x7fb133074000]       and     GPR_0017, 0xffffffffffffff00    # AND4RegImm4
 [0x7fb133074120]       mov     GPR_0018, qword ptr [GPR_0017+0x8]              # L8RegMem, SymRef [#421 +8]
 [0x7fb133074240]       test    dword ptr [GPR_0018+0x10], 0x00010000   # TEST4MemImm4, SymRef [#422 +16]
 [0x7fb1330742d0]       je      Label L0018                     # JE4
 [0x7fb133074400]       mov     GPR_0018, qword ptr [GPR_0017+0x58]             # L8RegMem, SymRef [#423 +88]
 [0x7fb133074520]       mov     GPR_0018, qword ptr [GPR_0018+0x8]              # L8RegMem, SymRef [#424 +8]
 [0x7fb133074640]       test    dword ptr [GPR_0018+0x10], 0x00020000   # TEST4MemImm4, SymRef [#425 +16]
 [0x7fb1330746d0]       jne     Label L0018                     # JNE4
 [0x7fb133074770]       mov     GPR_0019, 0x00000001    # MOV4RegImm4
 [0x7fb1330748a0]       Label L0018:                    # label # (End of internal control flow)
        POST: [&GPR_0016 : NoReg] [GPR_0017 : NoReg] [GPR_0019 : NoReg] [GPR_0018 : NoReg]

Case 2: objectRef instanceof [I (final)

 [0x7fd8a4073b40]       Label L0017:                    # label # (Start of internal control flow)
 [0x7fd8a4073be0]       xor     GPR_0018, GPR_0018              # XOR4RegReg
 [0x7fd8a4073c70]       test    &GPR_0016, &GPR_0016            # TEST8RegReg
 [0x7fd8a4073d00]       je      Label L0018                     # JE4
 [0x7fd8a4073e30]       mov     GPR_0017, dword ptr [&GPR_0016]         # L4RegMem
 [0x7fd8a4073ec0]       and     GPR_0017, 0xffffffffffffff00    # AND4RegImm4
 [0x7fd8a4073f50]       cmp     GPR_0017, 0x00049300    # CMP8RegImm4
 [0x7fd8a4073fe0]       sete    GPR_0018                        # SETE1Reg
 [0x7fd8a40740f0]       Label L0018:                    # label # (End of internal control flow)
        POST: [&GPR_0016 : NoReg] [GPR_0017 : NoReg] [GPR_0018 : NoReg]

Case 2: checkcast objectRef, [B (final)

 [0x7fd8a40cd340]       Label L0067:                    # label # (Start of internal control flow)
 [0x7fd8a40cd3e0]       test    &GPR_0079, &GPR_0079            # TEST8RegReg
 [0x7fd8a40cd470]       je      Label L0068                     # JE4
 [0x7fd8a40cd5a0]       mov     GPR_0080, dword ptr [&GPR_0079]         # L4RegMem
 [0x7fd8a40cd630]       and     GPR_0080, 0xffffffffffffff00    # AND4RegImm4
 [0x7fd8a40cd6c0]       cmp     GPR_0080, 0x00048b00    # CMP8RegImm4
 [0x7fd8a40cfef0]       jne     Outlined Label L0069                    # JNE4
 [0x7fd8a40d0070]       Label L0068:                    # label # (End of internal control flow)
        POST: [&GPR_0079 : NoReg] [GPR_0080 : NoReg]

...

 [0x7fd8a40cd820]       Outlined Label L0069:                   # label
 [0x7fd8a40cded0]       lea     GPR_0082, dword ptr [$0x0000000000048b00]               # LEA4RegMem, SymRef  [B[#440  Static] [flags 0x18307 0x0 ]
 [0x7fd8a40cf220]       mov     GPR_0085, GPR_0082              # MOV8RegReg
 [0x7fd8a40cf2b0]       mov     GPR_0087, &GPR_0079             # MOV8RegReg
 [0x7fd8a40cf340]       mov     GPR_0086, GPR_0000              # MOV8RegReg
 [0x7fd8a40cf8f0]       call    jitCheckCast            # CALLImm4 (00007FD8F1A26A10)# CALLImm4
         PRE: [GPR_0083 : eax] [GPR_0084 : ecx] [GPR_0085 : edx] [GPR_0086 : edi] [GPR_0087 : esi] [GPR_0088 : r8d] [GPR_0089 : r9d] [GPR_0090 : r10d] [GPR_0091 : r11d] [FPR_0092 : xmm0] [FPR_0093 : xmm1] [FPR_0094 : xmm2] [FPR_0095 : xmm3] [FPR_0096 : xmm4] [FPR_0097 : xmm5] [FPR_0098 : xmm6] [FPR_0099 : xmm7] [FPR_0100 : xmm8] [FPR_0101 : xmm9] [FPR_0102 : xmm10] [FPR_0103 : xmm11] [FPR_0104 : xmm12] [FPR_0105 : xmm13] [FPR_0106 : xmm14] [FPR_0107 : xmm15] [GPR_0000 : ebp]
        POST: [GPR_0083 : eax] [GPR_0084 : ecx] [GPR_0085 : edx] [GPR_0086 : edi] [GPR_0087 : esi] [GPR_0088 : r8d] [GPR_0089 : r9d] [GPR_0090 : r10d] [GPR_0091 : r11d] [FPR_0092 : xmm0] [FPR_0093 : xmm1] [FPR_0094 : xmm2] [FPR_0095 : xmm3] [FPR_0096 : xmm4] [FPR_0097 : xmm5] [FPR_0098 : xmm6] [FPR_0099 : xmm7] [FPR_0100 : xmm8] [FPR_0101 : xmm9] [FPR_0102 : xmm10] [FPR_0103 : xmm11] [FPR_0104 : xmm12] [FPR_0105 : xmm13] [FPR_0106 : xmm14] [FPR_0107 : xmm15] [GPR_0000 : ebp]
 [0x7fd8a40cfd50]       jmp     Label L0068                     # JMP4
 [0x7fd8a40cfe50]       Label L0070:                    # label

Case 3: objectRef instanceof [Lsun/reflect/generics/tree/FieldTypeSignature; (not final)

 ------------------------------
 n17n     (  0)  ificmpeq --> block_4 BBStart at n1n ()                                               [0x7fd8a7004d30] bci=[-1,9,124] rc=0 vc=19 vn=- li=2 udi=- nc=2 flg=0x20
 n13n     (  0)    instanceof  jitInstanceOf[#88  helper Method] [flags 0x400 0x0 ] (in GPR_0021) (X!=0 )  [0x7fd8a7004bf0] bci=[-1,6,124] rc=0 vc=19 vn=- li=2 udi=25808 nc=2 flg=0x4
 n11n     (  0)      aload  value<auto slot 1>[#420  Auto] [flags 0x7 0x0 ] (in &GPR_0019)            [0x7fd8a7004b50] bci=[-1,5,124] rc=0 vc=19 vn=- li=2 udi=25408 nc=0
 n12n     (  0)      loadaddr  [Lsun/reflect/generics/tree/FieldTypeSignature;[#421  Static] [flags 0x18307 0x0 ]  [0x7fd8a7004ba0] bci=[-1,6,124] rc=0 vc=19 vn=- li=2 udi=- nc=0
 n14n     (  0)    iconst 0 (X==0 X>=0 X<=0 )                                                         [0x7fd8a7004c40] bci=[-1,9,124] rc=0 vc=19 vn=- li=2 udi=- nc=0 flg=0x302
------------------------------

 [0x7fd8a70c63c0]       mov     &GPR_0019, qword ptr [vfp]              # L8RegMem, SymRef  value<auto slot 1>[#431  Auto] [flags 0x7 0x0 ]
 [0x7fd8a70c67d0]       Label L0017:                    # label # (Start of internal control flow)
 [0x7fd8a70c6870]       test    &GPR_0019, &GPR_0019            # TEST8RegReg
 [0x7fd8a70c6900]       je      Label L0021                     # JE4
 [0x7fd8a70c6a30]       mov     GPR_0020, dword ptr [&GPR_0019]         # L4RegMem
 [0x7fd8a70c6ac0]       and     GPR_0020, 0xffffffffffffff00    # AND4RegImm4
 [0x7fd8a70c6b50]       cmp     GPR_0020, 0x005b7100    # CMP8RegImm4
 [0x7fd8a70c6be0]       je      Label L0019                     # JE4
 [0x7fd8a70c6dd0]       mov     GPR_0022, qword ptr [GPR_0020+0xc8]             # L8RegMem, SymRef [#433 +200]
 [0x7fd8a70c6e60]       xor     GPR_0022, 0x005b7100    # XOR4RegImm4
 [0x7fd8a70c6ef0]       test    GPR_0022, 0xfffffffffffffffe    # TEST8RegImm4
 [0x7fd8a70c6fe0]       jne     Label L0023                     # JNE4
 [0x7fd8a70c7080]       test    GPR_0022, 0x00000001    # TEST8RegImm4
 [0x7fd8a70c7110]       je      Label L0019                     # JE4
 [0x7fd8a70c71b0]       jmp     Label L0021                     # JMP4
 [0x7fd8a70c7250]       Label L0023:                    # label
 [0x7fd8a70c7380]       test    dword ptr [GPR_0020+0x18], 0x00010000   # TEST4MemImm4, SymRef [#434 +24]
 [0x7fd8a70c7410]       je      Label L0022                     # JE4
 [0x7fd8a70c7540]       mov     GPR_0022, qword ptr [GPR_0020+0x48]             # L8RegMem, SymRef [#435 +72]
 [0x7fd8a70c75d0]       cmp     GPR_0022, 0x00000001    # CMP8RegImm4
 [0x7fd8a70c76c0]       jne     Label L0024                     # JNE4
 [0x7fd8a70c7930]       mov     GPR_0023, qword ptr [GPR_0020+0x40]             # L8RegMem, SymRef [#436 +64]
 [0x7fd8a70c7a50]       mov     GPR_0022, qword ptr [GPR_0023+0x18]             # L8RegMem, SymRef [#437 +24]
 [0x7fd8a70c7ae0]       and     GPR_0022, 0x000e0000    # AND8RegImm4
 [0x7fd8a70c7b70]       cmp     GPR_0022, 0x000e0000    # CMP8RegImm4
 [0x7fd8a70c7c00]       jne     Label L0022                     # JNE4
 [0x7fd8a70c7ca0]       cmp     GPR_0023, 0x00534000    # CMP8RegImm4
 [0x7fd8a70c7d30]       je      Label L0020                     # JE4
 [0x7fd8a70c7e60]       cmp     word ptr [GPR_0023+0x18], 0x0001        # CMP2MemImm2, SymRef [#438 +24]
 [0x7fd8a70c7ef0]       jbe     Label L0024                     # JBE4
 [0x7fd8a70c8020]       mov     GPR_0022, qword ptr [GPR_0023+0x10]             # L8RegMem, SymRef [#439 +16]
 [0x7fd8a70c8140]       cmp     dword ptr [GPR_0022+0x8], 0x00534000    # CMP4MemImm4, SymRef [#440 +8]
 [0x7fd8a70c81d0]       je      Label L0020                     # JE4
 [0x7fd8a70c8270]       jmp     Label L0024                     # JMP4
 [0x7fd8a70c8310]       Label L0022:                    # label
 [0x7fd8a70c8440]       mov     dword ptr [GPR_0020+0xc8], 0x005b7101   # S4MemImm4, SymRef [#441 +200]
 [0x7fd8a70c84d0]       Label L0021:                    # label
 [0x7fd8a70c8570]       xor     GPR_0021, GPR_0021              # XOR4RegReg
 [0x7fd8a70c8600]       jmp     Label L0018                     # JMP4
 [0x7fd8a70cb350]       Label L0024:                    # label
 [0x7fd8a70cb3f0]       jmp     Outlined Label L0025                    # JMP4
 [0x7fd8a70cb490]       Label L0020:                    # label
 [0x7fd8a70cb5c0]       mov     dword ptr [GPR_0020+0xc8], 0x005b7100   # S4MemImm4, SymRef [#443 +200]
 [0x7fd8a70cb650]       Label L0019:                    # label
 [0x7fd8a70cb6f0]       mov     GPR_0021, 0x00000001    # MOV4RegImm4
 [0x7fd8a70cb860]       Label L0018:                    # label # (End of internal control flow)
        POST: [&GPR_0019 : NoReg] [GPR_0020 : NoReg] [GPR_0021 : NoReg] [GPR_0022 : NoReg] [GPR_0023 : NoReg]
                        ...

...

 [0x7fd8a70c8770]       Outlined Label L0025:                   # label
 [0x7fd8a70c8e20]       lea     GPR_0025, dword ptr [$0x00000000005b7100]               # LEA4RegMem, SymRef  [Lsun/reflect/generics/tree/FieldTypeSignature;[#442  Static] [flags 0x18307 0x0 ]
 [0x7fd8a70ca4a0]       mov     GPR_0028, GPR_0025              # MOV8RegReg
 [0x7fd8a70ca530]       mov     GPR_0030, &GPR_0019             # MOV8RegReg
 [0x7fd8a70ca5c0]       mov     GPR_0029, GPR_0000              # MOV8RegReg
 [0x7fd8a70cab70]       call    jitInstanceOf           # CALLImm4 (00007FD8F1A19C60)# CALLImm4
         PRE: [GPR_0026 : eax] [GPR_0027 : ecx] [GPR_0028 : edx] [GPR_0029 : edi] [GPR_0030 : esi] [GPR_0031 : r8d] [GPR_0032 : r9d] [GPR_0033 : r10d] [GPR_0034 : r11d] [FPR_0035 : xmm0] [FPR_0036 : xmm1] [FPR_0037 : xmm2] [FPR_0038 : xmm3] [FPR_0039 : xmm4] [FPR_0040 : xmm5] [FPR_0041 : xmm6] [FPR_0042 : xmm7] [FPR_0043 : xmm8] [FPR_0044 : xmm9] [FPR_0045 : xmm10] [FPR_0046 : xmm11] [FPR_0047 : xmm12] [FPR_0048 : xmm13] [FPR_0049 : xmm14] [FPR_0050 : xmm15] [GPR_0000 : ebp]
        POST: [GPR_0026 : eax] [GPR_0027 : ecx] [GPR_0028 : edx] [GPR_0029 : edi] [GPR_0030 : esi] [GPR_0031 : r8d] [GPR_0032 : r9d] [GPR_0033 : r10d] [GPR_0034 : r11d] [FPR_0035 : xmm0] [FPR_0036 : xmm1] [FPR_0037 : xmm2] [FPR_0038 : xmm3] [FPR_0039 : xmm4] [FPR_0040 : xmm5] [FPR_0041 : xmm6] [FPR_0042 : xmm7] [FPR_0043 : xmm8] [FPR_0044 : xmm9] [FPR_0045 : xmm10] [FPR_0046 : xmm11] [FPR_0047 : xmm12] [FPR_0048 : xmm13] [FPR_0049 : xmm14] [FPR_0050 : xmm15] [GPR_0000 : ebp]
 [0x7fd8a70cb090]       mov     GPR_0051, GPR_0026              # MOV8RegReg
 [0x7fd8a70cb120]       mov     GPR_0021, GPR_0051              # MOV8RegReg
 [0x7fd8a70cb1b0]       jmp     Label L0018                     # JMP4
 [0x7fd8a70cb2b0]       Label L0026:                    # label

Case 3: checkcast objectRef, [Ljava/lang/reflect/Member; (not final)

 ------------------------------
 n72n     (  0)  checkcast [#87]                                                                      [0x7fd8a7080640] bci=[-1,36,322] rc=0 vc=32 vn=- li=6 udi=- nc=2
 n69n     (  1)    ==>acall (in &GPR_0109)
 n71n     (  0)    loadaddr  [Ljava/lang/reflect/Member;[#429  Static] [flags 0x18307 0x0 ]           [0x7fd8a70805f0] bci=[-1,36,322] rc=0 vc=32 vn=- li=6 udi=- nc=0
------------------------------

 [0x7fd8a70ffd80]       Label L0100:                    # label # (Start of internal control flow)
 [0x7fd8a70ffe20]       test    &GPR_0109, &GPR_0109            # TEST8RegReg
 [0x7fd8a70ffeb0]       je      Label L0101                     # JE4
 [0x7fd8a7100000]       mov     GPR_0110, dword ptr [&GPR_0109]         # L4RegMem
 [0x7fd8a7100090]       and     GPR_0110, 0xffffffffffffff00    # AND4RegImm4
 [0x7fd8a7100120]       cmp     GPR_0110, 0x00169c00    # CMP8RegImm4
 [0x7fd8a71001b0]       je      Label L0103                     # JE4
 [0x7fd8a7100360]       mov     GPR_0111, qword ptr [GPR_0110+0xc8]             # L8RegMem, SymRef [#468 +200]
 [0x7fd8a71003f0]       xor     GPR_0111, 0x00169c00    # XOR4RegImm4
 [0x7fd8a7100480]       test    GPR_0111, 0xfffffffffffffffe    # TEST8RegImm4
 [0x7fd8a7100570]       jne     Label L0107                     # JNE4
 [0x7fd8a7100610]       test    GPR_0111, 0x00000001    # TEST8RegImm4
 [0x7fd8a71006a0]       je      Label L0103                     # JE4
 [0x7fd8a7100740]       jmp     Label L0105                     # JMP4
 [0x7fd8a71007e0]       Label L0107:                    # label
 [0x7fd8a7100910]       test    dword ptr [GPR_0110+0x18], 0x00010000   # TEST4MemImm4, SymRef [#469 +24]
 [0x7fd8a71009a0]       je      Label L0106                     # JE4
 [0x7fd8a7100ad0]       mov     GPR_0111, qword ptr [GPR_0110+0x48]             # L8RegMem, SymRef [#470 +72]
 [0x7fd8a7100b60]       cmp     GPR_0111, 0x00000001    # CMP8RegImm4
 [0x7fd8a7100bf0]       jne     Label L0102                     # JNE4
 [0x7fd8a7100de0]       mov     GPR_0112, qword ptr [GPR_0110+0x40]             # L8RegMem, SymRef [#471 +64]
 [0x7fd8a7100f00]       mov     GPR_0111, qword ptr [GPR_0112+0x18]             # L8RegMem, SymRef [#472 +24]
 [0x7fd8a7100f90]       and     GPR_0111, 0x000e0000    # AND8RegImm4
 [0x7fd8a7101020]       cmp     GPR_0111, 0x000e0000    # CMP8RegImm4
 [0x7fd8a71010b0]       jne     Label L0106                     # JNE4
 [0x7fd8a7101150]       cmp     GPR_0112, 0x0005d700    # CMP8RegImm4
 [0x7fd8a71011e0]       je      Label L0104                     # JE4
 [0x7fd8a7101310]       cmp     word ptr [GPR_0112+0x18], 0x0001        # CMP2MemImm2, SymRef [#473 +24]
 [0x7fd8a71013a0]       jbe     Label L0102                     # JBE4
 [0x7fd8a71014d0]       mov     GPR_0111, qword ptr [GPR_0112+0x10]             # L8RegMem, SymRef [#474 +16]
 [0x7fd8a71015f0]       cmp     dword ptr [GPR_0111+0x8], 0x0005d700    # CMP4MemImm4, SymRef [#475 +8]
 [0x7fd8a7101680]       je      Label L0104                     # JE4
 [0x7fd8a7101720]       jmp     Label L0102                     # JMP4
 [0x7fd8a71017c0]       Label L0106:                    # label
 [0x7fd8a71018f0]       mov     dword ptr [GPR_0110+0xc8], 0x00169c01   # S4MemImm4, SymRef [#476 +200]
 [0x7fd8a7101980]       Label L0105:                    # label
 [0x7fd8a71045c0]       Label L0102:                    # label
 [0x7fd8a7104660]       jmp     Outlined Label L0108                    # JMP4
 [0x7fd8a7104700]       Label L0104:                    # label
 [0x7fd8a7104830]       mov     dword ptr [GPR_0110+0xc8], 0x00169c00   # S4MemImm4, SymRef [#478 +200]
 [0x7fd8a71048c0]       Label L0103:                    # label
 [0x7fd8a7104a30]       Label L0101:                    # label # (End of internal control flow)
        POST: [&GPR_0109 : NoReg] [GPR_0110 : NoReg] [GPR_0111 : NoReg] [GPR_0112 : NoReg]

        ...

 [0x7fd8a7101af0]       Outlined Label L0108:                   # label
 [0x7fd8a71021a0]       lea     GPR_0114, dword ptr [$0x0000000000169c00]               # LEA4RegMem, SymRef  [Ljava/lang/reflect/Member;[#477  Static] [flags 0x18307 0x0 ]
 [0x7fd8a71038f0]       mov     GPR_0117, GPR_0114              # MOV8RegReg
 [0x7fd8a7103980]       mov     GPR_0119, &GPR_0109             # MOV8RegReg
 [0x7fd8a7103a10]       mov     GPR_0118, GPR_0000              # MOV8RegReg
 [0x7fd8a7103fc0]       call    jitCheckCast            # CALLImm4 (00007FD8F1A26A10)# CALLImm4
         PRE: [GPR_0115 : eax] [GPR_0116 : ecx] [GPR_0117 : edx] [GPR_0118 : edi] [GPR_0119 : esi] [GPR_0120 : r8d] [GPR_0121 : r9d] [GPR_0122 : r10d] [GPR_0123 : r11d] [FPR_0124 : xmm0] [FPR_0125 : xmm1] [FPR_0126 : xmm2] [FPR_0127 : xmm3] [FPR_0128 : xmm4] [FPR_0129 : xmm5] [FPR_0130 : xmm6] [FPR_0131 : xmm7] [FPR_0132 : xmm8] [FPR_0133 : xmm9] [FPR_0134 : xmm10] [FPR_0135 : xmm11] [FPR_0136 : xmm12] [FPR_0137 : xmm13] [FPR_0138 : xmm14] [FPR_0139 : xmm15] [GPR_0000 : ebp]
        POST: [GPR_0115 : eax] [GPR_0116 : ecx] [GPR_0117 : edx] [GPR_0118 : edi] [GPR_0119 : esi] [GPR_0120 : r8d] [GPR_0121 : r9d] [GPR_0122 : r10d] [GPR_0123 : r11d] [FPR_0124 : xmm0] [FPR_0125 : xmm1] [FPR_0126 : xmm2] [FPR_0127 : xmm3] [FPR_0128 : xmm4] [FPR_0129 : xmm5] [FPR_0130 : xmm6] [FPR_0131 : xmm7] [FPR_0132 : xmm8] [FPR_0133 : xmm9] [FPR_0134 : xmm10] [FPR_0135 : xmm11] [FPR_0136 : xmm12] [FPR_0137 : xmm13] [FPR_0138 : xmm14] [FPR_0139 : xmm15] [GPR_0000 : ebp]
 [0x7fd8a7104420]       jmp     Label L0101                     # JMP4
 [0x7fd8a7104520]       Label L0109:                    # label

@0xdaryl

0xdaryl commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

I just realized that a further improvement here would be to avoid the inline sequence altogether (and handle out of line) if the leaf component class is an interface known at compile-time. I will save that enhancement for a subsequent PR to avoid destabilizing this one.

@vijaysun-omr

Copy link
Copy Markdown
Contributor

jenkins test sanity all jdk21

@0xdaryl

0xdaryl commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Failures are all infrastructural (likely related to the ongoing Artifactory issues).

This PR is entirely specific to x86 files, so there is no need to test on any platforms other than x86.

@0xdaryl

0xdaryl commented Mar 16, 2026

Copy link
Copy Markdown
Contributor Author

Jenkins test sanity.functional,sanity.openjdk xlinux,win,osx jdk21


generateLabelInstruction(TR::InstOpCode::JNE4, node, outlinedHelperCallLabel, cg);
} else {
generateRegInstruction(TR::InstOpCode::SETE1Reg, node, resultReg, cg);

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.

Is there a further optimization possible that directly branches to the result of an if that is based on the instanceof ? I ask, because we had such ifinstanceof optimization in the past, and it may be that the SET operation here might have been replaced with a jump to the ifinstanceof target directly ?

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.

It is possible that you consider that a separate optimization to be done in a later PR, which is fine, but I was just asking about the plan.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I think we talked about that a few weeks ago that the ifinstanceof optimizations on x86 seem to have been dropped over the years for some reason. You are right that there is an optimization opportunity here that can be exploited. See #23574.

/**
* The only reason this is disabled for relocatable compiles (AOT and out-of-process
* compiles like JitServer) is because the support has not been implemented yet.
* Implementing the code relocations and appropriate frontend queries can be 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.

@mpirvu and @dsouzai (both as FYI and in case you felt that you wanted to do the work for JIT server and AOT once this merges, especially if it is easy enough).


// -----------------------------------------------------------------------
// Perform trivial check whether objectClass is the same as the castClass.
// The castClass is known to be an array (implicitly final), so no

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.

Small suggestion : I think your "implicitly final" phrase applies to the array class (in fact all array classes) and not to the component class. You may want to make that explicit by changing "implicitly final" to "all array classes are implicitly final"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

generateLabelInstruction(TR::InstOpCode::JNE4, node, castClassCacheMissLabel, cg);

// ----------------------------------------------------------------------
// objectClass was found in the cache. Determine whether it was castable

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.

Just so I am clear (and this comment makes it easier in the future), what does it mean to find a class in the cache, but it was not castable ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The low-bit on the class address stored in the cache indicates whether the cast succeeds or fails. So, for a class to be "cached but not castable" means it is stored in the cache but is low-tagged.

* goto notCastableUpdateCache
* }
*
* if (objectClass->leaf == castClass->leaf) {

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.

Is leaf the "leaf class" (going past all dimensions) or the "component class" (going past one dimension) ?

e.g. is "leaf" [X or X when one starts with [[X as the objectClass ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is the leaf class, so X.

*
* if (castClass->leaf is final) {
* if ((objectClass->leaf->depth <= castClass->leaf->depth) {
* if (castClass->leaf is an interface) {

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.

Is punting to the helper what happens for interface checkcasts when encountered outside of the array case ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not immediately. See [1] called from [2].

[1]

inline void generateInlinedCheckCastOrInstanceOfForInterface(TR::Node *node, TR_OpaqueClassBlock *castClass,

[2]
if (TR::Compiler->cls.isInterfaceClass(comp, 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.

should we have a follow on PR that uses similar logic here in the array case too ?

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.

@0xdaryl can I please get your answer on this ? I will merge it once this is done

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I clarified in the comments that this applies to all casts (checkcast, instanceof, Class.isAssignableFrom). Issue #23616 was created to track inlining interface array case, and that issue was referenced from the code in the latest force push.

@mpirvu mpirvu left a comment

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.

Remote compilations with JITServer should work in theory because we know the value of the j9class that the client wants to use. However, dereferencing the j9class pointer in the codegen should be avoided. For some of the dereferences we already have frontend queries we could use, while for others we ned to create such frontend queries.

&& cg->comp()->target().is64Bit()) {
TR_OpaqueClassBlock *castClassComponentClass = fej9->getComponentClassFromArrayClass(clazz);

J9Class *castClassLeafJ9Class = ((J9ArrayClass *)clazz)->leafComponentType;

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.

Suggestion: use TR_J9VM::getLeafComponentClassFromArrayClass(TR_OpaqueClassBlock * arrayClass) instead of directly accessing a J9Class which would not work on JITServer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

// is not castable. Fall through to update the cache.
// ----------------------------------------------------------------------

if (J9CLASS_IS_MIXED(castClassLeafJ9Class)) {

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.

For JITServer we would need a frontend query for J9CLASS_IS_MIXED

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

generateX86MemoryReference(objectClassReg, offsetof(J9ArrayClass, arity), cg), cg);

generateRegImmInstruction(TR::InstOpCode::CMP8RegImm4, node, scratchReg,
(int32_t)(((J9ArrayClass *)clazz)->arity), cg);

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.

JITServer would need a frontend quert for clazz->arity

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.


#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES)
J9Class *castClassJ9Class = TR::Compiler->cls.convertClassOffsetToClassPtr(clazz);
if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(castClassJ9Class)) {

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.

#define J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(clazz) J9_ARE_ALL_BITS_SET((clazz)->classFlags, J9ClassArrayIsNullRestricted)
JITServer will need a frontend query for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

// Skip the subclass check if the castClassLeaf is final
// ----------------------------------------------------------------------

bool castClassLeafIsInterface = J9ROMCLASS_IS_INTERFACE(castClassLeafJ9Class->romClass);

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.

For JITServer benefit it's better to use J9::ClassEnv::romClassOf(TR_OpaqueClassBlock * clazz) for castClassLeafJ9Class->romClass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

@a7ehuo a7ehuo left a comment

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.

Thank you for the update! Here are my comments.

Comment on lines +4318 to +4319
if (clazz && TR::Compiler->cls.isClassArray(comp, clazz) && !isRelocatableCompile
&& cg->comp()->target().is64Bit()) {

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.

Just trying to understand, the original optimization for checkcast applies to isRelocatableCompile and 32-bit x86 as well. Now this new condition check narrows it to !isRelocatableCompile and target().is64Bit(). Is it intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was intentional, but I've changed my reasoning and went back to the way it was originally. The first case is enabled for 32-bit and AOT/JitServer, and the two new cases apply only to 64-bit and non-AOT/JitServer (more implementation and testing work needs to be done).

Comment on lines +4377 to +4378
// If the object is NULL, no exception is thrown for a checkcast and a 0
// is returned for an instanceof.

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.

Could the comment be updated to explain why we don't need to worry about throwing exception for isAssignableFrom(null) case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +4544 to +4545
uintptr_t clazzAddress = (uintptr_t)clazz;
J9Class *castClass = TR::Compiler->cls.convertClassOffsetToClassPtr(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.

castClass is not used in this commit

Comment on lines +4838 to +4854
generateRegMemInstruction(TR::InstOpCode::L8RegMem, node, scratchReg,
generateX86MemoryReference(objectClassReg, offsetof(J9Class, castClassCache), cg), cg);

if (use64BitClasses) {
if (IS_32BIT_SIGNED(clazzAddress)) {
generateRegImmInstruction(TR::InstOpCode::XOR8RegImm4, node, scratchReg, (int32_t)clazzAddress, cg);
} else {
if (!scratchReg2)
scratchReg2 = cg->allocateRegister();
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, node, scratchReg2, clazzAddress, cg);
generateRegRegInstruction(TR::InstOpCode::XOR8RegReg, node, scratchReg, scratchReg2, cg);
}
} else {
generateRegImmInstruction(TR::InstOpCode::XOR4RegImm4, node, scratchReg, (int32_t)clazzAddress, cg);
}

generateRegImmInstruction(TR::InstOpCode::TEST8RegImm4, node, scratchReg, ~1, cg);

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.

Would this be a problem if use64BitClasses is false?

L8RegMem scratchReg, [objectClass->castClassCache] // scratchReg was loaded with L8RegMem, the upper 32 bits can contain unrelated data
XOR4RegImm4 scratchReg, clazz32 // xor 32-bit
TEST8RegImm4 scratchReg, ~1 // test 64-bit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think so. On 64-bit, the castClassCache field is a UDATA which is 64-bits, and when it is populated the full 64-bits are written. Since your last review, one of the paths in generateCastClassCacheUpdate was fixed to write 8 bytes, and now all paths through that code write a full 8 bytes. VM code always writes 8 bytes into this field.

For the XOR and TEST instructions, the operation is 64-bits and the 32-bit immediates are sign extended to 64-bits.

Comment on lines +4928 to +4930
generateRegMemInstruction(TR::InstOpCode::L8RegMem, node, objectClassLeafReg,
generateX86MemoryReference(objectClassReg, offsetof(J9ArrayClass, leafComponentType), cg), cg);

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.

Does it need to consider use64BitClasses as false case for leafComponentType?

There is a bit inconsistency on when use64BitClasses should be considered: for example, when loading castClassCache in generateCastClassCacheUpdate, it does consider it. But in generateInlinedCheckCastOrInstanceOfForArrayClass, it doesn't. Then here we have J9ArrayClass.leafComponentType case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No. The use64BitClasses in generateCastClassCacheUpdate is used to understand the size of the cast class address and whether it will fit in a 32-bit immediate or not (and hence 32-bit immediate form instructions can be used). The castClassCache is a UDATA and the class should be stored as 8 bytes on 64-bit. Since your last review, one of the paths in generateCastClassCacheUpdate was fixed to write 8 bytes, and now all paths through that code write a full 8 bytes. VM code always writes 8 bytes into this field.

* Handles class addresses of different sizes.
*
* @param[in] objectClassReg : register containing the destination object class
* @param[in[ clazzAddress : the class address to update in the cache

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.

@param[in[ -> @param[in]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FIxed.

generateRegImmInstruction(TR::InstOpCode::XOR4RegImm4, node, scratchReg, (int32_t)clazzAddress, cg);
}

generateRegImmInstruction(TR::InstOpCode::TEST8RegImm4, node, scratchReg, ~1, cg);

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.

Could the magic numbers be defined as something like CAST_CLASS_CACHE_CASTABLE_BIT = 1 and CAST_CLASS_CACHE_MASK = ~1 for improved readability?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
}

static void generateInlinedCheckCastOrInstanceOfForArrayClass(TR::Node *node, TR_OpaqueClassBlock *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.

This method has gone rather long and complex with all three cases. Is it possible to refactor them into three separate functions for the 3 case: InlineObjectArrayCastClass, InlineFinalArrayCastClass, and InlineArrayExactCastClass?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. Fixed.

Comment on lines +4349 to +4351
static char *disableInlineObjectArrayCheck = feGetEnv("TR_DisableInlineObjectArrayCheck");
static char *disableInlineFinalArrayCastClass = feGetEnv("TR_DisableInlineFinalArrayClass");
static char *disableInlineArrayExactCastClass = feGetEnv("TR_DisableInlineArrayExactCastClass");

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.

Should TR_DisableInlineObjectArrayCheck be updated as something like TR_DisableInlineObjectArrayCaseClass to be more consistent as case 2 & 3?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

// or not and exit appropriately.
// ----------------------------------------------------------------------

generateRegImmInstruction(TR::InstOpCode::TEST8RegImm4, node, scratchReg, 1, cg);

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.

Similar question, should TEST here also consider use64BitClasses?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, by same rationale above.

@vijaysun-omr

Copy link
Copy Markdown
Contributor

I feel that this can be merged once the existing set of comments is addressed. So I am giving my approval for it to be actioned by Annabelle at her judgement (unless a non-trivial point comes out in response to the last set of comments).

@0xdaryl 0xdaryl left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Comments addressed in force push. The work for JitServer/AOT will be completed in a second phase (#23510).

* Handles class addresses of different sizes.
*
* @param[in] objectClassReg : register containing the destination object class
* @param[in[ clazzAddress : the class address to update in the cache

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FIxed.

// Skip the subclass check if the castClassLeaf is final
// ----------------------------------------------------------------------

bool castClassLeafIsInterface = J9ROMCLASS_IS_INTERFACE(castClassLeafJ9Class->romClass);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.


#if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES)
J9Class *castClassJ9Class = TR::Compiler->cls.convertClassOffsetToClassPtr(clazz);
if (J9_IS_J9ARRAYCLASS_NULL_RESTRICTED(castClassJ9Class)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

generateX86MemoryReference(objectClassReg, offsetof(J9ArrayClass, arity), cg), cg);

generateRegImmInstruction(TR::InstOpCode::CMP8RegImm4, node, scratchReg,
(int32_t)(((J9ArrayClass *)clazz)->arity), cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

generateLabelInstruction(TR::InstOpCode::JNE4, node, castClassCacheMissLabel, cg);

// ----------------------------------------------------------------------
// objectClass was found in the cache. Determine whether it was castable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The low-bit on the class address stored in the cache indicates whether the cast succeeds or fails. So, for a class to be "cached but not castable" means it is stored in the cache but is low-tagged.

&& cg->comp()->target().is64Bit()) {
TR_OpaqueClassBlock *castClassComponentClass = fej9->getComponentClassFromArrayClass(clazz);

J9Class *castClassLeafJ9Class = ((J9ArrayClass *)clazz)->leafComponentType;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you. I will defer this to a follow-on PR for AOT/JitServer support.

Comment on lines +4928 to +4930
generateRegMemInstruction(TR::InstOpCode::L8RegMem, node, objectClassLeafReg,
generateX86MemoryReference(objectClassReg, offsetof(J9ArrayClass, leafComponentType), cg), cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No. The use64BitClasses in generateCastClassCacheUpdate is used to understand the size of the cast class address and whether it will fit in a 32-bit immediate or not (and hence 32-bit immediate form instructions can be used). The castClassCache is a UDATA and the class should be stored as 8 bytes on 64-bit. Since your last review, one of the paths in generateCastClassCacheUpdate was fixed to write 8 bytes, and now all paths through that code write a full 8 bytes. VM code always writes 8 bytes into this field.

generateRegImmInstruction(TR::InstOpCode::XOR4RegImm4, node, scratchReg, (int32_t)clazzAddress, cg);
}

generateRegImmInstruction(TR::InstOpCode::TEST8RegImm4, node, scratchReg, ~1, cg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
}

static void generateInlinedCheckCastOrInstanceOfForArrayClass(TR::Node *node, TR_OpaqueClassBlock *clazz,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. Fixed.

Comment on lines +4318 to +4319
if (clazz && TR::Compiler->cls.isClassArray(comp, clazz) && !isRelocatableCompile
&& cg->comp()->target().is64Bit()) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was intentional, but I've changed my reasoning and went back to the way it was originally. The first case is enabled for 32-bit and AOT/JitServer, and the two new cases apply only to 64-bit and non-AOT/JitServer (more implementation and testing work needs to be done).

@0xdaryl

0xdaryl commented Mar 26, 2026

Copy link
Copy Markdown
Contributor Author

Jenkins test sanity xlinux,win,osx jdk17

@vijaysun-omr

Copy link
Copy Markdown
Contributor

Thanks for the changes, I approve

@0xdaryl

0xdaryl commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Jenkins test sanity xlinux,win,osx jdk17

@0xdaryl

0xdaryl commented Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

@a7ehuo : would you be able to sign off on these changes by end of week?

@a7ehuo a7ehuo left a comment

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.

LGTM. Thank you very much for the update and clarification!

@0xdaryl

0xdaryl commented Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the very thorough review @a7ehuo. It was very helpful.

@vijaysun-omr : I think this can be merged at your convenience.

0xdaryl added 3 commits March 31, 2026 11:16
…or [jlO

Extend the current x86 checkcast optimization when the cast class is a
java/lang/Object array to handle instanceof and Class.isAssignableFrom()
cases.

Also, move the inlining logic into a separate static function.

Signed-off-by: Daryl Maier <maier@ca.ibm.com>
…al leaf class

Signed-off-by: Daryl Maier <maier@ca.ibm.com>
Essentially inlines the checkcast/instanceof/isAssignableFrom() sequence when the
cast class is an array that the VM implements [1], but specializes it for when the
cast class is an array known at compile-time.  It performs:

* An exact equality check
* A check for a match in the cast class cache
* An arity check followed by a subclass check on the array leaf components when the
  arities are the same
* Updates the cast class cache on success/failure in the same manner that the VM
  implementation does

Any “unusual” cases are punted to the VM (e.g., mismatched arities, when the leaf
class is an interface), as well as to throw the CastClassException if required.

The opt can be disabled by setting `TR_DisableInlineArrayExactCastClass`.

[1] https://github.com/eclipse-openj9/openj9/blob/9698be0c19fdc436c2fd6a0588dbafcfc0dd76fa/runtime/oti/VMHelpers.hpp#L589

Signed-off-by: Daryl Maier <maier@ca.ibm.com>
@0xdaryl

0xdaryl commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

Force push only included changes to comments and a rebase. I don't believe re-running CI is necessary.

@vijaysun-omr

Copy link
Copy Markdown
Contributor

Thanks for all these changes and the review responses. Merging.

@vijaysun-omr
vijaysun-omr merged commit d89c3bb into eclipse-openj9:master Mar 31, 2026
3 checks passed
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 9, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 10, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 10, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 13, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 14, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 15, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 20, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 20, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 21, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 22, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 23, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 24, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 24, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
KavinSatheeskumar added a commit to KavinSatheeskumar/openj9 that referenced this pull request Apr 24, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
dev-koan pushed a commit to dev-koan/openj9 that referenced this pull request May 27, 2026
…upport to PR#23543

Replace J9Class* access with corresponding front end queries to enable JITServer support.

A follow up to this PR

eclipse-openj9#23383

Specifically these comments

eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
eclipse-openj9#23383 (comment)
dev-koan pushed a commit to dev-koan/openj9 that referenced this pull request May 27, 2026
Add support for SVM AOT to array check cast optimizations for x86.

A follow up to this PR

eclipse-openj9#23383

To address the issues raised in this issue

eclipse-openj9#23510

Very much in the same vein as this PR

eclipse-openj9#23662
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants