[14] RFR(S): 8230019: [REDO] compiler/types/correctness/* tests fail with "assert(recv == __null || recv->is_klass()) failed: wrong type"

Christian Hagedorn christian.hagedorn at oracle.com
Fri Oct 25 17:18:17 UTC 2019


Hi Vladimir

Thank you for your review!

On 24.10.19 18:55, Vladimir Kozlov wrote:
> I would like to see what happens with MDO data with Tobias fix which 
> caused regression. If it is simple. May be from LogCompilation.

> The only explanation I can think of if we had only one receiver in MDO 
> which we cleaned up and it confuse C2 to treat call site as never 
> executed [3] so that it can't use speculative_receiver_type which come 
> from other data
It was actually my fix. I've tried to come up with a test case where the 
regression can easily be seen. I found the following example which shows 
the benefit of not clearing the counter with clear_row():

     public void test() {
         String s = "test";
         for (int i = 0; i < 1_000_000; i++ ) {
             s.replace("e", "t"); // Just a big enough method that does 
not get inlined by default
         }
     }

If we use clear_row(), then String::replace is not inlined:

org.sample.ClearRow::test @ 5 (27 bytes)   made not entrant
   @ 16   java.lang.String::replace (255 bytes)   too big

The MDO counter for String::replace is reset to zero. This profile 
information is passed to ok_to_inline [1] and the zeroed counter is 
eventually read at [2]. The following frequency check [3] fails and thus 
InlineTree::should_inline returns false because the method is too big 
(and wrongly assumed to be never called).

However, if we just use set_receiver(row, NULL) instead of clear_row(), 
which does not reset the counter, then call_site_count is big number 
greater than zero at [2] and the check at [3] succeeds. The maximum 
inline size is increased to FreqInlineSize (325 by default). Therefore 
String::replace is inlined:

org.sample.ClearRow::test @ 5 (27 bytes)   made not entrant
   @ 16   java.lang.String::replace (255 bytes)   inline (hot)


I also quickly put it into a local JMH benchmark. It can clearly be seen 
that not clearing the counter is beneficial:

set_receiver(row, NULL):
Benchmark       Mode  Cnt   Score   Error  Units
ClearRow.test  thrpt    5  42.810 ± 1.572  ops/s

clear_row():
Benchmark       Mode  Cnt   Score   Error  Units
ClearRow.test  thrpt    5  32.658 ± 1.267  ops/s


There are other cases, for example by using HashMap::put instead, in 
which call_does_dispatch is true [4]. It then skips directly to your 
mentioned check at [5]. The check succeeds having a non reset counter 
and the speculative_receiver_type can be used to inline the method.


So I guess we've seen large performance regressions due to many such 
missed inline opportunities due to lost/wrong profiling information.

Best regards,
Christian


[1] 
http://hg.openjdk.java.net/jdk/jdk/file/18c246ad2ff9/src/hotspot/share/opto/doCall.cpp#l171
[2] 
http://hg.openjdk.java.net/jdk/jdk/file/18c246ad2ff9/src/hotspot/share/opto/bytecodeInfo.cpp#l163
[3] 
http://hg.openjdk.java.net/jdk/jdk/file/18c246ad2ff9/src/hotspot/share/opto/bytecodeInfo.cpp#l170
[4] 
http://hg.openjdk.java.net/jdk/jdk/file/18c246ad2ff9/src/hotspot/share/opto/doCall.cpp#l167
[5] 
http://hg.openjdk.java.net/jdk/jdk/file/18c246ad2ff9/src/hotspot/share/opto/doCall.cpp#l205 



More information about the hotspot-compiler-dev mailing list