RFR: 8333893: Optimization for StringBuilder append boolean & null [v13]

Emanuel Peter epeter at openjdk.org
Tue Jun 25 17:17:23 UTC 2024


On Sun, 23 Jun 2024 08:47:25 GMT, Shaojin Wen <duke at openjdk.org> wrote:

>> After PR https://github.com/openjdk/jdk/pull/16245, C2 optimizes stores into primitive arrays by combining values ​​into larger stores.
>> 
>> This PR rewrites the code of appendNull and append(boolean) methods so that these two methods can be optimized by C2.
>
> Shaojin Wen has updated the pull request incrementally with one additional commit since the last revision:
> 
>   private static final field `UNSAFE`

I'm running your benchmark [PutCharTest.java](https://github.com/user-attachments/files/15974672/PutCharTest.txt) with:

/oracle-work/jdk-fork2/build/linux-x64-slowdebug/jdk/bin/java --add-modules java.base --add-exports java.base/jdk.internal.misc=ALL-UNNAMED --add-exports java.base/jdk.internal.util=ALL-UNNAMED -XX:+TraceMergeStores -Xbatch -XX:CompileCommand=printcompilation,PutCharTest::* -XX:CompileCommand=compileonly,PutCharTest::put* -XX:+PrintIdeal PutCharTest.java


Aha, I found the limitation in `MergeStores`:
https://github.com/openjdk/jdk/blob/f7862bd6b9994814c6dfd43d471122408601f288/src/hotspot/share/opto/memnode.cpp#L2982-L2986

Basically, I require the `store` to have the same data-size as the element-size of the array.
But the `putChar` ends up as a 2-byte `StoreC`, and the array is a `byte[]` with 1-byte elements.

I quickly removed this check:

diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
index d0b6c59637f..78efda2db13 100644
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -2980,10 +2980,10 @@ StoreNode* MergePrimitiveArrayStores::run() {
     return nullptr;
   }
   BasicType bt = aryptr_t->elem()->array_element_basic_type();
-  if (!is_java_primitive(bt) ||
-      type2aelembytes(bt) != _store->memory_size()) {
-    return nullptr;
-  }
+  //if (!is_java_primitive(bt) ||
+  //    type2aelembytes(bt) != _store->memory_size()) {
+  //  return nullptr;
+  //}
 
   // The _store must be the "last" store in a chain. If we find a use we could merge with
   // then that use or a store further down is the "last" store.
@@ -3033,13 +3033,13 @@ bool MergePrimitiveArrayStores::is_compatible_store(const StoreNode* other_store
   if (!is_java_primitive(aryptr_bt1) || !is_java_primitive(aryptr_bt2)) {
     return false;
   }
-  int size1 = type2aelembytes(aryptr_bt1);
-  int size2 = type2aelembytes(aryptr_bt2);
-  if (size1 != size2 ||
-      size1 != _store->memory_size() ||
-      _store->memory_size() != other_store->memory_size()) {
-    return false;
-  }
+  //int size1 = type2aelembytes(aryptr_bt1);
+  //int size2 = type2aelembytes(aryptr_bt2);
+  //if (size1 != size2 ||
+  //    size1 != _store->memory_size() ||
+  //    _store->memory_size() != other_store->memory_size()) {
+  //  return false;
+  //}
   return true;
 }
 


And now it optimizes:


15523  103    b  4       PutCharTest::putNull (14 bytes)
[TraceMergeStores]: Replace
  74  StoreC  === 62 7 73 22  [[ 99 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact+any *, idx=4; mismatched unsafe  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4; !jvms: PutCharTest::putChar @ bci:14 (line 14) PutCharTest::putCharsAt @ bci:4 (line 7) PutCharTest::putNull @ bci:10 (line 23)
  99  StoreC  === 62 74 98 23  [[ 124 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact+any *, idx=4; mismatched unsafe  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4; !jvms: PutCharTest::putChar @ bci:14 (line 14) PutCharTest::putCharsAt @ bci:13 (line 8) PutCharTest::putNull @ bci:10 (line 23)
 124  StoreC  === 62 99 123 24  [[ 150 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact+any *, idx=4; mismatched unsafe  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4; !jvms: PutCharTest::putChar @ bci:14 (line 14) PutCharTest::putCharsAt @ bci:23 (line 9) PutCharTest::putNull @ bci:10 (line 23)
 150  StoreC  === 62 124 149 24  [[ 17 ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact+any *, idx=4; mismatched unsafe  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4; !jvms: PutCharTest::putChar @ bci:14 (line 14) PutCharTest::putCharsAt @ bci:33 (line 10) PutCharTest::putNull @ bci:10 (line 23)
[TraceMergeStores]: with
 155  ConL  === 0  [[ 156 ]]  #long:30399761348886638
 156  StoreL  === 62 7 73 155  [[ ]]  @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4; mismatched  Memory: @byte[int:>=0] (java/lang/Cloneable,java/io/Serializable):NotNull:exact+any *, idx=4;


I will file an RFE to enable this use-case as well. @wenshao thanks for the standalone test, that was really helpful!

-------------

PR Comment: https://git.openjdk.org/jdk/pull/19626#issuecomment-2189509983


More information about the core-libs-dev mailing list