Skip to content

Commit 38802ad

Browse files
committedNov 23, 2021
8254108: ciReplay: Support incremental inlining
Reviewed-by: dlong, thartmann
1 parent 64bdc84 commit 38802ad

File tree

9 files changed

+495
-178
lines changed

9 files changed

+495
-178
lines changed
 

‎src/hotspot/share/ci/ciReplay.cpp

+28-8
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ typedef struct _ciInlineRecord {
103103

104104
int _inline_depth;
105105
int _inline_bci;
106+
bool _inline_late;
106107
} ciInlineRecord;
107108

108109
class CompileReplay;
@@ -720,7 +721,7 @@ class CompileReplay : public StackObj {
720721
return NULL;
721722
}
722723

723-
// compile <klass> <name> <signature> <entry_bci> <comp_level> inline <count> (<depth> <bci> <klass> <name> <signature>)*
724+
// compile <klass> <name> <signature> <entry_bci> <comp_level> inline <count> (<depth> <bci> <inline_late> <klass> <name> <signature>)*
724725
void process_compile(TRAPS) {
725726
Method* method = parse_method(CHECK);
726727
if (had_error()) return;
@@ -762,11 +763,19 @@ class CompileReplay : public StackObj {
762763
if (had_error()) {
763764
break;
764765
}
766+
int inline_late = 0;
767+
if (_version >= 2) {
768+
inline_late = parse_int("inline_late");
769+
if (had_error()) {
770+
break;
771+
}
772+
}
773+
765774
Method* inl_method = parse_method(CHECK);
766775
if (had_error()) {
767776
break;
768777
}
769-
new_ciInlineRecord(inl_method, bci, depth);
778+
new_ciInlineRecord(inl_method, bci, depth, inline_late);
770779
}
771780
}
772781
if (_imethod != NULL) {
@@ -1227,13 +1236,14 @@ class CompileReplay : public StackObj {
12271236
}
12281237

12291238
// Create and initialize a record for a ciInlineRecord
1230-
ciInlineRecord* new_ciInlineRecord(Method* method, int bci, int depth) {
1239+
ciInlineRecord* new_ciInlineRecord(Method* method, int bci, int depth, int inline_late) {
12311240
ciInlineRecord* rec = NEW_RESOURCE_OBJ(ciInlineRecord);
12321241
rec->_klass_name = method->method_holder()->name()->as_utf8();
12331242
rec->_method_name = method->name()->as_utf8();
12341243
rec->_signature = method->signature()->as_utf8();
12351244
rec->_inline_bci = bci;
12361245
rec->_inline_depth = depth;
1246+
rec->_inline_late = inline_late;
12371247
_ci_inline_records->append(rec);
12381248
return rec;
12391249
}
@@ -1470,23 +1480,33 @@ bool ciReplay::should_not_inline(ciMethod* method) {
14701480
return replay_state->find_ciMethodRecord(method->get_Method()) == NULL;
14711481
}
14721482

1473-
bool ciReplay::should_inline(void* data, ciMethod* method, int bci, int inline_depth) {
1483+
bool ciReplay::should_inline(void* data, ciMethod* method, int bci, int inline_depth, bool& should_delay) {
14741484
if (data != NULL) {
1475-
GrowableArray<ciInlineRecord*>* records = (GrowableArray<ciInlineRecord*>*)data;
1485+
GrowableArray<ciInlineRecord*>* records = (GrowableArray<ciInlineRecord*>*)data;
14761486
VM_ENTRY_MARK;
14771487
// Inline record are ordered by bci and depth.
1478-
return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) != NULL;
1488+
ciInlineRecord* record = CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth);
1489+
if (record == NULL) {
1490+
return false;
1491+
}
1492+
should_delay = record->_inline_late;
1493+
return true;
14791494
} else if (replay_state != NULL) {
14801495
VM_ENTRY_MARK;
14811496
// Inline record are ordered by bci and depth.
1482-
return replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth) != NULL;
1497+
ciInlineRecord* record = replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth);
1498+
if (record == NULL) {
1499+
return false;
1500+
}
1501+
should_delay = record->_inline_late;
1502+
return true;
14831503
}
14841504
return false;
14851505
}
14861506

14871507
bool ciReplay::should_not_inline(void* data, ciMethod* method, int bci, int inline_depth) {
14881508
if (data != NULL) {
1489-
GrowableArray<ciInlineRecord*>* records = (GrowableArray<ciInlineRecord*>*)data;
1509+
GrowableArray<ciInlineRecord*>* records = (GrowableArray<ciInlineRecord*>*)data;
14901510
VM_ENTRY_MARK;
14911511
// Inline record are ordered by bci and depth.
14921512
return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) == NULL;

‎src/hotspot/share/ci/ciReplay.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class ciReplay {
121121
static bool is_loaded(Method* method);
122122

123123
static bool should_not_inline(ciMethod* method);
124-
static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth);
124+
static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth, bool& should_delay);
125125
static bool should_not_inline(void* data, ciMethod* method, int bci, int inline_depth);
126126
#endif
127127

