JDK-8312495 : assert(0 <= i && i < _len) failed: illegal index after JDK-8287061 on big endian platforms
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 22
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2023-07-21
  • Updated: 2023-09-04
  • Resolved: 2023-07-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 22
22 b09Fixed
Related Reports
Relates :  
Relates :  
Description
After JDK-8287061 the following assertion fails on big endian platforms (AIX, s390, Linux/PPC64be).

assert(0 <= i && i < _len) failed: illegal index at growableArray.hpp:145

Stack:

Stack: [0x00000fff6ac00000,0x00000fff6ae00000],  sp=0x00000fff6adfc280,  free space=2032k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xde9a10]  GrowableArrayView<ScopeValue*>::at(int)+0x84  (growableArray.hpp:145)
V  [libjvm.so+0x109fbd0]  ObjectMergeValue::select(frame&, RegisterMap&)+0x1dc  (debugInfo.cpp:263)
V  [libjvm.so+0x1b7e374]  ScopeDesc::objects_to_rematerialize(frame&, RegisterMap&)+0x224  (scopeDesc.cpp:150)
V  [libjvm.so+0x10d6694]  rematerialize_objects(JavaThread*, int, CompiledMethod*, frame&, RegisterMap&, GrowableArray<compiledVFrame*>*, bool&)+0x1b8  (deoptimization.cpp:333)
V  [libjvm.so+0x10d7924]  Deoptimization::fetch_unroll_info_helper(JavaThread*, int)+0x56c  (deoptimization.cpp:523)
V  [libjvm.so+0x10dfb24]  Deoptimization::uncommon_trap(JavaThread*, int, int)+0x64  (deoptimization.cpp:2572)
v  ~UncommonTrapBlob 0x00000fff8beca2f4
J 1520 c2 vm.mlvm.meth.share.transform.v2.MHCall.check()V (97 bytes) @ 0x00000fff8c63ed58 [0x00000fff8c63e900+0x0000000000000458]
j  vm.mlvm.meth.share.transform.v2.MHPrimitiveTF.computeInboundCall()Lvm/mlvm/meth/share/transform/v2/MHCall;+26
J 1392 c1 vm.mlvm.meth.share.transform.v2.MHMacroTF.addTransformation(Lvm/mlvm/meth/share/transform/v2/MHTF;)Lvm/mlvm/meth/share/transform/v2/MHCall; (95 bytes) @ 0x00000fff84bb96b4 [0x00000fff84bb9080+0x0000000000000634]
j  vm.mlvm.meth.share.MHTransformationGen.createSequence(Lvm/mlvm/meth/share/Argument;Ljava/lang/Object;Ljava/lang/invoke/MethodHandle;[Lvm/mlvm/meth/share/Argument;)Lvm/mlvm/meth/share/transform/v2/MHMacroTF;+66
j  vm.mlvm.meth.stress.compiler.sequences.Test.runThread(I)Z+34

The illegal index is always -559030609 (0xDEADDEAF)

vmTestbase/vm/mlvm/meth/stress/compiler/sequences/Test.java
vmTestbase/jit/escape/AdaptiveBlocking/AdaptiveBlocking001/AdaptiveBlocking001.java
are 2 tests that are prone to the assertion failure.

Analysis:

At https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/stackValue.cpp#L208-L213
the jint value is always put in the part with the lower address of the intptr_t value. On big endian platforms thats the location of the high word therefore the value cannot be cast directly back to jint.
Instead workarounds like these are needed:

https://github.com/openjdk/jdk/blob/8d29329138d44800ee4c0c02dacc01a06097de66/src/hotspot/share/runtime/deoptimization.cpp#L1358-L1391
https://github.com/openjdk/jdk/blob/8d29329138d44800ee4c0c02dacc01a06097de66/src/hotspot/share/runtime/deoptimization.cpp#L1489-L1519

I wonder if this is needed at all or if we could just do this instead:
return UCONST64(0xDEADDEAF00000000) | *(juint*)value_addr

EDIT: yes it is indeed needed but only on big endian. The size of a stack slot is sizeof(intptr_t). On big endian it depends on the type of an integer value where in the slot it is stored (at lower or higher address). Therefore reading the complete intptr_t and then casting it to jint does not work.

Details:

- Let intptr_t* S be the address of a stack slot

- When accessing S, interpreter and compiled code use load/store instructions matching the size of the integer value to be transferred.

- E.g. to access a jint in S, 32bit load/store instructions are used. Thats very much like casting S to jint*

- (jint)*(intptr_t*)S does not work on big endian because the jint part is in the high word of *(intptr_t*)S

- StackValue has to mimic this to be able to transfer the complete intptr_t value. E.g. `interpretedVFrame::set_locals`[1] transfers stack slot values without knowing if the value is a jint or jlong.

[1] https://github.com/openjdk/jdk/blob/842d6329cf5a3da8df7eddb195b5fcb7baadbdc3/src/hotspot/share/runtime/vframe.cpp#L456

Comments
Changeset: 8661b8e1 Author: Richard Reingruber <rrich@openjdk.org> Date: 2023-07-27 13:40:23 +0000 URL: https://git.openjdk.org/jdk/commit/8661b8e11568f752c0bc515a028092f77bcaf940
27-07-2023

ILW = Incorrect cast during rematerialization potentially leading to incorrect results, on big endian platforms, disable EA = HMM = P2
24-07-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/14976 Date: 2023-07-21 14:24:35 +0000
21-07-2023