Skip to content

Commit 40fef23

Browse files
committedNov 26, 2021
8275908: Record null_check traps for calls and array_check traps in the interpreter
Reviewed-by: chagedorn, mdoerr
1 parent 3d810ad commit 40fef23

File tree

9 files changed

+636
-9
lines changed

9 files changed

+636
-9
lines changed
 

‎src/hotspot/share/interpreter/interpreterRuntime.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,11 @@ JRT_ENTRY(void, InterpreterRuntime::create_klass_exception(JavaThread* current,
407407
// lookup exception klass
408408
TempNewSymbol s = SymbolTable::new_symbol(name);
409409
if (ProfileTraps) {
410-
note_trap(current, Deoptimization::Reason_class_check);
410+
if (s == vmSymbols::java_lang_ArrayStoreException()) {
411+
note_trap(current, Deoptimization::Reason_array_check);
412+
} else {
413+
note_trap(current, Deoptimization::Reason_class_check);
414+
}
411415
}
412416
// create exception, with klass name as detail message
413417
Handle exception = Exceptions::new_exception(current, s, klass_name);
@@ -825,7 +829,18 @@ void InterpreterRuntime::resolve_invoke(JavaThread* current, Bytecodes::Code byt
825829
JavaThread* THREAD = current; // For exception macros.
826830
LinkResolver::resolve_invoke(info, receiver, pool,
827831
last_frame.get_index_u2_cpcache(bytecode), bytecode,
828-
CHECK);
832+
THREAD);
833+
834+
if (HAS_PENDING_EXCEPTION) {
835+
if (ProfileTraps && PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_NullPointerException()) {
836+
// Preserve the original exception across the call to note_trap()
837+
PreserveExceptionMark pm(current);
838+
// Recording the trap will help the compiler to potentially recognize this exception as "hot"
839+
note_trap(current, Deoptimization::Reason_null_check);
840+
}
841+
return;
842+
}
843+
829844
if (JvmtiExport::can_hotswap_or_post_breakpoint() && info.resolved_method()->is_old()) {
830845
resolved_method = methodHandle(current, info.resolved_method()->get_new_method());
831846
} else {

‎src/hotspot/share/opto/graphKit.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,10 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) {
580580
ex_obj = env()->ArrayIndexOutOfBoundsException_instance();
581581
break;
582582
case Deoptimization::Reason_class_check:
583-
if (java_bc() == Bytecodes::_aastore) {
584-
ex_obj = env()->ArrayStoreException_instance();
585-
} else {
586-
ex_obj = env()->ClassCastException_instance();
587-
}
583+
ex_obj = env()->ClassCastException_instance();
584+
break;
585+
case Deoptimization::Reason_array_check:
586+
ex_obj = env()->ArrayStoreException_instance();
588587
break;
589588
default:
590589
break;
@@ -3340,7 +3339,10 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass,
33403339
// It needs a null check because a null will *pass* the cast check.
33413340
// A non-null value will always produce an exception.
33423341
if (!objtp->maybe_null()) {
3343-
builtin_throw(Deoptimization::Reason_class_check, makecon(TypeKlassPtr::make(objtp->klass())));
3342+
bool is_aastore = (java_bc() == Bytecodes::_aastore);
3343+
Deoptimization::DeoptReason reason = is_aastore ?
3344+
Deoptimization::Reason_array_check : Deoptimization::Reason_class_check;
3345+
builtin_throw(reason, makecon(TypeKlassPtr::make(objtp->klass())));
33443346
return top();
33453347
} else if (!too_many_traps_or_recompiles(Deoptimization::Reason_null_assert)) {
33463348
return null_assert(obj);
@@ -3422,7 +3424,10 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass,
34223424
if (not_subtype_ctrl != top()) { // If failure is possible
34233425
PreserveJVMState pjvms(this);
34243426
set_control(not_subtype_ctrl);
3425-
builtin_throw(Deoptimization::Reason_class_check, load_object_klass(not_null_obj));
3427+
bool is_aastore = (java_bc() == Bytecodes::_aastore);
3428+
Deoptimization::DeoptReason reason = is_aastore ?
3429+
Deoptimization::Reason_array_check : Deoptimization::Reason_class_check;
3430+
builtin_throw(reason, load_object_klass(not_null_obj));
34263431
}
34273432
} else {
34283433
(*failure_control) = not_subtype_ctrl;

‎src/hotspot/share/prims/whitebox.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,72 @@ WB_ENTRY(void, WB_MakeMethodNotCompilable(JNIEnv* env, jobject o, jobject method
955955
}
956956
WB_END
957957

958+
WB_ENTRY(jint, WB_GetMethodDecompileCount(JNIEnv* env, jobject o, jobject method))
959+
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
960+
CHECK_JNI_EXCEPTION_(env, 0);
961+
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
962+
uint cnt = 0;
963+
MethodData* mdo = mh->method_data();
964+
if (mdo != NULL) {
965+
cnt = mdo->decompile_count();
966+
}
967+
return cnt;
968+
WB_END
969+
970+
// Get the trap count of a method for a specific reason. If the trap count for
971+
// that reason did overflow, this includes the overflow trap count of the method.
972+
// If 'reason' is NULL, the sum of the traps for all reasons will be returned.
973+
// This number includes the overflow trap count if the trap count for any reason
974+
// did overflow.
975+
WB_ENTRY(jint, WB_GetMethodTrapCount(JNIEnv* env, jobject o, jobject method, jstring reason_obj))
976+
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
977+
CHECK_JNI_EXCEPTION_(env, 0);
978+
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
979+
uint cnt = 0;
980+
MethodData* mdo = mh->method_data();
981+
if (mdo != NULL) {
982+
ResourceMark rm(THREAD);
983+
char* reason_str = (reason_obj == NULL) ?
984+
NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(reason_obj));
985+
bool overflow = false;
986+
for (uint reason = 0; reason < mdo->trap_reason_limit(); reason++) {
987+
if (reason_str != NULL && !strcmp(reason_str, Deoptimization::trap_reason_name(reason))) {
988+
cnt = mdo->trap_count(reason);
989+
// Count in the overflow trap count on overflow
990+
if (cnt == (uint)-1) {
991+
cnt = mdo->trap_count_limit() + mdo->overflow_trap_count();
992+
}
993+
break;
994+
} else if (reason_str == NULL) {
995+
uint c = mdo->trap_count(reason);
996+
if (c == (uint)-1) {
997+
c = mdo->trap_count_limit();
998+
if (!overflow) {
999+
// Count overflow trap count just once
1000+
overflow = true;
1001+
c += mdo->overflow_trap_count();
1002+
}
1003+
}
1004+
cnt += c;
1005+
}
1006+
}
1007+
}
1008+
return cnt;
1009+
WB_END
1010+
1011+
WB_ENTRY(jint, WB_GetDeoptCount(JNIEnv* env, jobject o, jstring reason_obj, jstring action_obj))
1012+
if (reason_obj == NULL && action_obj == NULL) {
1013+
return Deoptimization::total_deoptimization_count();
1014+
}
1015+
ResourceMark rm(THREAD);
1016+
const char *reason_str = (reason_obj == NULL) ?
1017+
NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(reason_obj));
1018+
const char *action_str = (action_obj == NULL) ?
1019+
NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(action_obj));
1020+
1021+
return Deoptimization::deoptimization_count(reason_str, action_str);
1022+
WB_END
1023+
9581024
WB_ENTRY(jint, WB_GetMethodEntryBci(JNIEnv* env, jobject o, jobject method))
9591025
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
9601026
CHECK_JNI_EXCEPTION_(env, InvocationEntryBci);
@@ -2527,6 +2593,13 @@ static JNINativeMethod methods[] = {
25272593
CC"(Ljava/lang/reflect/Executable;Z)Z", (void*)&WB_TestSetDontInlineMethod},
25282594
{CC"getMethodCompilationLevel0",
25292595
CC"(Ljava/lang/reflect/Executable;Z)I", (void*)&WB_GetMethodCompilationLevel},
2596+
{CC"getMethodDecompileCount0",
2597+
CC"(Ljava/lang/reflect/Executable;)I", (void*)&WB_GetMethodDecompileCount},
2598+
{CC"getMethodTrapCount0",
2599+
CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)I",
2600+
(void*)&WB_GetMethodTrapCount},
2601+
{CC"getDeoptCount0",
2602+
CC"(Ljava/lang/String;Ljava/lang/String;)I", (void*)&WB_GetDeoptCount},
25302603
{CC"getMethodEntryBci0",
25312604
CC"(Ljava/lang/reflect/Executable;)I", (void*)&WB_GetMethodEntryBci},
25322605
{CC"getCompileQueueSize",

‎src/hotspot/share/runtime/deoptimization.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,30 @@ jint Deoptimization::total_deoptimization_count() {
26082608
return _deoptimization_hist[Reason_none][0][0];
26092609
}
26102610

2611+
// Get the deopt count for a specific reason and a specific action. If either
2612+
// one of 'reason' or 'action' is null, the method returns the sum of all
2613+
// deoptimizations with the specific 'action' or 'reason' respectively.
2614+
// If both arguments are null, the method returns the total deopt count.
2615+
jint Deoptimization::deoptimization_count(const char *reason_str, const char *action_str) {
2616+
if (reason_str == NULL && action_str == NULL) {
2617+
return total_deoptimization_count();
2618+
}
2619+
juint counter = 0;
2620+
for (int reason = 0; reason < Reason_LIMIT; reason++) {
2621+
if (reason_str == NULL || !strcmp(reason_str, trap_reason_name(reason))) {
2622+
for (int action = 0; action < Action_LIMIT; action++) {
2623+
if (action_str == NULL || !strcmp(action_str, trap_action_name(action))) {
2624+
juint* cases = _deoptimization_hist[reason][1+action];
2625+
for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
2626+
counter += cases[bc_case] >> LSB_BITS;
2627+
}
2628+
}
2629+
}
2630+
}
2631+
}
2632+
return counter;
2633+
}
2634+
26112635
void Deoptimization::print_statistics() {
26122636
juint total = total_deoptimization_count();
26132637
juint account = total;
@@ -2661,6 +2685,14 @@ const char* Deoptimization::trap_reason_name(int reason) {
26612685
return "unknown";
26622686
}
26632687

2688+
jint Deoptimization::total_deoptimization_count() {
2689+
return 0;
2690+
}
2691+
2692+
jint Deoptimization::deoptimization_count(const char *reason_str, const char *action_str) {
2693+
return 0;
2694+
}
2695+
26642696
void Deoptimization::print_statistics() {
26652697
// no output
26662698
}

‎src/hotspot/share/runtime/deoptimization.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ class Deoptimization : AllStatic {
433433
int trap_request);
434434

435435
static jint total_deoptimization_count();
436+
static jint deoptimization_count(const char* reason_str, const char* action_str);
436437

437438
// JVMTI PopFrame support
438439

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8275908
27+
* @summary Record null_check traps for calls and array_check traps in the interpreter
28+
*
29+
* @requires vm.compiler2.enabled & vm.compMode != "Xcomp"
30+
*
31+
* @library /test/lib
32+
* @build jdk.test.whitebox.WhiteBox
33+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
34+
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
35+
* -XX:+UseSerialGC -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation
36+
* -XX:CompileCommand=compileonly,compiler.exceptions.OptimizeImplicitExceptions::throwImplicitException
37+
* compiler.exceptions.OptimizeImplicitExceptions
38+
*/
39+
40+
package compiler.exceptions;
41+
42+
import java.lang.reflect.Method;
43+
import java.util.HashMap;
44+
45+
import jdk.test.lib.Asserts;
46+
import jdk.test.whitebox.WhiteBox;
47+
48+
public class OptimizeImplicitExceptions {
49+
// ImplicitException represents the various implicit (aka. 'built-in') exceptions
50+
// which can be thrown implicitely by the JVM when executing bytecodes.
51+
public enum ImplicitException {
52+
// NullPointerException during field access
53+
NULL_POINTER_EXCEPTION("null_check"),
54+
// NullPointerException during invoke
55+
INVOKE_NULL_POINTER_EXCEPTION("null_check"),
56+
ARITHMETIC_EXCEPTION("div0_check"),
57+
ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION("range_check"),
58+
ARRAY_STORE_EXCEPTION("array_check"),
59+
CLASS_CAST_EXCEPTION("class_check");
60+
private final String reason;
61+
ImplicitException(String reason) {
62+
this.reason = reason;
63+
}
64+
public String getReason() {
65+
return reason;
66+
}
67+
}
68+
// TestMode represents a specific combination of the OmitStackTraceInFastThrow command line options.
69+
// They will be set up in 'setFlags(TestMode testMode)' before a new test run starts.
70+
public enum TestMode {
71+
OMIT_STACKTRACES_IN_FASTTHROW,
72+
STACKTRACES_IN_FASTTHROW
73+
}
74+
75+
private static final WhiteBox WB = WhiteBox.getWhiteBox();
76+
// The number of deoptimizations after which a method will be made not-entrant
77+
private static final int PerBytecodeTrapLimit = WB.getIntxVMFlag("PerBytecodeTrapLimit").intValue();
78+
// The number of interpreter invocations after which a decompiled method will be re-compiled.
79+
private static final int Tier0InvokeNotifyFreq = (int)Math.pow(2, WB.getIntxVMFlag("Tier0InvokeNotifyFreqLog"));
80+
// The following variables are used to track the value of the global deopt counters between the various test phases.
81+
private static int oldDeoptCount = 0;
82+
private static HashMap<String, Integer> oldDeoptCountReason = new HashMap<String, Integer>(ImplicitException.values().length);
83+
// The following two objects are declared statically to simplify the test method.
84+
private static String[] string_a = new String[1];
85+
private static final Object o = new Object();
86+
87+
// This is the main test method. It will repeatedly called with the same ImplicitException 'type' to
88+
// JIT-compile it, deoptimized it, re-compile it again and do various checks on the way.
89+
// This process will be repeated then for each kind of ImplicitException 'type'.
90+
public static Object throwImplicitException(ImplicitException type, Object[] object_a) {
91+
switch (type) {
92+
case NULL_POINTER_EXCEPTION: {
93+
return object_a.length;
94+
}
95+
case INVOKE_NULL_POINTER_EXCEPTION: {
96+
return object_a.hashCode();
97+
}
98+
case ARITHMETIC_EXCEPTION: {
99+
return ((42 / (object_a.length - 1)) > 2) ? null : object_a[0];
100+
}
101+
case ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION: {
102+
return object_a[5];
103+
}
104+
case ARRAY_STORE_EXCEPTION: {
105+
return (object_a[0] = o);
106+
}
107+
case CLASS_CAST_EXCEPTION: {
108+
return (ImplicitException[])object_a;
109+
}
110+
}
111+
return null;
112+
}
113+
114+
// Completely unload (i.e. make "not-entrant"->"zombie"->"unload/free") a JIT-compiled
115+
// version of a method and clear the method's profiling counters.
116+
private static void unloadAndClean(Method m) {
117+
WB.deoptimizeMethod(m); // Makes the nmethod "not entrant".
118+
WB.forceNMethodSweep(); // Makes all "not entrant" nmethods "zombie". This requires
119+
WB.forceNMethodSweep(); // two sweeps, see 'nmethod::can_convert_to_zombie()' for why.
120+
WB.forceNMethodSweep(); // Need third sweep to actually unload/free all "zombie" nmethods.
121+
System.gc();
122+
WB.clearMethodState(m);
123+
}
124+
125+
// Set '-XX' flags according to 'TestMode'
126+
private static void setFlags(TestMode testMode) {
127+
if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) {
128+
WB.setBooleanVMFlag("OmitStackTraceInFastThrow", true);
129+
} else {
130+
WB.setBooleanVMFlag("OmitStackTraceInFastThrow", false);
131+
}
132+
133+
System.out.println("==========================================================");
134+
System.out.println("testMode=" + testMode +
135+
" OmitStackTraceInFastThrow=" + WB.getBooleanVMFlag("OmitStackTraceInFastThrow"));
136+
System.out.println("==========================================================");
137+
}
138+
139+
private static void printCounters(TestMode testMode, ImplicitException impExcp, Method throwImplicitException_m, int invocations) {
140+
System.out.println("testMode=" + testMode + " exception=" + impExcp + " invocations=" + invocations + "\n" +
141+
"decompilecount=" + WB.getMethodDecompileCount(throwImplicitException_m) + " " +
142+
"trapCount=" + WB.getMethodTrapCount(throwImplicitException_m) + " " +
143+
"trapCount(" + impExcp.getReason() + ")=" +
144+
WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason()) + " " +
145+
"globalDeoptCount=" + WB.getDeoptCount() + " " +
146+
"globalDeoptCount(" + impExcp.getReason() + ")=" + WB.getDeoptCount(impExcp.getReason(), null));
147+
System.out.println("method compiled=" + WB.isMethodCompiled(throwImplicitException_m));
148+
}
149+
150+
// Checks after the test method has been JIT-compiled but before the compiled version has been invoked.
151+
private static void checkSimple(TestMode testMode, ImplicitException impExcp, Exception ex, Method throwImplicitException_m, int invocations) {
152+
153+
printCounters(testMode, impExcp, throwImplicitException_m, invocations);
154+
// At this point, throwImplicitException() has been compiled but the compiled version has not been invoked yet.
155+
Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4.");
156+
157+
int trapCount = WB.getMethodTrapCount(throwImplicitException_m);
158+
int trapCountSpecific = WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason());
159+
Asserts.assertEQ(trapCount, invocations, "Trap count must much invocation count.");
160+
Asserts.assertEQ(trapCountSpecific, invocations, "Trap count must much invocation count.");
161+
Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message.");
162+
}
163+
164+
// Checks after the JIT-compiled test method has been invoked 'invocations' times.
165+
private static void check(TestMode testMode, ImplicitException impExcp, Exception ex,
166+
Method throwImplicitException_m, int invocations, int totalInvocations) {
167+
168+
printCounters(testMode, impExcp, throwImplicitException_m, totalInvocations);
169+
// At this point, the compiled version of 'throwImplicitException()' has been invoked 'invocations' times.
170+
Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4.");
171+
int deoptCount = WB.getDeoptCount();
172+
int deoptCountReason = WB.getDeoptCount(impExcp.getReason(), null/*action*/);
173+
if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) {
174+
// No deoptimizations for '-XX:+OmitStackTraceInFastThrow'
175+
Asserts.assertEQ(oldDeoptCount, deoptCount, "Wrong number of deoptimizations.");
176+
Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()), deoptCountReason, "Wrong number of deoptimizations.");
177+
// '-XX:+OmitStackTraceInFastThrow' never has message because it is using a global singleton exception.
178+
Asserts.assertNull(ex.getMessage(), "Optimized exceptions have no message.");
179+
} else if (testMode == TestMode.STACKTRACES_IN_FASTTHROW) {
180+
// We always deoptimize for '-XX:-OmitStackTraceInFastThrow
181+
Asserts.assertEQ(oldDeoptCount + invocations, deoptCount, "Wrong number of deoptimizations.");
182+
Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()) + invocations, deoptCountReason, "Wrong number of deoptimizations.");
183+
Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message.");
184+
} else {
185+
Asserts.fail("Unknown test mode.");
186+
}
187+
oldDeoptCount = deoptCount;
188+
oldDeoptCountReason.put(impExcp.getReason(), deoptCountReason);
189+
}
190+
191+
public static void main(String[] args) throws Exception {
192+
193+
if (!WB.getBooleanVMFlag("ProfileTraps")) {
194+
// The fast-throw optimzation only works if we're running with -XX:+ProfileTraps
195+
return;
196+
}
197+
198+
// Initialize global deopt counts to zero.
199+
for (ImplicitException impExcp : ImplicitException.values()) {
200+
oldDeoptCountReason.put(impExcp.getReason(), 0);
201+
}
202+
// Get a handle of the test method for usage with the WhiteBox API.
203+
Method throwImplicitException_m = OptimizeImplicitExceptions.class
204+
.getDeclaredMethod("throwImplicitException", new Class[] { ImplicitException.class, Object[].class});
205+
206+
for (TestMode testMode : TestMode.values()) {
207+
setFlags(testMode);
208+
for (ImplicitException impExcp : ImplicitException.values()) {
209+
int invocations = 0;
210+
Exception lastException = null;
211+
212+
// Warmup and compile, but don't invoke compiled code.
213+
while(!WB.isMethodCompiled(throwImplicitException_m)) {
214+
invocations++;
215+
try {
216+
throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a);
217+
} catch (Exception catchedExcp) {
218+
lastException = catchedExcp;
219+
continue;
220+
}
221+
throw new Exception("Should not happen");
222+
}
223+
224+
checkSimple(testMode, impExcp, lastException, throwImplicitException_m, invocations);
225+
226+
// Invoke compiled code 'PerBytecodeTrapLimit' times.
227+
for (int i = 0; i < PerBytecodeTrapLimit; i++) {
228+
invocations++;
229+
try {
230+
throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a);
231+
} catch (Exception catchedExcp) {
232+
lastException = catchedExcp;
233+
continue;
234+
}
235+
throw new Exception("Should not happen");
236+
}
237+
238+
check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations);
239+
240+
// Invoke compiled code 'Tier0InvokeNotifyFreq' times.
241+
// If the method was de-compiled before, this will re-compile it again.
242+
for (int i = 0; i < Tier0InvokeNotifyFreq; i++) {
243+
invocations++;
244+
try {
245+
throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a);
246+
} catch (Exception catchedExcp) {
247+
lastException = catchedExcp;
248+
continue;
249+
}
250+
throw new Exception("Should not happen");
251+
}
252+
253+
check(testMode, impExcp, lastException, throwImplicitException_m, Tier0InvokeNotifyFreq, invocations);
254+
255+
// Invoke compiled code 'PerBytecodeTrapLimit' times.
256+
for (int i = 0; i < PerBytecodeTrapLimit; i++) {
257+
invocations++;
258+
try {
259+
throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a);
260+
} catch (Exception catchedExcp) {
261+
lastException = catchedExcp;
262+
continue;
263+
}
264+
throw new Exception("Should not happen");
265+
}
266+
267+
check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations);
268+
269+
System.out.println("------------------------------------------------------------------");
270+
271+
unloadAndClean(throwImplicitException_m);
272+
}
273+
}
274+
}
275+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8275908
27+
* @summary Quick test for the new WhiteBox methods of JDK-8275908
28+
*
29+
* @requires vm.compiler2.enabled & vm.compMode != "Xcomp"
30+
*
31+
* @library /test/lib
32+
* @build jdk.test.whitebox.WhiteBox
33+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
34+
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
35+
* -XX:+UseSerialGC -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation
36+
* -XX:CompileCommand=compileonly,compiler.uncommontrap.Decompile::uncommonTrap
37+
* -XX:CompileCommand=inline,compiler.uncommontrap.Decompile*::foo
38+
* compiler.uncommontrap.Decompile
39+
*/
40+
41+
package compiler.uncommontrap;
42+
43+
import java.lang.reflect.Method;
44+
45+
import jdk.test.lib.Asserts;
46+
import jdk.test.whitebox.WhiteBox;
47+
48+
public class Decompile {
49+
50+
private static final WhiteBox WB = WhiteBox.getWhiteBox();
51+
// The number of deoptimizations after which a method will be made not-entrant
52+
private static final int PerBytecodeTrapLimit = WB.getIntxVMFlag("PerBytecodeTrapLimit").intValue();
53+
// The number of interpreter invocations after which a decompiled method will be re-compiled.
54+
private static final int Tier0InvokeNotifyFreq = (int)Math.pow(2, WB.getIntxVMFlag("Tier0InvokeNotifyFreqLog"));
55+
// VM builds without JVMCI like x86_32 call the bimorphic inlining trap just 'bimorphic'
56+
// while all the other builds with JVMCI call it 'bimorphic_or_optimized_type_check'.
57+
private static final boolean isJVMCISupported = WhiteBox.getWhiteBox().isJVMCISupportedByGC();
58+
private static final String bimorphicTrapName = isJVMCISupported ? "bimorphic_or_optimized_type_check" : "bimorphic";
59+
60+
static class Base {
61+
void foo() {}
62+
}
63+
static class X extends Base {
64+
void foo() {}
65+
}
66+
static class Y extends Base {
67+
void foo() {}
68+
}
69+
70+
static void uncommonTrap(Base t) {
71+
t.foo();
72+
}
73+
74+
private static void printCounters(Method uncommonTrap_m, int invocations) {
75+
System.out.println("-----------------------------------------------------------------");
76+
System.out.println("invocations=" + invocations + " " +
77+
"method compiled=" + WB.isMethodCompiled(uncommonTrap_m) + " " +
78+
"decompileCount=" + WB.getMethodDecompileCount(uncommonTrap_m) + "\n" +
79+
"trapCount=" + WB.getMethodTrapCount(uncommonTrap_m) + " " +
80+
"trapCount(class_check)=" + WB.getMethodTrapCount(uncommonTrap_m, "class_check") + " " +
81+
"trapCount(" + bimorphicTrapName + ")=" +
82+
WB.getMethodTrapCount(uncommonTrap_m, bimorphicTrapName) + "\n" +
83+
"globalDeoptCount=" + WB.getDeoptCount() + " " +
84+
"globalDeoptCount(class_check)=" + WB.getDeoptCount("class_check", null) + " " +
85+
"globalDeoptCount(" + bimorphicTrapName + ")=" +
86+
WB.getDeoptCount(bimorphicTrapName, null));
87+
System.out.println("-----------------------------------------------------------------");
88+
}
89+
90+
private static void check(Method uncommonTrap_m, int invocations, boolean isCompiled, int decompileCount,
91+
int trapCount, int trapCountClassCheck, int trapCountBimorphic,
92+
int deoptCount, int deoptCountClassCheck, int deoptCountBimorphic) {
93+
94+
printCounters(uncommonTrap_m, invocations);
95+
96+
Asserts.assertEQ(isCompiled, WB.isMethodCompiled(uncommonTrap_m),
97+
"Wrong compilation status.");
98+
Asserts.assertEQ(decompileCount, WB.getMethodDecompileCount(uncommonTrap_m),
99+
"Wrong number of decompilations.");
100+
Asserts.assertEQ(trapCount, WB.getMethodTrapCount(uncommonTrap_m),
101+
"Wrong number of traps.");
102+
Asserts.assertEQ(trapCountClassCheck, WB.getMethodTrapCount(uncommonTrap_m, "class_check"),
103+
"Wrong number of traps.");
104+
Asserts.assertEQ(trapCountBimorphic, WB.getMethodTrapCount(uncommonTrap_m, bimorphicTrapName),
105+
"Wrong number of traps.");
106+
Asserts.assertEQ(deoptCount, WB.getDeoptCount(),
107+
"Wrong number of deoptimizations.");
108+
Asserts.assertEQ(deoptCountClassCheck, WB.getDeoptCount("class_check", null),
109+
"Wrong number of class_check deoptimizations.");
110+
Asserts.assertEQ(deoptCountBimorphic, WB.getDeoptCount(bimorphicTrapName, null),
111+
"Wrong number of " + bimorphicTrapName + "deoptimizations.");
112+
}
113+
public static void main(String[] args) throws Exception {
114+
115+
// Get a handle of the test method for usage with the WhiteBox API.
116+
Method uncommonTrap_m = Decompile.class
117+
.getDeclaredMethod("uncommonTrap", new Class[] { Base.class });
118+
119+
int invocations = 0;
120+
Base b = new Base();
121+
// This is a little tricky :) We have to define 'x' already here otherwise
122+
// the class 'X' won't be loaded and 'uncommonTrap()' will be compiled without
123+
// a class check but a CHA dependency that class 'B' has no subtypes.
124+
X x = new X();
125+
Y y = new Y();
126+
127+
// Warmup and compile with an object of type 'Base' as receiver, but don't invoke compiled code.
128+
while(!WB.isMethodCompiled(uncommonTrap_m)) {
129+
invocations++;
130+
uncommonTrap(b);
131+
}
132+
check(uncommonTrap_m, invocations, true /* is_compiled */, 0 /* decompileCount */,
133+
0 /* trapCount */, 0 /* trapCountClassCheck */, 0 /* trapCountBimorphic */,
134+
0 /* deoptCount */, 0 /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */);
135+
136+
// Invoke compiled code 'PerBytecodeTrapLimit' times with an receiver object of type 'X'.
137+
// This should deoptimize 'PerBytecodeTrapLimit' times and finally decompile the method.
138+
for (int i = 0; i < PerBytecodeTrapLimit; i++) {
139+
invocations++;
140+
uncommonTrap(x);
141+
}
142+
check(uncommonTrap_m, invocations, false /* is_compiled */, 1 /* decompileCount */,
143+
PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, 0 /* trapCountBimorphic */,
144+
PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */);
145+
146+
// Invoke the method 'Tier0InvokeNotifyFreq' more times with an receiver object of type 'X'.
147+
// This should re-compile the method again with bimorphic inlining for receiver types 'Base' and 'X'.
148+
for (int i = 0; i < Tier0InvokeNotifyFreq; i++) {
149+
invocations++;
150+
uncommonTrap(x);
151+
}
152+
check(uncommonTrap_m, invocations, true /* is_compiled */, 1 /* decompileCount */,
153+
PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, 0 /* trapCountBimorphic */,
154+
PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */);
155+
156+
// Invoke compiled code 'PerBytecodeTrapLimit' times with an receiver object of type 'Y'.
157+
// This should deoptimize 'PerBytecodeTrapLimit' times and finally decompile the method.
158+
for (int i = 0; i < PerBytecodeTrapLimit; i++) {
159+
invocations++;
160+
uncommonTrap(y);
161+
}
162+
check(uncommonTrap_m, invocations, false /* is_compiled */, 2 /* decompileCount */,
163+
2*PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, PerBytecodeTrapLimit /* trapCountBimorphic */,
164+
2*PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, PerBytecodeTrapLimit /* deoptCountBimorphic */);
165+
}
166+
}