@@ -135,6 +135,7 @@ class ciReplay {
135135
// 0: legacy (no version number)
136136
// 1: first instanceKlass sets protection domain (8275868)
137137
// replace current_mileage with invocation_count (8276095)
138-
#define REPLAY_VERSION 1 // current version, bump up for incompatible changes
138+
// 2: incremental inlining support (8254108)
139+
#define REPLAY_VERSION 2 // current version, bump up for incompatible changes
139140

140141
#endif // SHARE_CI_CIREPLAY_HPP

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

+29-14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ InlineTree::InlineTree(Compile* c,
4646
C(c),
4747
_caller_jvms(caller_jvms),
4848
_method(callee),
49+
_late_inline(false),
4950
_caller_tree((InlineTree*) caller_tree),
5051
_count_inline_bcs(method()->code_size_for_inlining()),
5152
_max_inline_level(max_inline_level),
@@ -113,7 +114,7 @@ static bool is_unboxing_method(ciMethod* callee_method, Compile* C) {
113114

114115
// positive filter: should callee be inlined?
115116
bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
116-
int caller_bci, ciCallProfile& profile) {
117+
int caller_bci, NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile) {
117118
// Allows targeted inlining
118119
if (C->directive()->should_inline(callee_method)) {
119120
set_msg("force inline by CompileCommand");
@@ -128,9 +129,13 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
128129
}
129130

130131
#ifndef PRODUCT
131-
int inline_depth = inline_level()+1;
132-
if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) {
133-
set_msg("force inline by ciReplay");
132+
int inline_depth = inline_level() + 1;
133+
if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth, should_delay)) {
134+
if (should_delay) {
135+
set_msg("force (incremental) inline by ciReplay");
136+
} else {
137+
set_msg("force inline by ciReplay");
138+
}
134139
_forced_inline = true;
135140
return true;
136141
}
@@ -194,7 +199,7 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
194199

195200
// negative filter: should callee NOT be inlined?
196201
bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_method,
197-
int caller_bci, ciCallProfile& profile) {
202+
int caller_bci, NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile) {
198203
const char* fail_msg = NULL;
199204

200205
// First check all inlining restrictions which are required for correctness
@@ -232,9 +237,13 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met
232237
}
233238

234239
#ifndef PRODUCT
235-
int inline_depth = inline_level()+1;
236-
if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) {
237-
set_msg("force inline by ciReplay");
240+
int inline_depth = inline_level() + 1;
241+
if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth, should_delay)) {
242+
if (should_delay) {
243+
set_msg("force (incremental) inline by ciReplay");
244+
} else {
245+
set_msg("force inline by ciReplay");
246+
}
238247
return false;
239248
}
240249

@@ -369,10 +378,13 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method,
369378
}
370379

