JDK-8025937 : assert(existing_f1 == NULL || existing_f1 == f1) failed: illegal field change
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: hs25
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2013-10-04
  • Updated: 2014-06-26
  • Resolved: 2013-11-14
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 8 Other Other
8Fixed hs25Fixed hs25,port-stage-ppc-aixFixed
Related Reports
Relates :  
Relates :  
Description
#  Internal Error (/HUDSON/workspace/8-2-build-linux-amd64/jdk8/367/hotspot/src/share/vm/oops/cpCache.hpp:139), pid=13656, tid=140632431101696
#  assert(existing_f1 == NULL || existing_f1 == f1) failed: illegal field change
#
# JRE version: Java(TM) SE Runtime Environment (8.0-b110) (build 1.8.0-ea-fastdebug-b110)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.0-b52-fastdebug mixed mode linux-amd64 compressed oops)

Current thread (0x00007fe79c1f8000):  JavaThread "MainThread" [_thread_in_vm, id=13673, stack(0x00007fe78a098000,0x00007fe78a199000)]

Stack: [0x00007fe78a098000,0x00007fe78a199000],  sp=0x00007fe78a194c70,  free space=1011k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xe5ea8a]  VMError::report_and_die()+0x2da
V  [libjvm.so+0x6b4294]  report_vm_error(char const*, int, char const*, char const*)+0x84
V  [libjvm.so+0x6b0841]  ConstantPoolCacheEntry::set_itable_call(Bytecodes::Code, methodHandle, int)+0x101
V  [libjvm.so+0x8b9a85]  InterpreterRuntime::resolve_invoke(JavaThread*, Bytecodes::Code)+0x955
j  java.time.DayOfWeek.from(Ljava/time/temporal/TemporalAccessor;)Ljava/time/DayOfWeek;+16
j  tck.java.time.TCKDayOfWeek.test_factory_CalendricalObject()V+10
v  ~StubRoutines::call_stub
V  [libjvm.so+0x8c94de]  JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x188e
V  [libjvm.so+0xcd9e50]  Reflection::invoke(instanceKlassHandle, methodHandle, Handle, bool, objArrayHandle, BasicType, objArrayHandle, bool, Thread*)+0x5f0
V  [libjvm.so+0xcdaf5e]  Reflection::invoke_method(oopDesc*, Handle, objArrayHandle, Thread*)+0x1ce
V  [libjvm.so+0x9a0009]  JVM_InvokeMethod+0x1d9
j  sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j  sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j  sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j  java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+56
j  org.testng.internal.MethodInvocationHelper.invokeMethod(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+242

Reproduces with some JDK regression tests:
java/time/tck/java/time/TCKDayOfWeek.java
Comments
FYI, notes for easier reproduction and observation, you want to edit tck/java/time/TCKDayOfWeek.java to add the lines below: ----- import org.testng.TestNG; import org.testng.TestListenerAdapter; /** * Test DayOfWeek. */ @Test public class TCKDayOfWeek extends AbstractDateTimeTest { public static void main(String[] args) { TestListenerAdapter tla = new TestListenerAdapter(); TestNG testng = new TestNG(); testng.setTestClasses(new Class[] { TCKDayOfWeek.class }); testng.addListener(tla); testng.run(); } ----- To compile and run, get to the "right place" in the hierarchy (for ute, cd local/common/testbase/jtreg/8/JT_JDK/test/java/time, for jtreg, cd jdk/test/java/time) $JAVA_HOME/bin/javac -classpath .:$HOME/ute/local/common/jtreg/lib/testng.jar tck/java/time/TCKDayOfWeek.java gdb $JAVA_HOME/bin/java handle SIGSEGV nostop pass noprint break report_vm_error run -classpath .:$HOME/ute/local/common/jtreg/lib/testng.jar tck/java/time/TCKDayOfWeek 3 breakpoint keep y 0x0000000101569937 in ConstantPoolCacheEntry::set_direct_or_vtable_call(Bytecodes::Code, methodHandle, int) at cpCache.cpp:197 stop only if method._value->_vtable_index == -10
31-10-2013

I think the new problem is just between invokespecial and invokeinterface - since both can reference the same interfacemethodref for an instance method. (invokestatic should be caught before returning from the linkResolver if it finds an instance method and vice-versa ,invokespecial and invokeinterface should be caught if they find a static method). One possibility, if invokespecial is invoked with an interfacemethodref, consider instead of set_static, use set_interface in the LinkResolver, since we would have a method_holder and an itable index (or a j.l.Object method and a vtable_index just like invokeinterface would). invokespecial can return 1. local interface method 2. j.l.Object method 3. default_method - concrete method in a super interface 4. abstract method in a super interface -- just like invokeinterface can We would need to modify the cpCache logic to remove the assertion that set_itable_call is only for invokeinterface and we would need to modify invokespecial to pick up the f1/f2 values differently depending on methodref (same as today, from _f1 with method()), or interfacemethodref, as invokeinterface does: also varying depending on the change_to_virtual bit. If we pick up for invokespecial and we don't know if we had a methodref or an interfacemethodref, there may be yet another bit in the flags we could use for this. Or am I missing something?
31-10-2013

She claims to have had a Eureka moment, assign the bug to her, and I will not let this chance to toss the hot potato away pass.
31-10-2013

You can also have InterfaceMethodref cpool entries with invokestatic so that would completely clobber the cpCache if shared with an invokeinterface bytecode.
31-10-2013

The above won't work because we still get different values for _f1 and _f2 coming back into the interpreter. I was thinking of something else.
31-10-2013

So it appears that one of the specification changes for lambda is to allow invokespecial refer to JVM_CONSTANT_InterfaceMethodref entries. We create a constant pool cache entry for each JVM_CONSTANT_InterfaceMethodref entry in the constant pool. It is assumed in the cpCache that the only bytecode referring to this entry is going to be an invokeinterface and the cpCache fields are defined for the resolution information in invokeinterface. The resolution information is different for invokespecial because it usually refers to a JVM_CONSTANT_Methodref. Invokespecial can share with invokevirtual because the resolution information saved is the same. One sets bytecode 1 and the other sets bytecode 2 field in the cpCacheEntry to mark that it's resolved. Since the cpCache entry can't be shared, I suggest a simple fix of not updating the cpCache for invokespecial/InterfaceMethodref case and making the interpreter go through resolution every time. It's only an interpreter optimization which most of the time shouldn't affect throughput. We do this for a few other things. Otherwise, you'll also have to change all the interpreters if you change the meaning of the cpCache fields so that the entry for this can be shared.
31-10-2013

> And invokespecial can refer to an InterfaceMethodRef? That's going to cause a problem in the cpCache. invokespecial is used to invoke private interface methods. This is allowed in the VM but not expressible at the Java language level in Java 8.
31-10-2013

$ff/bin/java -classpath .:$HOME/ute/local/common/jtreg/lib/testng.jar -Xverify:all -Xfuture tck.java.time.TCKDayOfWeek ($ff is my fastdebug build image) That would kick it out in the verifier, right? It didn't complain, it crashed in the usual way.
31-10-2013

Run -verify:all - the classfile is wrong. The invokeinterface should point to a JVM_CONSTANT_InterfaceMethodref and the verifier checks that (if I'm reading this correctly). I don't think you can lie in the linkResolver and get the wrong cpCache index. In the verifier: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: types = (_klass->major_version() < STATIC_METHOD_IN_INTERFACE_MAJOR_VERSION) ? (1 << JVM_CONSTANT_Methodref) : ((1 << JVM_CONSTANT_InterfaceMethodref) | (1 << JVM_CONSTANT_Methodref)); break; And invokespecial can refer to an InterfaceMethodRef? That's going to cause a problem in the cpCache.
31-10-2013

Coleen, no earthly idea. I don't know a lot about the rewriter or constant pool cache (yet). What I do know is that the input contains an invokeInterface and an invokeSpecial both referencing the same CP index in the classfile. The ConstantPoolCache code I read seemed to assume that this would not happen by the time the bytecodes reached it. I assume I should be looking at src/share/vm/interpreter/rewriter.*pp ?
30-10-2013

It would be useful first to know why you're sharing entries between invokespecial and invokeinterface. The sharing between invokevirtual and invokespecial is because both point to a JVM_CONSTANT_Methodref so one cpCache entry is created for that in the rewriter. The invokeinterface should point to an InterfaceMethodRef. This might have changed with corner cases of default methods? In general the invokeinterface call to default method should point to an InterfaceMethodref but maybe it's resolved incorrectly in the cpCache?
30-10-2013

Bread crumbs to the rewriter would be much appreciated. The particular problem is with invokespecial and invokeinterface; no idea if invokevirtual is also affected. David
30-10-2013

If these invariants are now invalid, then instead of cloning the cpCache entry, you add a new one for invokespecial in the rewriter.
30-10-2013

Someone appears to have broken this. // Note: invokevirtual & invokespecial bytecodes can share the same constant // pool entry and thus the same constant pool cache entry. All invoke // bytecodes but invokevirtual use only _f1 and the corresponding b1 // bytecode, while invokevirtual uses only _f2 and the corresponding // b2 bytecode. The value of _flags is shared for both types of entries. invokeinterface shouldn't share the cpCache entry.
30-10-2013

Problem: invokespecial and invokeinteface reference the same constant pool index, but they make incompatible (overlapping) use of the ConstantPoolCacheEntry for that index: 14: invokespecial #17 // InterfaceMethod java/time/temporal/TemporalAccessor.get:(Ljava/time/temporal/TemporalField;)I ... 16: invokeinterface #17, 2 // InterfaceMethod java/time/temporal/TemporalAccessor.get:(Ljava/time/temporal/TemporalField;)I Proposed fixes: Either clone the constant pool entry (how would you know to do this?) or add an additional field to the CPCE to resolve the overlap (but there's very exciting concurrency going on here, that uses field _f1 as a "all done writing flag", and because of its possible sharing, we're not *really* all done writing, so this seems to break an important invariant.)
30-10-2013

More (amusing) details -- the two field values are not even the same type: (gdb) p f1 -> print() java.time.temporal.TemporalAccessor {0x000000080004b138} - instance size: 2 - klass size: 61 - access: public interface abstract - state: fully_initialized - name: 'java/time/temporal/TemporalAccessor' - super: 'java/lang/Object' - sub: - nof implementors: 2 - arrays: 'java/time/temporal/TemporalAccessor'[] - methods: Array<T>(0x000000011dc17240) - method ordering: Array<T>(0x000000011d963048) - default_methods: Array<T>(0x0000000000000000) - local interfaces: Array<T>(0x000000011d963090) - trans. interfaces: Array<T>(0x000000011d963090) - constants: constant pool [126] {0x000000011dc16de8} for 'java/time/temporal/TemporalAccessor' cache=0x000000011dd47090 - class loader data: NULL class_loader - host class: NULL - source file: 'TemporalAccessor.java' - class annotations: Array<T>(0x0000000000000000) - class type annotations: Array<T>(0x0000000000000000) - field annotations: Array<T>(0x0000000000000000) - field type annotations: Array<T>(0x0000000000000000) - inner classes: Array<T>(0x000000011d963060) - java mirror: a 'java/lang/Class' = 'java/time/temporal/TemporalAccessor' - vtable length 5 (start addr: 0x000000080004b2f0) - itable length 0 (start addr: 0x000000080004b318) - ---- static fields (0 words): - ---- non-static fields (0 words): - non-static oop maps: $2 = void (gdb) p existing_f1 -> print() {method} - this oop: 0x000000011dc174d8 - method holder: 'java/time/temporal/TemporalAccessor' - constants: 0x000000011dc16de8 constant pool [126] {0x000000011dc16de8} for 'java/time/temporal/TemporalAccessor' cache=0x000000011dd47090 - access: 0x1 public - name: 'get' - signature: '(Ljava/time/temporal/TemporalField;)I' - max stack: 6 - max locals: 5 - size of params: 2 - method size: 12 - vtable index: -10 - i2i entry: 0x000000010501e6e0 - adapters: AHE@0x000000010487fb20: 0xbb000000 i2c: 0x0000000105106220 c2i: 0x0000000105106344 c2iUV: 0x000000010510630b - compiled entry 0x0000000105106344 - code size: 111 - code start: 0x000000011dc17428 - code end (excl): 0x000000011dc17497 - checked ex length: 0 - linenumber start: 0x000000011dc17497 - localvar length: 4 - localvar start: 0x000000011dc174a6 $3 = void
29-10-2013

Release team: We're not OK to defer this one until we know more. We're putting this on critical watch for further investigation and will not count it for ZBB. We'd like to understand if this is a valid case (and if so what it would take to fix it) or if this is a faulty assert. The asserts are meant to find invalid conditions (when using debug builds) so the fact that it doesn't happen with a release build doesn't mean it's less of a problem.
23-10-2013

ILW=HLL=P4 8-defer-request: There was a change in the implementation of java.time.temporal.TemporalQuery that changes the code path that caused this bug. This means this bug no longer reproduces on the latest repository. Without a lack of reproducible test case and the changes in the Java code for TemporalQuery I'd like to request a deferral.
23-10-2013

This bug just got a lot harder to reproduce because of an apparent wholesale change in java.time.temporal.TemporalQuery on October 15 -- my cached copy of the ute test no longer compiles. See http://hg.openjdk.java.net/jdk8/tl/jdk/rev/087c8c1d2631 .
21-10-2013

<deleted comment> This assert was changed a while back and the changed assert is now failing.
05-10-2013

This is most likely a bug introduced by the change in JDK-8014013, giving it to Compiler.
04-10-2013