Currently, C2 can't eliminate the cyclic object graph even though it is useless as a whole. Doubly linked list is the simplest cyclic object graph. In the example, C2 optimizer fails to eliminate both and b even without any gc barrier.
```java
public static void test() {
var a = new Node();
var b = new Node();
a._next = b; b._next = a;
}
```
here is full output.
```
java.lang.Object::<init> (1 bytes)
JavaObject(0) GlobalEscape(GlobalEscape) NSR is NSR. Phantom object
JavaObject(1) NoEscape(NoEscape) is NSR. Null object
JavaObject(2) GlobalEscape(GlobalEscape) NSR is NSR. Constant pointer
+++++ Initial worklist for static void DoublyLinkedList.test() (ea_inv=0)
JavaObject(0) GlobalEscape(GlobalEscape) NSR [ [ ]] 1 Con === 0 [[ ]] Type:top
JavaObject(2) GlobalEscape(GlobalEscape) NSR [ [ ]] 20 ConP === 0 [[ 24 88 ]] Klass:precise DoublyLinkedList$Node: 0x00007f6bdc371b70:Constant:exact *
JavaObject(1) NoEscape(NoEscape) NSR [ [ ]] 167 ConP === 0 [[ ]] Type:null
JavaObject(1) NoEscape(NoEscape) NSR [ [ ]] 167 ConP === 0 [[ ]] Type:null
JavaObject(3) NoEscape(NoEscape) [ [ 36 ]] 24 Allocate === 5 6 7 8 1 (22 20 21 1 1 1 1 ) [[ 25 26 27 34 35 36 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:0 (line 8) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:0 (line 8)
JavaObject(4) NoEscape(NoEscape) [ [ 100 ]] 88 Allocate === 38 35 49 8 1 (22 20 21 1 1 41 1 ) [[ 89 90 91 98 99 100 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:8 (line 9) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:8 (line 9)
Field(5) NoEscape(NoEscape) oop +12 ( )[ [ ]] 155 AddP === _ 41 41 154 [[ 157 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:18 (line 11)
Field(6) NoEscape(NoEscape) oop +12 ( )[ [ ]] 158 AddP === _ 105 105 154 [[ 160 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:23 (line 11)
LocalVar(7) GlobalEscape(GlobalEscape) [ [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
LocalVar(8) NoEscape(NoEscape) [ 24P [ 41 ]] 36 Proj === 24 [[ 37 41 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(9) NoEscape(NoEscape) [ 88P [ 105 ]] 100 Proj === 88 [[ 101 105 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(10) NoEscape(NoEscape) [ 36 [ 159 ]] 41 CheckCastPP === 38 36 [[ 155 155 159 88 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(11) NoEscape(NoEscape) [ 100 [ 156 ]] 105 CheckCastPP === 102 100 [[ 156 158 158 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(12) NoEscape(NoEscape) [ [ ]] 115 Phi === 108 33 97 [[ 166 ]] Oop:java/lang/Throwable (java/io/Serializable):NotNull * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(13) NoEscape(NoEscape) [ 41 [ ]] 159 EncodeP === _ 41 [[ 160 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:23 (line 11)
LocalVar(14) NoEscape(NoEscape) [ 105 [ ]] 156 EncodeP === _ 105 [[ 157 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:18 (line 11)
+++++ Calculating escape states and scalar replaceability
LocalVar(12) NoEscape(NoEscape) -> GlobalEscape(NoEscape) propagated from: LocalVar(7) GlobalEscape(GlobalEscape) [ 115 [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
LocalVar(12) GlobalEscape(NoEscape) -> GlobalEscape(GlobalEscape) propagated from: LocalVar(7) GlobalEscape(GlobalEscape) [ 115 [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
JavaObject(3) NoEscape(NoEscape) is NSR. is merged with other object: JavaObject(1) NoEscape(NoEscape) NSR
JavaObject(1) NoEscape(NoEscape) NSR is NSR. is merged with other object: JavaObject(3) NoEscape(NoEscape) NSR
JavaObject(4) NoEscape(NoEscape) is NSR. is stored into field with NSR base
======== Connection graph for DoublyLinkedList::test
invocation #0: 2 iterations and 0.000009 sec to build connection graph with 169 nodes and worklist size 16
JavaObject(3) NoEscape(NoEscape) NSR [ 155F [ 36 41 159 158 ]] 24 Allocate === 5 6 7 8 1 (22 20 21 1 1 1 1 ) [[ 25 26 27 34 35 36 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:0 (line 8) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(8) NoEscape(NoEscape) [ 24P [ 41 ]] 36 Proj === 24 [[ 37 41 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(10) NoEscape(NoEscape) [ 36 24P [ 159 155b ]] 41 CheckCastPP === 38 36 [[ 155 155 159 88 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(13) NoEscape(NoEscape) [ 41 24P [ 158 ]] 159 EncodeP === _ 41 [[ 160 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:23 (line 11)
Field(6) NoEscape(NoEscape) oop +12 ( 105 88P )[ 159 24P 167P [ ]] 158 AddP === _ 105 105 154 [[ 160 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:23 (line 11)
JavaObject(4) NoEscape(NoEscape) NSR [ 158F [ 100 105 156 155 ]] 88 Allocate === 38 35 49 8 1 (22 20 21 1 1 41 1 ) [[ 89 90 91 98 99 100 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:8 (line 9) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(9) NoEscape(NoEscape) [ 88P [ 105 ]] 100 Proj === 88 [[ 101 105 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(11) NoEscape(NoEscape) [ 100 88P [ 156 158b ]] 105 CheckCastPP === 102 100 [[ 156 158 158 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(14) NoEscape(NoEscape) [ 105 88P [ 155 ]] 156 EncodeP === _ 105 [[ 157 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:18 (line 11)
Field(5) NoEscape(NoEscape) oop +12 ( 41 24P )[ 156 88P 167P [ ]] 155 AddP === _ 41 41 154 [[ 157 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:18 (line 11)
=== No allocations eliminated for DoublyLinkedList::test since there are no scalar replaceable candidates ===
```
there are multiple reasons.
1. EA marks both a NSR because it merges with Null object. same as var b.
JavaObject(3) NoEscape(NoEscape) is NSR. is merged with other object: JavaObject(1) NoEscape(NoEscape) NSR
2. Scalar Replacement can't support cyclic object graph. deoptimization need to support rematerialization of cyclic object graph.
3. with non-trivial gc, we found that gc barrier may interfere with EA.
Doubly linked list is not uncommon in java even though Java developers may not be aware of it sometimes. eg. Stream framework AbstractPipeline uses Doubly linked list to form a pipeline. In javac, ClassSymbol and ClassType also forms a doubly linked list
```java
public static void test() {
var a = new Node();
var b = new Node();
a._next = b; b._next = a;
}
```
here is full output.
```
java.lang.Object::<init> (1 bytes)
JavaObject(0) GlobalEscape(GlobalEscape) NSR is NSR. Phantom object
JavaObject(1) NoEscape(NoEscape) is NSR. Null object
JavaObject(2) GlobalEscape(GlobalEscape) NSR is NSR. Constant pointer
+++++ Initial worklist for static void DoublyLinkedList.test() (ea_inv=0)
JavaObject(0) GlobalEscape(GlobalEscape) NSR [ [ ]] 1 Con === 0 [[ ]] Type:top
JavaObject(2) GlobalEscape(GlobalEscape) NSR [ [ ]] 20 ConP === 0 [[ 24 88 ]] Klass:precise DoublyLinkedList$Node: 0x00007f6bdc371b70:Constant:exact *
JavaObject(1) NoEscape(NoEscape) NSR [ [ ]] 167 ConP === 0 [[ ]] Type:null
JavaObject(1) NoEscape(NoEscape) NSR [ [ ]] 167 ConP === 0 [[ ]] Type:null
JavaObject(3) NoEscape(NoEscape) [ [ 36 ]] 24 Allocate === 5 6 7 8 1 (22 20 21 1 1 1 1 ) [[ 25 26 27 34 35 36 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:0 (line 8) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:0 (line 8)
JavaObject(4) NoEscape(NoEscape) [ [ 100 ]] 88 Allocate === 38 35 49 8 1 (22 20 21 1 1 41 1 ) [[ 89 90 91 98 99 100 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:8 (line 9) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:8 (line 9)
Field(5) NoEscape(NoEscape) oop +12 ( )[ [ ]] 155 AddP === _ 41 41 154 [[ 157 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:18 (line 11)
Field(6) NoEscape(NoEscape) oop +12 ( )[ [ ]] 158 AddP === _ 105 105 154 [[ 160 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:23 (line 11)
LocalVar(7) GlobalEscape(GlobalEscape) [ [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
LocalVar(8) NoEscape(NoEscape) [ 24P [ 41 ]] 36 Proj === 24 [[ 37 41 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(9) NoEscape(NoEscape) [ 88P [ 105 ]] 100 Proj === 88 [[ 101 105 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(10) NoEscape(NoEscape) [ 36 [ 159 ]] 41 CheckCastPP === 38 36 [[ 155 155 159 88 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(11) NoEscape(NoEscape) [ 100 [ 156 ]] 105 CheckCastPP === 102 100 [[ 156 158 158 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(12) NoEscape(NoEscape) [ [ ]] 115 Phi === 108 33 97 [[ 166 ]] Oop:java/lang/Throwable (java/io/Serializable):NotNull * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(13) NoEscape(NoEscape) [ 41 [ ]] 159 EncodeP === _ 41 [[ 160 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:23 (line 11)
LocalVar(14) NoEscape(NoEscape) [ 105 [ ]] 156 EncodeP === _ 105 [[ 157 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:18 (line 11)
+++++ Calculating escape states and scalar replaceability
LocalVar(12) NoEscape(NoEscape) -> GlobalEscape(NoEscape) propagated from: LocalVar(7) GlobalEscape(GlobalEscape) [ 115 [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
LocalVar(12) GlobalEscape(NoEscape) -> GlobalEscape(GlobalEscape) propagated from: LocalVar(7) GlobalEscape(GlobalEscape) [ 115 [ ]] 166 Rethrow === 108 109 31 8 9 exception 115 [[ 0 ]] Type:bottom
JavaObject(3) NoEscape(NoEscape) is NSR. is merged with other object: JavaObject(1) NoEscape(NoEscape) NSR
JavaObject(1) NoEscape(NoEscape) NSR is NSR. is merged with other object: JavaObject(3) NoEscape(NoEscape) NSR
JavaObject(4) NoEscape(NoEscape) is NSR. is stored into field with NSR base
======== Connection graph for DoublyLinkedList::test
invocation #0: 2 iterations and 0.000009 sec to build connection graph with 169 nodes and worklist size 16
JavaObject(3) NoEscape(NoEscape) NSR [ 155F [ 36 41 159 158 ]] 24 Allocate === 5 6 7 8 1 (22 20 21 1 1 1 1 ) [[ 25 26 27 34 35 36 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:0 (line 8) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(8) NoEscape(NoEscape) [ 24P [ 41 ]] 36 Proj === 24 [[ 37 41 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(10) NoEscape(NoEscape) [ 36 24P [ 159 155b ]] 41 CheckCastPP === 38 36 [[ 155 155 159 88 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:0 (line 8)
LocalVar(13) NoEscape(NoEscape) [ 41 24P [ 158 ]] 159 EncodeP === _ 41 [[ 160 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:23 (line 11)
Field(6) NoEscape(NoEscape) oop +12 ( 105 88P )[ 159 24P 167P [ ]] 158 AddP === _ 105 105 154 [[ 160 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:23 (line 11)
JavaObject(4) NoEscape(NoEscape) NSR [ 158F [ 100 105 156 155 ]] 88 Allocate === 38 35 49 8 1 (22 20 21 1 1 41 1 ) [[ 89 90 91 98 99 100 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, top, bool ) DoublyLinkedList::test @ bci:8 (line 9) Type:{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:rawptr:NotNull} !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(9) NoEscape(NoEscape) [ 88P [ 105 ]] 100 Proj === 88 [[ 101 105 ]] #5 Type:rawptr:NotNull !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(11) NoEscape(NoEscape) [ 100 88P [ 156 158b ]] 105 CheckCastPP === 102 100 [[ 156 158 158 ]] Oop:DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:8 (line 9)
LocalVar(14) NoEscape(NoEscape) [ 105 88P [ 155 ]] 156 EncodeP === _ 105 [[ 157 ]] Type:narrowoop: DoublyLinkedList$Node:NotNull:exact * !jvms: DoublyLinkedList::test @ bci:18 (line 11)
Field(5) NoEscape(NoEscape) oop +12 ( 41 24P )[ 156 88P 167P [ ]] 155 AddP === _ 41 41 154 [[ 157 ]] Oop:DoublyLinkedList$Node:NotNull:exact+12 * [narrow] !jvms: DoublyLinkedList::test @ bci:18 (line 11)
=== No allocations eliminated for DoublyLinkedList::test since there are no scalar replaceable candidates ===
```
there are multiple reasons.
1. EA marks both a NSR because it merges with Null object. same as var b.
JavaObject(3) NoEscape(NoEscape) is NSR. is merged with other object: JavaObject(1) NoEscape(NoEscape) NSR
2. Scalar Replacement can't support cyclic object graph. deoptimization need to support rematerialization of cyclic object graph.
3. with non-trivial gc, we found that gc barrier may interfere with EA.
Doubly linked list is not uncommon in java even though Java developers may not be aware of it sometimes. eg. Stream framework AbstractPipeline uses Doubly linked list to form a pipeline. In javac, ClassSymbol and ClassType also forms a doubly linked list