371380
_forced_inline = false; // Reset
372-
if (!should_inline(callee_method, caller_method, caller_bci, profile)) {
381+
382+
// 'should_delay' can be overridden during replay compilation
383+
if (!should_inline(callee_method, caller_method, caller_bci, NOT_PRODUCT_ARG(should_delay) profile)) {
373384
return false;
374385
}
375-
if (should_not_inline(callee_method, caller_method, caller_bci, profile)) {
386+
// 'should_delay' can be overridden during replay compilation
387+
if (should_not_inline(callee_method, caller_method, caller_bci, NOT_PRODUCT_ARG(should_delay) profile)) {
376388
return false;
377389
}
378390

@@ -557,9 +569,8 @@ void InlineTree::print_inlining(ciMethod* callee_method, int caller_bci,
557569
//------------------------------ok_to_inline-----------------------------------
558570
bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallProfile& profile,
559571
bool& should_delay) {
560-
assert(callee_method != NULL, "caller checks for optimized virtual!");
561-
assert(!should_delay, "should be initialized to false");
562572
#ifdef ASSERT
573+
assert(callee_method != NULL, "caller checks for optimized virtual!");
563574
// Make sure the incoming jvms has the same information content as me.
564575
// This means that we can eventually make this whole class AllStatic.
565576
if (jvms->caller() == NULL) {
@@ -595,7 +606,11 @@ bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallPro
595606
set_msg("inline (hot)");
596607
}
597608
print_inlining(callee_method, caller_bci, caller_method, true /* success */);
598-
build_inline_tree_for_callee(callee_method, jvms, caller_bci);
609+
InlineTree* callee_tree = build_inline_tree_for_callee(callee_method, jvms, caller_bci);
610+
if (should_delay) {
611+
// Record late inlining decision in order to dump it for compiler replay
612+
callee_tree->set_late_inline();
613+
}
599614
return true;
600615
} else {
601616
// Do not inline
@@ -700,7 +715,7 @@ int InlineTree::count() const {
700715
}
701716

702717
void InlineTree::dump_replay_data(outputStream* out) {
703-
out->print(" %d %d ", inline_level(), caller_bci());
718+
out->print(" %d %d %d ", inline_level(), caller_bci(), _late_inline);
704719
method()->dump_name_as_ascii(out);
705720
for (int i = 0 ; i < _subtrees.length(); i++) {
706721
_subtrees.at(i)->dump_replay_data(out);

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
164164
// Try inlining a bytecoded method:
165165
if (!call_does_dispatch) {
166166
InlineTree* ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method());
167-
bool should_delay = false;
167+
bool should_delay = AlwaysIncrementalInline;
168168
if (ilt->ok_to_inline(callee, jvms, profile, should_delay)) {
169169
CallGenerator* cg = CallGenerator::for_inline(callee, expected_uses);
170170
// For optimized virtual calls assert at runtime that receiver object
@@ -189,7 +189,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
189189
return CallGenerator::for_boxing_late_inline(callee, cg);
190190
} else if (should_delay_vector_reboxing_inlining(callee, jvms)) {
191191
return CallGenerator::for_vector_reboxing_late_inline(callee, cg);
192-
} else if ((should_delay || AlwaysIncrementalInline)) {
192+
} else if (should_delay) {
193193
return CallGenerator::for_late_inline(callee, cg);
194194
} else {
195195
return cg;

‎src/hotspot/share/opto/parse.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class InlineTree : public ResourceObj {
4646
Compile* C; // cache
4747
JVMState* _caller_jvms; // state of caller
4848
ciMethod* _method; // method being called by the caller_jvms
49+
bool _late_inline; // method is inlined incrementally
4950
InlineTree* _caller_tree;
5051
uint _count_inline_bcs; // Accumulated count of inlined bytecodes
5152
const int _max_inline_level; // the maximum inline level for this sub-tree (may be adjusted)
@@ -75,10 +76,12 @@ class InlineTree : public ResourceObj {
7576
bool should_inline(ciMethod* callee_method,
7677
ciMethod* caller_method,
7778
int caller_bci,
79+
NOT_PRODUCT_ARG(bool& should_delay)
7880
ciCallProfile& profile);
7981
bool should_not_inline(ciMethod* callee_method,
8082
ciMethod* caller_method,
8183
int caller_bci,
84+
NOT_PRODUCT_ARG(bool& should_delay)
8285
ciCallProfile& profile);
8386
bool is_not_reached(ciMethod* callee_method,
8487
ciMethod* caller_method,
@@ -112,6 +115,10 @@ class InlineTree : public ResourceObj {
112115
// The call_method is an optimized virtual method candidate otherwise.
113116
bool ok_to_inline(ciMethod *call_method, JVMState* caller_jvms, ciCallProfile& profile, bool& should_delay);
114117

118+
void set_late_inline() {
119+
_late_inline = true;
120+
}
121+
115122
// Information about inlined method
116123
JVMState* caller_jvms() const { return _caller_jvms; }
117124
ciMethod *method() const { return _method; }

‎test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java

+46-10
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,17 @@
2424
package compiler.ciReplay;
2525

2626
import compiler.whitebox.CompilerWhiteBoxTest;
27-
import java.io.IOException;
28-
import java.io.File;
27+
import jdk.test.lib.Asserts;
28+
import jdk.test.lib.Platform;
29+
import jdk.test.lib.Utils;
30+
import jdk.test.lib.process.OutputAnalyzer;
31+
import jdk.test.lib.process.ProcessTools;
32+
import jdk.test.lib.util.CoreUtils;
33+
2934
import java.io.BufferedReader;
35+
import java.io.File;
3036
import java.io.FileReader;
37+
import java.io.IOException;
3138
import java.nio.file.Files;
3239
import java.nio.file.Path;
3340
import java.nio.file.Paths;
@@ -36,14 +43,6 @@
3643
import java.util.Arrays;
3744
import java.util.List;
3845
import java.util.Optional;
39-
import java.util.regex.Pattern;
40-
import java.util.regex.Matcher;
41-
import jdk.test.lib.Platform;
42-
import jdk.test.lib.process.ProcessTools;
43-
import jdk.test.lib.process.OutputAnalyzer;
44-
import jdk.test.lib.Asserts;
45-
import jdk.test.lib.Utils;
46-
import jdk.test.lib.util.CoreUtils;
4746

4847
public abstract class CiReplayBase {
4948
public static final String REPLAY_FILE_NAME = "test_replay.txt";
@@ -296,4 +295,41 @@ private String[] getTestJvmCommandlineWithPrefix(String prefix, String... args)
296295
throw new Error("Can't create process builder: " + t, t);
297296
}
298297
}
298+
299+
protected void removeVersionFromReplayFile() {
300+
setNewVersionLineInReplayFile(null);
301+
}
302+
303+
protected void setNewVersionInReplayFile(int newVersionNumber) {
304+
setNewVersionLineInReplayFile("version " + newVersionNumber);
305+
}
306+
307+
private void setNewVersionLineInReplayFile(String firstLineString) {
308+
List<String> newLines = new ArrayList<>();
309+
Path replayFilePath = Paths.get(getReplayFileName());
310+
try (var br = Files.newBufferedReader(replayFilePath)) {
311+
String line;
312+
boolean firstLine = true;
313+
while ((line = br.readLine()) != null) {
314+
if (firstLine) {
315+
firstLine = false;
316+
Asserts.assertTrue(line.startsWith("version"), "version number must exist in a proper replay file");
317+
if (firstLineString != null) {
318+
newLines.add(firstLineString);
319+
}
320+
// Else: Remove first line by skipping it.
321+
} else {
322+
newLines.add(line);
323+
}
324+
}
325+
Asserts.assertFalse(firstLine, replayFilePath + " should not be empty");
326+
} catch (IOException e) {
327+
throw new Error("Failed to read replay data: " + e, e);
328+
}
329+
try {
330+
Files.write(replayFilePath, newLines, StandardOpenOption.TRUNCATE_EXISTING);
331+
} catch (IOException e) {
332+
throw new Error("Failed to write replay data: " + e, e);
333+
}
334+
}
299335
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/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+
package compiler.ciReplay;
25+
26+
import jdk.test.lib.Asserts;
27+
28+
import java.io.IOException;
29+
import java.nio.file.Files;
30+
import java.nio.file.Paths;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.regex.Matcher;
34+
import java.util.regex.Pattern;
35+
36+
public abstract class InliningBase extends DumpReplayBase {
37+
public static final String LOG_FILE_NORMAL = "hotspot_normal.log";
38+
public static final String LOG_FILE_REPLAY = "hotspot_replay.log";
39+
protected final String[] commandLineReplay;
40+
protected final List<String> commandLineNormal;
41+
protected final Class<?> testClass;
42+
43+
protected InliningBase(Class<?> testClass) {
44+
this.testClass = testClass;
45+
commandLineNormal = new ArrayList<>(List.of("-XX:LogFile=" + LOG_FILE_NORMAL + "", "-XX:+LogCompilation", "-XX:-TieredCompilation",
46+
"-XX:CompileCommand=exclude," + testClass.getName() + "::main",
47+
"-XX:CompileCommand=option," + testClass.getName() + "::test,bool,PrintInlining,true"));
48+
commandLineReplay = new String[]
49+
{"-XX:LogFile=" + LOG_FILE_REPLAY, "-XX:+LogCompilation",
50+
"-XX:CompileCommand=option," + testClass.getName() + "::test,bool,PrintInlining,true"};
51+
}
52+
53+
protected void runTest() {
54+
runTest(commandLineNormal.toArray(new String[0]));
55+
}
56+
57+
@Override
58+
public String getTestClass() {
59+
return testClass.getName();
60+
}
61+
62+
@Override
63+
public void cleanup() {
64+
super.cleanup();
65+
remove(LOG_FILE_NORMAL);
66+
remove(LOG_FILE_REPLAY);
67+
}
68+
69+
static class InlineEntry {
70+
String klass;
71+
String method;
72+
String reason;
73+
74+
public InlineEntry(String klass, String method, String reason) {
75+
this.klass = klass;
76+
this.method = method;
77+
this.reason = reason;
78+
}
79+
80+
public boolean isNormalInline() {
81+
return reason.equals("inline (hot)");
82+
}
83+
84+
public boolean isForcedByReplay() {
85+
return reason.equals("force inline by ciReplay");
86+
}
87+
88+
public boolean isDisallowedByReplay() {
89+
return reason.equals("disallowed by ciReplay");
90+
}
91+
92+
public boolean isUnloadedSignatureClasses() {
93+
return reason.equals("unloaded signature classes");
94+
}
95+
96+
public boolean isForcedIncrementalInlineByReplay() {
97+
return reason.equals("force (incremental) inline by ciReplay");
98+
}
99+
100+
public boolean isForcedInline() {
101+
return reason.equals("force inline by annotation");
102+
}
103+
104+
public boolean isTooDeep() {
105+
return reason.equals("inlining too deep");
106+
}
107+
108+
@Override
109+
public boolean equals(Object other) {
110+
if (other == this) {
111+
return true;
112+
}
113+
114+
if (!(other instanceof InlineEntry)) {
115+
return false;
116+
}
117+
118+
InlineEntry e = (InlineEntry)other;
119+
return klass.equals(e.klass) && method.equals(e.method);
120+
}
121+
122+
public boolean compare(String klass, String method, boolean kind) {
123+
return this.klass.equals(klass) && this.method.equals(method) && kind;
124+
}
125+
}
126+
127+
protected static List<InlineEntry> parseLogFile(String logFile, String rootMethod, String nmethodMatch, int inlineeCount) {
128+
String nmethodStart = "<nmethod";
129+
List<InlineEntry> inlinees = new ArrayList<>();
130+
int foundLines = 0;
131+
try (var br = Files.newBufferedReader(Paths.get(logFile))) {
132+
String line;
133+
boolean nmethodLine = false;
134+
boolean inlinineLine = false;
135+
while ((line = br.readLine()) != null) {
136+
if (nmethodLine) {
137+
// Ignore other entries which could be in between nmethod entry and inlining statements
138+
if (line.startsWith(" ")) {
139+
inlinineLine = true;
140+
Pattern p = Pattern.compile("(\\S+)::(\\S+).*bytes\\)\s+(.*)");
141+
Matcher matcher = p.matcher(line);
142+
Asserts.assertTrue(matcher.find(), "must find inlinee method");
143+
inlinees.add(new InlineEntry(matcher.group(1), matcher.group(2), matcher.group(3).trim()));
144+
foundLines++;
145+
} else if (inlinineLine) {
146+
Asserts.assertEQ(foundLines, inlineeCount, "did not find all inlinees");
147+
return inlinees;
148+
}
149+
} else {
150+
nmethodLine = line.startsWith(nmethodStart) && line.contains(nmethodMatch);
151+
if (nmethodLine) {
152+
Asserts.assertTrue(line.contains(rootMethod), "should only dump inline information for " + rootMethod);
153+
}
154+
}
155+
}
156+
} catch (IOException e) {
157+
throw new Error("Failed to read " + logFile + " data: " + e, e);
158+
}
159+
Asserts.fail("Should have found inlinees");
160+
return inlinees;
161+
}
162+
163+
protected void verifyLists(List<InlineEntry> inlineesNormal, List<InlineEntry> inlineesReplay, int expectedSize) {
164+
if (!inlineesNormal.equals(inlineesReplay)) {
165+
System.err.println("Normal entries:");
166+
inlineesNormal.forEach(System.err::println);
167+
System.err.println("Replay entries:");
168+
inlineesReplay.forEach(System.err::println);
169+
Asserts.fail("different inlining decision in normal run vs. replay run");
170+
}
171+
Asserts.assertEQ(expectedSize, inlineesNormal.size(), "unexpected number of inlinees found");
172+
}
173+
}
174+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/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 8254108
27+
* @library / /test/lib
28+
* @summary Testing of ciReplay with incremental inlining.
29+
* @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.compMode != "Xcomp" & vm.debug == true & vm.compiler2.enabled
30+
* @modules java.base/jdk.internal.misc
31+
* @build jdk.test.whitebox.WhiteBox
32+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
33+
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
34+
* compiler.ciReplay.TestIncrementalInlining
35+
*/
36+
37+
package compiler.ciReplay;
38+
39+
import jdk.test.lib.Asserts;
40+
import jdk.test.lib.Utils;
41+
import jdk.test.whitebox.WhiteBox;
42+
43+
import java.io.IOException;
44+
import java.nio.file.Files;
45+
import java.nio.file.Path;
46+
import java.nio.file.Paths;
47+
import java.nio.file.StandardOpenOption;
48+
import java.util.List;
49+
import java.util.regex.Matcher;
50+
import java.util.regex.Pattern;
51+
52+
public class TestIncrementalInlining extends InliningBase {
53+
54+
private List<InlineEntry> inlineesNormal;
55+
private List<InlineEntry> inlineesReplay;
56+
public static void main(String[] args) {
57+
new TestIncrementalInlining();
58+
}
59+
60+
TestIncrementalInlining() {
61+
super(IncrementalInliningTest.class);
62+
// Enable Whitebox access for test VM.
63+
commandLineNormal.add("-Dtest.jdk=" + Utils.TEST_JDK);
64+
commandLineNormal.add("-cp");
65+
commandLineNormal.add(Utils.TEST_CLASS_PATH);
66+
commandLineNormal.add("-Xbootclasspath/a:.");
67+
commandLineNormal.add("-XX:+UnlockDiagnosticVMOptions");
68+
commandLineNormal.add("-XX:+WhiteBoxAPI");
69+
commandLineNormal.add("-XX:MaxInlineLevel=2");
70+
commandLineNormal.add("-XX:-AlwaysIncrementalInline");
71+
runTest();
72+
}
73+
74+
@Override
75+
public void testAction() {
76+
positiveTest(commandLineReplay);
77+
inlineesNormal = parseLogFile(LOG_FILE_NORMAL, getTestClass() + " " + "test", "compile_id='" + getCompileIdFromFile(getReplayFileName()), 5);
78+
verify(true);
79+
80+
// Incremental inlining is supported in version 2+
81+
// Test replay file version 1.
82+
removeIncrementalInlineInfo();
83+
setNewVersionInReplayFile(1);
84+
positiveTest(commandLineReplay);
85+
verify(false);
86+
87+
// Test replay file without version.
88+
removeVersionFromReplayFile();
89+
positiveTest(commandLineReplay);
90+
verify(false);
91+
}
92+
93+
private void verify(boolean isNewFormat) {
94+
inlineesReplay = parseLogFile(LOG_FILE_REPLAY, getTestClass() + " " + "test", "test ()V", 5);
95+
verifyLists(inlineesNormal, inlineesReplay, 5);
96+
checkInlining(isNewFormat);
97+
}
98+
99+
// Check if inlining is done correctly in ciReplay.
100+
private void checkInlining(boolean isNewFormat) {
101+
String klass = getTestClass();
102+
Asserts.assertTrue(inlineesNormal.get(0).compare(klass, "level0", inlineesNormal.get(0).isForcedInline()));
103+
Asserts.assertTrue(inlineesReplay.get(0).compare(klass, "level0", inlineesReplay.get(0).isForcedByReplay()));
104+
Asserts.assertTrue(inlineesNormal.get(1).compare(klass, "level1", inlineesNormal.get(1).isNormalInline()));
105+
Asserts.assertTrue(inlineesReplay.get(1).compare(klass, "level1", inlineesReplay.get(1).isForcedByReplay()));
106+
Asserts.assertTrue(inlineesNormal.get(2).compare(klass, "level2", inlineesNormal.get(2).isForcedInline()));
107+
Asserts.assertTrue(inlineesReplay.get(2).compare(klass, "level2", inlineesReplay.get(2).isForcedByReplay()));
108+
Asserts.assertTrue(inlineesNormal.get(3).compare(klass, "late", inlineesNormal.get(3).isForcedInline()));
109+
Asserts.assertTrue(inlineesReplay.get(3).compare(klass, "late", isNewFormat ?
110+
inlineesReplay.get(3).isForcedIncrementalInlineByReplay()
111+
: inlineesReplay.get(3).isForcedByReplay()));
112+
Asserts.assertTrue(inlineesNormal.get(4).compare(klass, "level4", inlineesNormal.get(4).isTooDeep()));
113+
Asserts.assertTrue(inlineesReplay.get(4).compare(klass, "level4", inlineesReplay.get(4).isDisallowedByReplay()));
114+
}
115+
116+
private void removeIncrementalInlineInfo() {
117+
try {
118+
Path replayFilePath = Paths.get(getReplayFileName());
119+
List<String> replayContent = Files.readAllLines(replayFilePath);
120+
for (int i = 0; i < replayContent.size(); i++) {
121+
String line = replayContent.get(i);
122+
if (line.startsWith("compile ")) {
123+
int lastIndex = 0;
124+
StringBuilder newLine = new StringBuilder();
125+
Pattern p = Pattern.compile("(\\d (-?\\d)) \\d compiler");
126+
Matcher m = p.matcher(line);
127+
boolean firstMatch = true;
128+
while (m.find()) {
129+
newLine.append(line, lastIndex, m.start())
130+
.append(m.group(1))
131+
.append(" compiler");
132+
lastIndex = m.end();
133+
String bci = m.group(2);
134+
Asserts.assertTrue(firstMatch ? bci.equals("-1") : bci.equals("0"), "only root has -1");
135+
firstMatch = false;
136+
}
137+
Asserts.assertLessThan(lastIndex, line.length(), "not reached end of line, yet");
138+
newLine.append(line, lastIndex, line.length());
139+
replayContent.set(i, newLine.toString());
140+
}
141+
}
142+
Files.write(replayFilePath, replayContent, StandardOpenOption.TRUNCATE_EXISTING);
143+
} catch (IOException ioe) {
144+
throw new Error("Failed to read/write replay data: " + ioe, ioe);
145+
}
146+
}
147+
}
148+
149+
class IncrementalInliningTest {
150+
private static final WhiteBox WB = WhiteBox.getWhiteBox();
151+
private static String s;
152+
153+
public static void main(String[] args) throws NoSuchMethodException {
154+
WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("level0"), true);
155+
WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("level2"), true);
156+
WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("late"), true);
157+
for (int i = 0; i < 10000; i++) {
158+
test();
159+
}
160+
}
161+
162+
private static void test() {
163+
level0();
164+
}
165+
166+
public static void level0() {
167+
level1();
168+
}
169+
170+
public static void level1() {
171+
level2();
172+
}
173+
174+
public static void level2() {
175+
late();
176+
}
177+
178+
// Reached max inline level but forced to be inlined -> inline late.
179+
public static void late() {
180+
level4();
181+
}
182+
183+
// Reached max inline level and not forced to be inlined -> no inline.
184+
public static void level4() {
185+
s = "HelloWorld";
186+
}
187+
188+
}

‎test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java

+18-142
Original file line numberDiff line numberDiff line change
@@ -28,173 +28,49 @@
2828
* @summary Testing that ciReplay inlining does not fail with unresolved signature classes.
2929
* @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.compMode != "Xcomp" & vm.debug == true & vm.compiler2.enabled
3030
* @modules java.base/jdk.internal.misc
31-
* @build sun.hotspot.WhiteBox
32-
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
33-
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
34-
* compiler.ciReplay.TestInliningProtectionDomain
31+
* @run driver compiler.ciReplay.TestInliningProtectionDomain
3532
*/
3633

3734
package compiler.ciReplay;
3835

3936
import jdk.test.lib.Asserts;
4037

41-
import java.io.IOException;
42-
import java.nio.file.Files;
43-
import java.nio.file.Paths;
44-
import java.util.ArrayList;
4538
import java.util.List;
46-
import java.util.regex.Matcher;
47-
import java.util.regex.Pattern;
4839

49-
public class TestInliningProtectionDomain extends DumpReplayBase {
50-
public static final String LOG_FILE_NORMAL = "hotspot_normal.log";
51-
public static final String LOG_FILE_REPLAY = "hotspot_replay.log";
52-
private final String[] commandLineReplay;
53-
54-
private final String className;
40+
public class TestInliningProtectionDomain extends InliningBase {
5541

5642
public static void main(String[] args) {
57-
new TestInliningProtectionDomain("ProtectionDomainTestCompiledBefore", true);
58-
new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPublic", false);
59-
new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPrivate", false);
60-
new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPrivateString", false);
43+
new TestInliningProtectionDomain(ProtectionDomainTestCompiledBefore.class, true);
44+
new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPublic.class, false);
45+
new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivate.class, false);
46+
new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivateString.class, false);
6147
}
6248

63-
public TestInliningProtectionDomain(String className, boolean compileBar) {
64-
this.className = className;
65-
List<String> commandLineNormal = new ArrayList<>(List.of("-XX:LogFile=" + LOG_FILE_NORMAL + "", "-XX:+LogCompilation", "-XX:-TieredCompilation",
66-
"-XX:CompileCommand=exclude," + getTestClass() + "::main",
67-
"-XX:CompileCommand=option," + getTestClass() + "::test,bool,PrintInlining,true"));
49+
public TestInliningProtectionDomain(Class<?> testClass, boolean compileBar) {
50+
super(testClass);
6851
if (compileBar) {
69-
commandLineNormal.add("-XX:CompileCommand=compileonly," + getTestClass() + "::bar");
52+
commandLineNormal.add("-XX:CompileCommand=compileonly," + testClass.getName() + "::bar");
7053
}
71-
commandLineReplay = new String[]
72-
{"-XX:LogFile=" + LOG_FILE_REPLAY + "", "-XX:+LogCompilation",
73-
"-XX:CompileCommand=option," + getTestClass() + "::test,bool,PrintInlining,true"};
74-
runTest(commandLineNormal.toArray(new String[0]));
54+
runTest();
7555
}
7656

7757
@Override
7858
public void testAction() {
7959
positiveTest(commandLineReplay);
80-
String klass = "compiler.ciReplay." + className;
81-
String entryString = klass + " " + "test";
82-
boolean inlineFails = className.equals("ProtectionDomainTestNoOtherCompilationPrivate");
60+
String entryString = getTestClass() + " " + "test";
61+
boolean inlineFails = testClass == ProtectionDomainTestNoOtherCompilationPrivate.class;
8362
int inlineeCount = inlineFails ? 1 : 5;
8463

85-
List<Entry> inlineesNormal = parseLogFile(LOG_FILE_NORMAL, entryString, "compile_id='" + getCompileIdFromFile(getReplayFileName()), inlineeCount);
86-
List<Entry> inlineesReplay = parseLogFile(LOG_FILE_REPLAY, entryString, "test ()V", inlineeCount);
64+
List<InlineEntry> inlineesNormal = parseLogFile(LOG_FILE_NORMAL, entryString, "compile_id='" + getCompileIdFromFile(getReplayFileName()), inlineeCount);
65+
List<InlineEntry> inlineesReplay = parseLogFile(LOG_FILE_REPLAY, entryString, "test ()V", inlineeCount);
8766
verifyLists(inlineesNormal, inlineesReplay, inlineeCount);
8867

8968
if (inlineFails) {
90-
Asserts.assertTrue(compare(inlineesNormal.get(0), "compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate",
91-
"bar", inlineesNormal.get(0).isUnloadedSignatureClasses()));
92-
Asserts.assertTrue(compare(inlineesReplay.get(0), "compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate",
93-
"bar", inlineesReplay.get(0).isDisallowedByReplay()));
69+
Asserts.assertTrue(inlineesNormal.get(0).compare("compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", "bar", inlineesNormal.get(0).isUnloadedSignatureClasses()));
70+
Asserts.assertTrue(inlineesReplay.get(0).compare("compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", "bar", inlineesReplay.get(0).isDisallowedByReplay()));
9471
} else {
95-
Asserts.assertTrue(compare(inlineesNormal.get(4), "compiler.ciReplay.InliningBar", "bar2", inlineesNormal.get(4).isNormalInline()));
96-
Asserts.assertTrue(compare(inlineesReplay.get(4), "compiler.ciReplay.InliningBar", "bar2", inlineesReplay.get(4).isForcedByReplay()));
97-
}
98-
remove(LOG_FILE_NORMAL);
99-
remove(LOG_FILE_REPLAY);
100-
}
101-
102-
private void verifyLists(List<Entry> inlineesNormal, List<Entry> inlineesReplay, int expectedSize) {
103-
if (!inlineesNormal.equals(inlineesReplay)) {
104-
System.err.println("Normal entries:");
105-
inlineesNormal.forEach(System.err::println);
106-
System.err.println("Replay entries:");
107-
inlineesReplay.forEach(System.err::println);
108-
Asserts.fail("different inlining decision in normal run vs. replay run");
109-
}
110-
Asserts.assertEQ(expectedSize, inlineesNormal.size(), "unexpected number of inlinees found");
111-
}
112-
113-
public static boolean compare(Entry e, String klass, String method, boolean kind) {
114-
return e.klass.equals(klass) && e.method.equals(method) && kind;
115-
}
116-
117-
public static List<Entry> parseLogFile(String logFile, String rootMethod, String nmethodMatch, int inlineeCount) {
118-
String nmethodStart = "<nmethod";
119-
List<Entry> inlinees = new ArrayList<>();
120-
int foundLines = 0;
121-
try (var br = Files.newBufferedReader(Paths.get(logFile))) {
122-
String line;
123-
boolean nmethodLine = false;
124-
boolean inlinineLine = false;
125-
while ((line = br.readLine()) != null) {
126-
if (nmethodLine) {
127-
// Ignore other entries which could be in between nmethod entry and inlining statements
128-
if (line.startsWith(" ")) {
129-
inlinineLine = true;
130-
Pattern p = Pattern.compile("(\\S+)::(\\S+).*bytes\\)\s+(.*)");
131-
Matcher matcher = p.matcher(line);
132-
Asserts.assertTrue(matcher.find(), "must find inlinee method");
133-
inlinees.add(new Entry(matcher.group(1), matcher.group(2), matcher.group(3).trim()));
134-
foundLines++;
135-
} else if (inlinineLine) {
136-
Asserts.assertEQ(foundLines, inlineeCount, "did not find all inlinees");
137-
return inlinees;
138-
}
139-
} else {
140-
nmethodLine = line.startsWith(nmethodStart) && line.contains(nmethodMatch);
141-
if (nmethodLine) {
142-
Asserts.assertTrue(line.contains(rootMethod), "should only dump inline information for " + rootMethod);
143-
}
144-
}
145-
}
146-
} catch (IOException e) {
147-
throw new Error("Failed to read " + logFile + " data: " + e, e);
148-
}
149-
Asserts.fail("Should have found inlinees");
150-
return inlinees;
151-
}
152-
153-
154-
@Override
155-
public String getTestClass() {
156-
return "compiler.ciReplay." + className;
157-
}
158-
159-
static class Entry {
160-
String klass;
161-
String method;
162-
String reason;
163-
164-
public Entry(String klass, String method, String reason) {
165-
this.klass = klass;
166-
this.method = method;
167-
this.reason = reason;
168-
}
169-
170-
public boolean isNormalInline() {
171-
return reason.equals("inline (hot)");
172-
}
173-
174-
public boolean isForcedByReplay() {
175-
return reason.equals("force inline by ciReplay");
176-
}
177-
178-
public boolean isDisallowedByReplay() {
179-
return reason.equals("disallowed by ciReplay");
180-
}
181-
182-
public boolean isUnloadedSignatureClasses() {
183-
return reason.equals("unloaded signature classes");
184-
}
185-
186-
@Override
187-
public boolean equals(Object other) {
188-
if (other == this) {
189-
return true;
190-
}
191-
192-
if (!(other instanceof Entry)) {
193-
return false;
194-
}
195-
196-
Entry e = (Entry)other;
197-
return klass.equals(e.klass) && method.equals(e.method);
72+
Asserts.assertTrue(inlineesNormal.get(4).compare("compiler.ciReplay.InliningBar", "bar2", inlineesNormal.get(4).isNormalInline()));
73+
Asserts.assertTrue(inlineesReplay.get(4).compare("compiler.ciReplay.InliningBar", "bar2", inlineesReplay.get(4).isForcedByReplay()));
19874
}
19975
}
20076
}

0 commit comments

Comments
 (0)
Please sign in to comment.