‎test/lib/jdk/test/whitebox/WhiteBox.java

+30
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,36 @@ public int getMethodCompilationLevel(Executable method, boolean isOs
317317
Objects.requireNonNull(method);
318318
return getMethodCompilationLevel0(method, isOsr);
319319
}
320+
public int getMethodDecompileCount(Executable method) {
321+
Objects.requireNonNull(method);
322+
return getMethodDecompileCount0(method);
323+
}
324+
private native int getMethodDecompileCount0(Executable method);
325+
// Get the total trap count of a method. If the trap count for a specific reason
326+
// did overflow, this includes the overflow trap count of the method.
327+
public int getMethodTrapCount(Executable method) {
328+
Objects.requireNonNull(method);
329+
return getMethodTrapCount0(method, null);
330+
}
331+
// Get the trap count of a method for a specific reason. If the trap count for
332+
// that reason did overflow, this includes the overflow trap count of the method.
333+
public int getMethodTrapCount(Executable method, String reason) {
334+
Objects.requireNonNull(method);
335+
return getMethodTrapCount0(method, reason);
336+
}
337+
private native int getMethodTrapCount0(Executable method, String reason);
338+
// Get the total deopt count.
339+
public int getDeoptCount() {
340+
return getDeoptCount0(null, null);
341+
}
342+
// Get the deopt count for a specific reason and a specific action. If either
343+
// one of 'reason' or 'action' is null, the method returns the sum of all
344+
// deoptimizations with the specific 'action' or 'reason' respectively.
345+
// If both arguments are null, the method returns the total deopt count.
346+
public int getDeoptCount(String reason, String action) {
347+
return getDeoptCount0(reason, action);
348+
}
349+
private native int getDeoptCount0(String reason, String action);
320350
private native boolean testSetDontInlineMethod0(Executable method, boolean value);
321351
public boolean testSetDontInlineMethod(Executable method, boolean value) {
322352
Objects.requireNonNull(method);

‎test/lib/sun/hotspot/WhiteBox.java

+30
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,36 @@ public int getMethodCompilationLevel(Executable method, boolean isOs
318318
Objects.requireNonNull(method);
319319
return getMethodCompilationLevel0(method, isOsr);
320320
}
321+
public int getMethodDecompileCount(Executable method) {
322+
Objects.requireNonNull(method);
323+
return getMethodDecompileCount0(method);
324+
}
325+
private native int getMethodDecompileCount0(Executable method);
326+
// Get the total trap count of a method. If the trap count for a specific reason
327+
// did overflow, this includes the overflow trap count of the method.
328+
public int getMethodTrapCount(Executable method) {
329+
Objects.requireNonNull(method);
330+
return getMethodTrapCount0(method, null);
331+
}
332+
// Get the trap count of a method for a specific reason. If the trap count for
333+
// that reason did overflow, this includes the overflow trap count of the method.
334+
public int getMethodTrapCount(Executable method, String reason) {
335+
Objects.requireNonNull(method);
336+
return getMethodTrapCount0(method, reason);
337+
}
338+
private native int getMethodTrapCount0(Executable method, String reason);
339+
// Get the total deopt count.
340+
public int getDeoptCount() {
341+
return getDeoptCount0(null, null);
342+
}
343+
// Get the deopt count for a specific reason and a specific action. If either
344+
// one of 'reason' or 'action' is null, the method returns the sum of all
345+
// deoptimizations with the specific 'action' or 'reason' respectively.
346+
// If both arguments are null, the method returns the total deopt count.
347+
public int getDeoptCount(String reason, String action) {
348+
return getDeoptCount0(reason, action);
349+
}
350+
private native int getDeoptCount0(String reason, String action);
321351
private native boolean testSetDontInlineMethod0(Executable method, boolean value);
322352
public boolean testSetDontInlineMethod(Executable method, boolean value) {
323353
Objects.requireNonNull(method);

0 commit comments

Comments
 (0)
Please sign in to comment.