Skip to content

Commit 38802ad

Browse files
committed
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

Lines changed: 28 additions & 8 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 29 additions & 14 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 46 additions & 10 deletions
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
}

0 commit comments

Comments
 (0)